Linterとは、静的にプログラムを解析するツール。プログラムにおける解析ツールの総称。実行はできるがエラーにつながる書き方をチェックしてくれたり、チームでの書き方を揃えるのに使う。
一般的にはプログラム言語が対象だが、自然言語に対するlint(TextLint)も存在する。プログラム言語それぞれでツールが存在する。
- ESLint
- JavaScript, TypeScriptにおけるLint
- Rubocop
- RubyにおけるLint。
静的解析によってできることは多い。
- 特定の書き方を検知する
- コードの数値化
- 自動修正
- 静的解析を助けるメタツール
- より深い理解につながる
メモ。
静的開発ツール開発の流れ。
flowchart LR
開始 -->|Goファイル| 字句解析
字句解析 -->|トークン| 構文解析
構文解析 -->|抽象構文木| 型チェック
構文解析 -->|抽象構文木| 自作部分
型チェック -->|型情報| 自作部分
Goにおける静的解析のフェーズ。
- 構文解析
- 型チェック
静的単一代入形式
- ポインタ解析
言語仕様を読む。
- 抽象構文木を扱うためにGoの構文に詳しくなる
- 言語仕様書
- EBNFを読む
- 構文を深く理解すれば抽象構文木を自由に扱える
- どういうノードで構成されているか
- どういう情報がどういうノードから取得できるか
使い方。
- Preorderはインターフェース型でフィルタできない
- ast.Walkは探索アルゴリズムを切り替えるときに使う
- visitorインターフェースがアルゴリズム部分
- ノードの置換はApply
- スコープを取得できる
静的解析で何ができる、の勘所がないのでいくつか作ってみる。ベースがあれば、何かやっているときにひらめくだろう。14. 静的解析とコード生成 - Google Slidesを参考にしている。
- [X] プライベートだけどタグがついてるフィールドを探す
- [X] 引数をカウント
- [ ] どこからも呼ばれてないパッケージ関数を見つける
- どこからも呼ばれてない識別子を見つける
- int型の式を見つける
- 名前の短いパッケージ変数を探す
- [X] コンテキストを構造体に保持しているのを探す
- サブテストでt.Parallel()を呼び出しているのに親テストで呼び出していないケースを探す
- knifeを使ってコード生成する
- 構造体のフィールドのgetter, setterを作る
- タグがつけられているものだけ
- GOSSAFUNCを指定してSSA形式を確認する
- godump ASTとSSAのダンプを行うCLIツール
- 使われていない引数を探す
- 自由変数の値を変更しているコードを見つける
- 関数が呼び出されているか
- テストで生成する用。いろは歌的にすべてのnodeが登場しているのを確かめる。
- 学ぶ用。すべてのnodeの例を自分で書いて確かめる用
気づいたところを書く。
- interfaceのNodeは、Pos()とEnd()で構成されている。ファイルでの開始位置、終了位置
- Nodeには3種類ある
- Stmt node 文
- Expr node 式
- Decl node 定義
eslintのplugin集。 種類が多い。
unitchecker.Main(trashcomment.Analyzer)
unitchecker.Main(gophersample.Analyzer)
↑だと先に定義した1つ(つまりtrashcomment)しか実行できない。
unitcheckerは引数に複数のAnalyzerを取って実行できる。
unitchecker.Main(trashcomment.Analyzer, gophersample.Analyzer)
無意味なコメントを検知してみる。
引数が超えると警告する。
オリジナルで作ったけど、それらをひとまとめにして簡単に利用できるようにする。1つにインポートしてまとめて、ビルドしてコンテナに入れればいい。
よく使うのでツール化する。
コンテキストを探す。
- 構造体である
- フィールドの型がそれぞれcontext.Contextでないか調べる
- エラーを出す
インターフェースの場合は、型アサーションしないと個別の型にはアクセスできない。そりゃそう。
どうやってやるのだろう。unusedのコードを調べたが、完全に理解してはいない。識別子はあらゆるパターンで出てきて、それぞれを考慮するのが必要になる。大きく分けると定義か呼び出し。さらに基本型ごとにある。
タグはパブリックでないと意味がない。- タグは実行時に参照可能なメタ情報
- タグ情報はリフレクション経由で取得できる
ひと目でわかるようにする。
interfaceを実装している構造体を一覧で見られれば網羅できるが、調べ方を忘れた。
自作ツールの考慮にかなり漏れがあるようだ。
を参考に修正する。
- analysisutilの関数で一発でcontext.Contextを見つけている
- ResultOfの使い方。なぜident.Mapで型アサーションしているか
- グローバルに定義されてる場合、エクスポートされてる場合はスルー
- 変数ではない・フィールドではない・無名関数の場合はスルー
- ポインタも確認
- インポートしたものをループで処理
テストでの未考慮が多いところに問題がある。問題があるとわかれば、調べて解決できるだろう。