コンテンツへスキップ

ASTインデックス検索

ASTインデックス検索は、AST検索にキャッシュを追加したモードです。最初の呼び出しではノード単位の情報(呼び出し名、インスタンス化、クラス宣言、メソッド宣言)をディスク上のストアに抽出します。以降の呼び出しではこのストアを使い、構造パターンに一致する可能性が明らかにないファイルを除外します。その後、一致候補をソースに照らして再検証します。

ファクトインデックスは、greph-index ast-index CLIサブコマンドとGreph::buildAstIndex() / Greph::searchAstIndexed()ファサードメソッドから利用できます。事前作成済みインデックスを使った検索や、マニフェストを使ったセット検索にも対応しています。

インデックスの作成

シェル
# Full build at the current directory
./vendor/bin/greph-index ast-index build .

# Full build at an explicit root
./vendor/bin/greph-index ast-index build path/to/repo

# Use a non-default index directory
./vendor/bin/greph-index ast-index build . --index-dir /tmp/greph-ast-index

# Build a mutable overlay that can refresh cheaply on demand
./vendor/bin/greph-index ast-index build . \
  --lifecycle opportunistic-refresh \
  --auto-refresh-max-files 32 \
  --auto-refresh-max-bytes 1048576

デフォルトでは、ファクトインデックスは<root>/.greph-ast-index/に保存されます。

作成に成功すると、概要が1行で表示されます:

Text
Built AST index for 2547 files in .greph-ast-index (84931 fact rows, +2547 ~0 -0 =0)

4つのカウンターには、前回の作成以降に追加、更新、削除されたファイル数と、変更されていないファイル数が表示されます。

インデックスの更新

シェル
./vendor/bin/greph-index ast-index refresh .

refreshはインデックス対象のルートを再走査し、変更されたファイルを再解析して、変更があったファクト行のみを書き換えます。出力にはテキストインデックスと同じ4つのカウンターが使われます。

ライフサイクルプロファイルの動作は、事前作成済みテキストインデックスと同じです:

  • static: 鮮度の確認も自動変更も行わない
  • manual-refresh: 古い状態をstatsで報告するが、検索中は更新しない
  • opportunistic-refresh: 変更対象が十分少ない場合に限り、検索中に更新する
  • strict-stale-check: 更新せず、古いインデックスでの検索を拒否する

インデックスの検索

シェル
./vendor/bin/greph-index ast-index search 'new $CLASS()' src
./vendor/bin/greph-index ast-index search '$obj->$method($$$ARGS)' src
./vendor/bin/greph-index ast-index search --json 'array($$$ITEMS)' src
./vendor/bin/greph-index ast-index search -l 'array($$$ITEMS)' src
./vendor/bin/greph-index ast-index search --trace-plan 'new $CLASS()' src

メタ変数の構文は、標準のAST検索と同じです。詳しい文法はモード / AST検索を参照してください。

オプション

フラグ 意味
--lang NAME ASTの言語。デフォルト: php
-j N、--jobs N 並列ワーカー数
-l、--files-with-matches 一致したファイルのみ一覧表示
--glob GLOB パスがGLOBに一致するファイルのみを含める
--type NAME / --type-not NAME ファイル形式フィルター
--json JSON形式で出力
--no-ignore .gitignoreと.grephignoreを無視
--hidden 隠しファイルを含める
--strict-parse 解析エラーをスキップせず、エラーとして終了
--fallback MODE インデックスがない場合の動作: デフォルトのfail、またはscan
--index-dir DIR デフォルト以外のインデックスディレクトリを使用
--trace-plan 事前作成済みASTのプランナー診断をstderrに出力
--show-index-origin 複数インデックスまたはセットモードで、一致したインデックスのラベルを出力の先頭に付ける

インデックスがない場合のフォールバック

デフォルトでは、指定場所にインデックスがない場合、ast-index searchはエラーになります。--fallback scanを渡すと、代わりに標準のASTスキャンを実行します。インデックスがあれば利用し、まだ作成されていない場合も検索結果を返したいツールに適した設定です。

シェル
./vendor/bin/greph-index ast-index search --fallback scan 'new $CLASS()' src

--index-dirを繰り返し指定すると、1つのコマンドで複数の事前作成済みASTインデックスを検索できます。WordPressのコアにプラグインやテーマを加えた構成などで便利です。

シェル
./vendor/bin/greph-index ast-index search \
  --index-dir ../wordpress/.greph-ast-index \
  --index-dir ./.greph-ast-index \
  --show-index-origin \
  'new $CLASS()' .

--show-index-originを有効にすると、出力の先頭に一致した事前作成済みインデックスのエントリー名が表示され、どのルートで見つかったかがわかります。

マニフェストを使ったインデックスセット

安定した事前作成済みインデックスの構成では、マニフェストを定義し、グループ化されたインデックスをgreph-index setに処理させます:

シェル
./vendor/bin/greph-index set build
./vendor/bin/greph-index set stats --mode ast-index --dry-refresh
./vendor/bin/greph-index set search --mode ast-index --show-index-origin 'new $CLASS()' .

セット用サブコマンドは、直接ast-indexを使う場合と同じライフサイクル処理、古さの確認、プランナー診断に対応しています。

プランナーの診断

--trace-planは、完全なマッチャーを実行する前にGrephがクエリをどう絞り込んだかを表示します。ast-indexでは、通常次の情報が含まれます:

  • パターンから抽出された大まかなファクトシグネチャ
  • 検索した事前作成済みインデックスの数
  • ファクトフィルターで除外されたファイル数
  • 正確な構造検証のために再解析した候補ファイル数

インデックスに保存される情報

インデックス対象の各ファイルについて、ASTファクト抽出器は「重要な」ノードごとに1行を記録します。ファクトの種類は、ASTパターンでよく対象になる構文を網羅しています:

  • 関数とメソッドの呼び出し(呼び出し名別)
  • クラスのインスタンス化(クラス名別)
  • クラス、インターフェース、トレイト、enumの宣言(名前別)
  • 関数宣言(名前別)
  • メソッド宣言(名前と可視性別)
  • use文

クエリを受け取ると、Greph\Ast\AstFactQueryは検索パターンを大まかなファクトシグネチャ(例:「任意のクラスのnewを含む」)に変換し、ファクトストアと照合します。残ったファイルを再解析し、完全なパターンと照合します。

つまり、ファクトインデックスは完全な一致情報を保存するものではなく、候補を絞り込むための事前フィルターです。一致処理ではソースを引き続き解析するため、構造の正確さは標準のAST検索と同じで、候補の数だけが減ります。

使用する場面

次のような場合は、ASTインデックスモードが適しています:

  • 同じコードベースに対して同じ構造クエリを繰り返し実行する
  • パターンが、ファクト抽出器の保存する識別可能な形(呼び出し名、クラス名、宣言の種類)を対象にしている
  • すべてのファイルを再解析するより、待ち時間を短縮したい

パターンを区別する特徴がファクトセットに含まれない場合(例: 識別可能な先頭部分を持たない複雑な入れ子式)は、ASTキャッシュモードが一般に適しています。大まかなシグネチャに頼らず、解析の手順そのものを省略できます。

プログラムからの利用

PHP
use Greph\Greph;
use Greph\Ast\AstSearchOptions;
use Greph\Index\IndexLifecycleProfile;

Greph::buildAstIndex('.', lifecycle: IndexLifecycleProfile::OpportunisticRefresh);

$matches = Greph::searchAstIndexed(
    'new $CLASS()',
    'src',
    new AstSearchOptions(jobs: 4, tracePlan: true),
);

searchAstIndexedは標準のAST検索と同じAstSearchOptionsを受け取り、同じ形式のlist<AstMatch>を返します。複数インデックスやマニフェストを使う場合は、searchAstIndexedMany(...)とsearchAstIndexedSet(...)を使用してください。