Slide 1

Slide 1 text

The Go gopher was designed by Renée French. Goで静的解析をはじめてみよう! 2021/11/13(Sat) Go Conference 2021 Autumn Online Tsuji Daishiro

Slide 2

Slide 2 text

Who are you? ● 辻 大志郎(つじ だいしろう) ○ @d_tutuz ● 所属 Future Architect(2014/10~)

Slide 3

Slide 3 text

本セッションのゴール ● みなさんも go vet で使われているような静的解析モジュー ルを作れる!

Slide 4

Slide 4 text

静的解析はしきいが高い? ● ソースコードの解析が必要? ● 解析結果からロジックを実装する必要がある? ● Goが提供している準標準ライブラリを使うことができる ○ golang.org/x/tools/go/analysis ○ analysis パッケージは go vet でも使われている ● analysisパッケージを使おう! ● ロジックの実装に集中できる

Slide 5

Slide 5 text

analysis パッケージの中心的な構造体 ● analysis.Pass ○ 静的解析に使う情報を扱う構造体 ○ analysis.Pass を利用することでパッケージの情報や型、抽象構文木のトラ バース結果が簡単に手に入る! type Pass struct { // ... Pkg *types.Package // パッケージに関する情報 TypesInfo *types.Info // 抽象構文木の型に関する情報 // ... ResultOf map[*Analyzer]interface{} // 解析器をキーにした解析結果を保持 // ... }

Slide 6

Slide 6 text

unmarshal モジュールで静的解析する流れ ● Goのお手本の実装例を見るのがおすすめ ○ unmarshal モジュール ○ https://pkg.go.dev/golang.org/x/[email protected]/go/analysis/passes/unm arshal ● 例:JSON のデコード ○ json.Unmarshal や (*"encoding/json".Decoder).Decodeでは引数はポイ ンタ型である必要がある ○ 引数がポインタ型でない場合は実行時エラー ○ 静的解析(unmarshal モジュール)で引数の型をチェックできる

Slide 7

Slide 7 text

unmarshal モジュールの実装 1. PreOrder(深さ優先探索)で抽象構文木の解析結果を取得 2. ノードがレシーバを保持しているかチェック 3. ノードの名前が Unmarshal か Decode であるかチェック 4. 関数呼び出しノードの引数の型情報を取得。ポインタ型やイン ターフェース型の場合はOK

Slide 8

Slide 8 text

unmarshal モジュールの実装 (コア実装は70行程度) 1. PreOrder(深さ優先探索)で抽象構文木の解析結果を取得 → Pass.ResultOf の利用 2. ノードがレシーバを保持しているかチェック → Pass.ResultOf と Pass.TypesInfo の利用 3. ノードの名前が Unmarshal か Decode であるかチェック → Pass.ResultOf と Pass.TypesInfo の利用 4. 型情報から関数呼び出しノードの引数の型情報を取得。ポイ ンタ型やインターフェース型の場合はOK → Pass.ResultOf と Pass.TypesInfo の利用 Pass 構造体のフィールドを利用してロジックを実装できる

Slide 9

Slide 9 text

まとめ ● Goが提供している準標準ライブラリ (golang.org/x/tools/go/analysis) を使おう! ○ 解析ロジックに集中できる ● Goが提供している静的解析のモジュールのコードを参考に しよう!