コード変更の影響を可視化する
変更したコードが意図しないところへ影響して問題になったことはありませんか?
変更するコードが影響する範囲を可視化する静的チェックツール Inga を OSS で開発しています。CI で実行することで変更が影響する API エンドポイントや UI Component を自動で表示させることができます。
今回はこのツールがどのようにコードを解析しているか簡単に話します。コード解析はなかなか面白いので興味を持ってもらえれば嬉しいです。
ASTとは?
まず前提知識として、コードはただの文字列なのでコードの参照を探したり、構造を解析する用途には向きません。コード解析を行う前処理としてコードを AST (Abstract Syntax Tree) と呼ばれるツリー構造のデータへ変換する必要があります。
AST はコードをコンパイルする際や、Linter や IDE など様々な箇所で内部的に使用されている便利なデータ構造です。
言語によって違いはありますが主に下記の情報を持ったデータの集まりです:
- タイプ(e.g. class, method)
- 名前
- コード内の位置
- 子ノード
影響範囲を探す方法
変更したコードの影響範囲を探す上で重要な2つの要素として、メソッドやメンバなどの定義とそれを参照するコードがあります。コードと AST を比較しながら解説します。
定義を探す
まず変更されたコードが影響するメソッドを特定します。例えば create
というメソッドのブロック内に変更を加えた場合、AST の各ノードにはファイル内の文字の開始と終了の範囲情報を持っているため、変更を加えた箇所が create
method のノードに含まれることがツリーを探索することで分かります。
ただし create
という名前のメソッドはプロジェクト内にたくさんあるので、これだけでは参照の検索には使えません。AST の木を上から順に降りながらユニークな名前である「app.service.Order.create」を取得します。
処理 | 見つけた名前 | |
---|---|---|
1 | package ノードの名前を取得 | app.service |
2 | class ノードの名前を取得 | app.service.Order |
3 | method ノードの名前を取得 | app.service.Order.create |
参照を探す
少し AST の例が複雑になりましたが、メソッドを呼び出す AST のノードは特殊なタイプになっているので先程探した対象メソッドの名前 create
と一致するノードを探します。さらに order.create()
の order
が何のクラスか不明確なので、降りてきた木を上へ逆に登ることで最終的にユニークな名前の「app.service.Order.create」を取得でき、対象のメソッド呼び出しと関連づけることができます。
処理 | 見つけた名前 | |
---|---|---|
1 |
create メソッドの呼び出しを探す |
create |
2 |
order メンバのクラス定義を探す |
Order.create |
3 |
Order の import を探す |
app.service.Order.create |
まとめ
「定義」と「参照」というユニークな名前で繰り返し検索することで呼び出し元のエントリーポイントまでたどることができます。これにより Java と Kotlin など複数の言語を扱うプロジェクトでも共通のユニークな名前で検索することで言語を超えて解析することもできます。
まだまだ開発途中ですが、興味があれば Star ⭐️ 登録やフィードバックをお願いします!
Discussion