go/analysis : モジュール型静的解析フレームワーク、Goチームによる、小さく再利用可能なブロックで構成

Dev & Code 21 h agoブックマークに追加

go/analysis : モジュール型静的解析フレームワーク、Goチームによる、小さく再利用可能なブロックで構成
イラスト : Momiji Shirogane

`go vet`、`staticcheck` などの裏側:Go チームは、数十行でリンターを書くことができる公式のフレームワークを維持しています。

具体的な事例

200,000行のGoプロジェクトを引き継ぎます。6ヶ月間、コードレビューで見逃されていたゴルーチンリークを引き起こすパターンがあります:defer cancel()なしで作成されたcontext.WithTimeoutです。誰も読んで気づきません。go vetはそれを報告しません。他の言語では、カスタムルールを追加するのに1週間かかります。

Goでは、1晩でAnalyzerを書くことができます。言語チームが長年、ほとんど目立たないが重要なパッケージを維持しているからです:golang.org/x/tools/go/analysisです。

なぜ重要なのか

go/analysis(「Go Analysis Framework」)パッケージは新しいものではありません - pkg.go.dev/golang.org/x/tools/go/analysisで公開され、ドキュメント化されています - ですが、Hacker Newsで定期的に取り上げられます(最後の取り上げは2026-07-26)。まだ多くのGo開発者がこれを知らないからです。しかし、これは言語の静的解析エコシステムのインフラです:go vetはこれに依存しており、staticcheck(Dominik Honnef)はこれを使用し、gopls(公式の言語サーバー)は診断をここに接続し、現代のCIのほとんどのリント(golangci-lintを筆頭に)は単にAnalyzerのコレクションです。

フレームワークのコンセプトはシンプルです:1つの解析器 = 1つのstruct。このstructは名前、ドキュメント、他の解析器への依存関係、および*analysis.Pass(解析されたコード、型チェッカー、ファイル、コメント)を受け取るRun関数を宣言します。そして、pass.Reportを通じて診断を報告します。それだけです。

背後にある仕組み

3つのアイデアがアーキテクチャを支えています:

  1. 組み合わせによるモジュラリティ。 解析器はRequires: []*analysis.Analyzer{inspect.Analyzer}のように宣言できます:フレームワークは依存関係グラフを解決し、前提となる解析器の結果をPass.ResultOfに注入します。結果として、ビジネス解析器はファイルを再解析せず、inspect(これはタイプ付きASTを1回だけウォークする)の結果を消費します。

  2. 1つの「ドライバー」、複数のブートストラッピング。Analyzerを書きます。singlechecker.Main(myAnalyzer)はそれを独立したCLIバイナリにし、multichecker.Main(a1, a2, a3)はそれをマルチチェックバイナリにし、unitchecker.Main(...)はそれをgo vet -vettool=...に接続します。グルーコードはゼロ:同じAnalyzerコードはローカル、CI、IDE(gopls経由)、golangci-lintで実行されます。

  3. タイプ付きの自動修正。 診断にはSuggestedFixを付けることができます:token.Posの位置に基づいたテキスト置換です。goplsはエディタで1クリックでこれを適用します。例えば、staticcheckが「errors.Is(err, sql.ErrNoRows)をよりイディオム的なチェックに置き換える」と提案できるのはこのためです。

規模感を示すために、singlechecker - *Analyzerを完全なバイナリに変換する関数 - は1つのファイルで構成されています。公開可能なGoリンターの最小限の骨格は約40行です。ドキュメントとテストを含めて。ESLintプラグインをゼロから書くのと比べると、スケールが全く違います。

実際にどう変わるのか

実際のプロジェクトで見られる3つの用途:

  • 企業内のルール。net/httpを直接インポートすることは禁止です。トレース用のラッパーを通してください。」 30行、*ast.ImportSpecを検査するAnalyzer、フィクスターファイルに対するanalysistest.Runのテスト、そしてルールはCIに接続されます。

  • 大規模なコード移行。 大規模なリファクタリング(500ファイルで非推奨APIの名前変更):SuggestedFixを持つAnalyzerは操作をgo vet -vettool=./monfix -fix ./...に変えます。人間はループに入っていません。

  • カスタムデータフロー分析。 フレームワークはコンパニオンパッケージを提供します - ctrlflow(制御フローグラフ)、buildssa(GoコードのSSA表現) - これに非自明なデータフロー分析を接続できます。例えば、sql.Rowsの未閉じのリークを検出するには、SSAから真剣に行う必要があり、生のASTではありません。

知っておくべき制限

2つの盲点を頭に入れておく必要があります:

  • パッケージ内解析。 デフォルトでは、Analyzerは1つのパッケージずつしか見ません。パッケージ間解析(全プログラム)には追加の作業が必要です - 「事実」(analysis.Fact)を使用すると可能ですが、これは別のパッケージがそのパッケージをインポートしたときに、解析器がパッケージのシンボルについての事実をエクスポートできるようにするもので、より複雑です。

  • 動的プラグインのエコシステムはありません。go vetAnalyzerをランタイム設定でプラグインすることはできません:バイナリにコンパイルします。これは選択(Goは動的プラグインが好きではない)であり、忘れではない。

覚えておくべきこと

  • go/analysisは、カスタムリントから自動コード移行まで、Goの静的解析に関する公式APIです。
  • 解析器はRun(*Pass)を持つstructです。残り(CLI、go vet統合、gopls統合、CI)は提供されます。
  • コード品質ルールをシェルやsedで書いている場合、その夜はよく投資されています。
リソース

本記事は人工知能により作成され、人間の編集管理のもとで校閲されています。

編集部について
この記事は役に立ちましたか?

3 人がこの記事を評価しました

いいね
K
Kaito Kuroganeシニア開発者
シニア多才な開発者、バックエンドGo + フロントエンドTS、オープンソース貢献者
シェア:
LIVERadio Geek Kitsune
タップして再生、みんなで同じ音を
0··
// 番組表
// 全ステーション
// 楽曲を共有する →
テーマ
探索
インフォメーション