ast-grep について
ast-grep でコードの構造検索や書き換えを行う開発者向けのスキルです。自然言語の依頼を AST ルールに変換し、ルールのテストやデバッグ、コードベースの検索を案内します。
主な機能
- 01検索依頼を AST ルールに変換
- 02パターンと YAML ルールの作成
- 03ルールのテストとデバッグ
- 04構造に基づくコード検索
- 05ルールによるコード書き換え
活用シーン
- 01特定の構造を持つ関数の検索
- 02特定の引数を持つ呼び出しの特定
- 03エラー処理のない非同期関数の検索
- 04ルールに沿ったコード変換
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ ampcode/ amp-contrib --skill ast-grepインストール後、Claude Code で/ast-grepを入力して読み込まれたか確認します。
同梱ファイルreferences/rule_reference.md
詳細
ast-grep によるコード検索
概要
このスキルは、自然言語のクエリを ast-grep のルールに変換し、構造的なコード検索を行うのに役立ちます。ast-grep は、単なるテキストではなくコードの構造に基づいて一致する AST(抽象構文木)パターンを使用するため、大規模なコードベース全体を強力かつ正確に検索できます。
このスキルを使用する場面
次のような場合に、このスキルを使用してください。
- 構造的なマッチングを使ってコードパターンを検索する必要がある場合(例:「エラーハンドリングのない非同期関数をすべて検索する」)
- 特定の言語構造を見つけたい場合(例:「特定のパラメーターを持つ関数呼び出しをすべて検索する」)
- 単なるテキスト検索ではなく、コード構造の理解が必要な検索を依頼された場合
- 特定の AST 特性を持つコードの検索を依頼された場合
- 従来のテキスト検索では対応できない複雑なコードクエリを実行する必要がある場合
一般的なワークフロー
効果的な ast-grep ルールをユーザーが作成できるよう、次の手順に従ってください。
ステップ 1:クエリを理解する
ユーザーが何を見つけたいのかを明確に理解してください。必要に応じて、確認の質問をします。
- どのようなコードパターンや構造を探していますか?
- どのプログラミング言語ですか?
- 考慮すべき特別なエッジケースやバリエーションはありますか?
- マッチに含めるもの、または除外するものは何ですか?
ステップ 2:サンプルコードを作成する
ユーザーが検索したいものを表す簡単なコードスニペットを書きます。テスト用に一時ファイルへ保存してください。
例: 「await を使用する非同期関数」を検索する場合は、テストファイルを作成します。
// test_example.js
async function example() {
const result = await fetchData()
return result
}
ステップ 3:ast-grep ルールを作成する
パターンを ast-grep ルールに変換します。まずはシンプルなルールから始め、必要に応じて複雑にしていきます。
主な原則:
- リレーショナルルール(
inside、has)では、検索範囲を方向の末端まで広げるため、必ずstopBy: endを使用します - シンプルな構造には
patternを使用します - 複雑な構造には
has/insideとkindを使用します - 複雑なクエリは、
all、any、またはnotを使って小さなサブルールに分割します
ルールファイルの例(test_rule.yml):
id: async-with-await
language: javascript
rule:
kind: function_declaration
has:
pattern: await $EXPR
stopBy: end
ルールの詳細については、references/rule_reference.md を参照してください。
ステップ 4:ルールをテストする
ast-grep CLI を使用して、ルールがサンプルコードにマッチすることを確認します。主な方法は 2 つあります。
オプション A:インラインルールでテストする(すばやく繰り返す場合)
echo "async function test() { await fetch(); }" | ast-grep scan --inline-rules "id: test
language: javascript
rule:
kind: function_declaration
has:
pattern: await \$EXPR
stopBy: end" --stdin
オプション B:ルールファイルでテストする(複雑なルールに推奨)
ast-grep scan --rule test_rule.yml test_example.js
マッチしない場合のデバッグ:
- ルールを簡略化します(サブルールを削除します)
- リレーショナルルールに
stopBy: endがない場合は追加します --debug-queryを使って AST 構造を確認します(下記参照)kindの値がその言語で正しいか確認します
ステップ 5:コードベースを検索する
ルールがサンプルコードに正しくマッチしたら、実際のコードベースを検索します。
シンプルなパターン検索の場合:
ast-grep run --pattern 'console.log($ARG)' --lang javascript /path/to/project
複雑なルールベースの検索の場合:
ast-grep scan --rule my_rule.yml /path/to/project
インラインルールの場合(ファイルを作成しない場合):
ast-grep scan --inline-rules "id: my-rule
language: javascript
rule:
pattern: \$PATTERN" /path/to/project
ast-grep でコードを書き換える
ast-grep は、コードパターンを検索し、新しいコードに変換できる強力な AST ベースのツールです。単なるテキストではなくコード構造を理解する、構文を認識する sed/grep のように動作します。
方法 1:--rewrite を使ったコマンドライン
最も簡単な方法は、ターミナルで --rewrite(または -r)フラグを直接使用することです。
ast-grep run --pattern 'foo' --rewrite 'bar' --lang python
これにより、foo のすべての出現箇所が見つかり、bar に置き換えられます。実用的な例を示します。
# Convert old-style property checks to optional chaining
ast-grep -p '$PROP && $PROP()' --rewrite '$PROP?.()' --interactive -l ts ./src
主なフラグ:
--interactiveまたは-i:変更を適用する前に、1 件ずつ確認します--update-allまたは-U:確認なしですべての変更を適用します
方法 2:fix を使った YAML ルール
より複雑な変換には、fix フィールドを含む YAML ルールファイルを使用します。
id: change_def
language: Python
rule:
pattern: |
def foo($X):
$$$S
fix: |-
def baz($X):
$$$S
次のコマンドで実行します:ast-grep scan -r rule.yml ./src
メタ変数
メタ変数は、強力な書き換えを実現する鍵です。正規表現のキャプチャグループのように機能します。
| メタ変数 | マッチするもの |
|---|---|
$NAME | 任意の単一の AST ノード(式、識別子など) |
$$$ITEMS | 複数のノード(関数の引数など) |
例 — 代入の左右を入れ替える:
rule:
pattern: $X = $Y
fix: $Y = $X
a = b を b = a に変換します。
インデントへの対応
ast-grep は書き換え時にインデントを保持します。修正テンプレートにインデントがある場合、元のコード位置に対する相対的なインデントが維持されます。
rule:
pattern: '$B = lambda: $R'
fix: |-
def $B():
return $R
FixConfig でマッチ範囲を広げる
場合によっては、カンマなど周囲の文字を削除する必要があります。expandStart と expandEnd を指定した FixConfig を使います。
rule:
kind: pair
has:
field: key
regex: Remove
fix:
template: ''
expandEnd: { regex: ',' } # Also deletes trailing comma
これにより、マッチしたノードに加えて、後続のカンマも削除されます。
高度な機能:リライター
複数のノードにまたがる複雑な変換には、マッチしたノードのリストを処理する rewriters を使います。
id: barrel-to-single
language: JavaScript
rule:
pattern: import {$$$IDENTS} from './module'
rewriters:
- id: rewrite-identifier
rule:
pattern: $IDENT
kind: identifier
transform:
LIB: { convert: { source: $IDENT, toCase: lowerCase } }
fix: import $IDENT from './module/$LIB'
transform:
IMPORTS:
rewrite:
rewriters: [rewrite-identifier]
source: $$$IDENTS
joinBy: "\n"
fix: $IMPORTS
これにより、import { A, B } from './module' のようなバレルインポートが個別のインポートに変換されます。
ワークフローの概要
- 検索:パターンを使って AST ノードにマッチさせます
- キャプチャ:メタ変数(
$VAR、$$$ARGS)でマッチした内容をキャプチャします - 変換:必要に応じて、キャプチャした内容を処理します(大文字・小文字の変換、正規表現による置換など)
- 適用:マッチしたノードを
fixテンプレートで置き換えます
ヒント
- コマンドラインでは、
$のシェル展開を防ぐためにシングルクォートを使います - マッチしなかったメタ変数は、修正時に空文字列になります
ast-grep CLI コマンド
コード構造を調べる(--debug-query)
AST の構造を出力して、コードがどのように解析されるかを確認します。
ast-grep run --pattern 'async function example() { await fetch(); }' \
--lang javascript \
--debug-query=cst
利用可能な形式:
cst:具象構文木(句読点を含むすべてのノードを表示します)ast:抽象構文木(名前付きノードのみを表示します)pattern:ast-grep がパターンをどのように解釈するかを表示します
次の用途に使います:
- ノードに対応する正しい
kindの値を見つける - マッチさせたいコードの構造を理解する
- パターンがマッチしない原因を調べる
例:
# See the structure of your target code
ast-grep run --pattern 'class User { constructor() {} }' \
--lang javascript \
--debug-query=cst
# See how ast-grep interprets your pattern
ast-grep run --pattern 'class $NAME { $$$BODY }' \
--lang javascript \
--debug-query=pattern
ルールをテストする(--stdin を指定して scan)
ファイルを作成せずに、コードスニペットに対してルールをテストします。
echo "const x = await fetch();" | ast-grep scan --inline-rules "id: test
language: javascript
rule:
pattern: await \$EXPR" --stdin
構造化された出力にするには --json を追加します:
echo "const x = await fetch();" | ast-grep scan --inline-rules "..." --stdin --json
パターンで検索する(run)
単一の AST ノードにマッチさせる、シンプルなパターンベースの検索です。
# Basic pattern search
ast-grep run --pattern 'console.log($ARG)' --lang javascript .
# Search specific files
ast-grep run --pattern 'class $NAME' --lang python /path/to/project
# JSON output for programmatic use
ast-grep run --pattern 'function $NAME($$$)' --lang javascript --json .
次の場合に使います:
- シンプルな単一ノードのマッチ
- 複雑なロジックを使わない手早い検索
- 関係ルール(inside/has)が不要な場合
ルールで検索する(scan)
複雑な構造クエリに対応する、YAML ルールベースの検索です。
# With rule file
ast-grep scan --rule my_rule.yml /path/to/project
# With inline rules
ast-grep scan --inline-rules "id: find-async
language: javascript
rule:
kind: function_declaration
has:
pattern: await \$EXPR
stopBy: end" /path/to/project
# JSON output
ast-grep scan --rule my_rule.yml --json /path/to/project
次の場合に使います:
- 複雑な構造の検索
- 関係ルール(inside、has、precedes、follows)
- 複合ロジック(all、any、not)
- YAML ルールの全機能が必要な場合
ヒント: 関係ルール(inside/has)では、完全に走査できるように、必ず stopBy: end を追加してください。
効果的なルールを書くためのヒント
常に stopBy: end を使う
特別な理由がない限り、関係ルールでは常に stopBy: end を使います。
has:
pattern: await $EXPR
stopBy: end
これにより、最初にマッチしないノードで停止せず、検索がサブツリー全体を走査します。
シンプルに始めてから、複雑にする
まず、機能する可能性のある最もシンプルなルールから始めます。
- 最初に
patternを試します - うまくいかない場合は、
kindを試してノードの種類にマッチさせます - 必要に応じて関係ルール(
has、inside)を追加します - 複雑なロジックには、複合ルール(
all、any、not)を組み合わせます
適切なルールの種類を使う
- パターン:単純で直接的なコードのマッチに使います(例:
console.log($ARG)) - Kind + 関係ルール:複雑な構造に使います(例:「await を含む関数」)
- 複合ルール:論理的な組み合わせに使います(例:「try-catch の外にある await を含む関数」)
AST の確認でデバッグする
ルールがマッチしない場合:
--debug-query=cstを使って、実際の AST 構造を確認します- メタ変数が正しく検出されているか確認します
- ノードの
kindが想定どおりか確認します - 関係ルールが正しい方向を検索しているか確認します
インラインルールでのエスケープ
--inline-rules を使用する場合は、シェルコマンド内のメタ変数をエスケープしてください。
$はシェルによって変数として解釈されるため、$VARの代わりに\$VARを使用してください- または、シングルクォートを使用してください。ほとんどのシェルでは
'$VAR'が機能します
例:
# Correct: escaped $
ast-grep scan --inline-rules "rule: {pattern: 'console.log(\$ARG)'}" .
# Or use single quotes
ast-grep scan --inline-rules 'rule: {pattern: "console.log($ARG)"}' .
一般的なユースケース
特定の内容を含む関数を検索する
await を使用する非同期関数を検索します:
ast-grep scan --inline-rules "id: async-await
language: javascript
rule:
all:
- kind: function_declaration
- has:
pattern: await \$EXPR
stopBy: end" /path/to/project
特定のコンテキスト内のコードを検索する
クラスメソッド内の console.log を検索します:
ast-grep scan --inline-rules "id: console-in-class
language: javascript
rule:
pattern: console.log(\$\$\$)
inside:
kind: method_definition
stopBy: end" /path/to/project
期待されるパターンがないコードを検索する
try-catch のない非同期関数を検索します:
ast-grep scan --inline-rules "id: async-no-trycatch
language: javascript
rule:
all:
- kind: function_declaration
- has:
pattern: await \$EXPR
stopBy: end
- not:
has:
pattern: try { \$\$\$ } catch (\$E) { \$\$\$ }
stopBy: end" /path/to/project
リソース
references/
ast-grep のルール構文に関する詳細なドキュメントが含まれています:
rule_reference.md:アトミックルール、関係ルール、複合ルール、メタ変数を網羅した ast-grep ルールの包括的なドキュメント
詳細なルール構文の情報が必要な場合は、これらのリファレンスを読み込んでください。