Upgrade to Pro — share decks privately, control downloads, hide ads and more …

analysis パッケージの仕組みの上でMulti linter with configを実...

analysis パッケージの仕組みの上でMulti linter with configを実現する / Go Conference 2025

Avatar for Ken’ichiro Oyama

Ken’ichiro Oyama

September 27, 2025
Tweet

More Decks by Ken’ichiro Oyama

Other Decks in Technology

Transcript

  1. 少し実⽤的で⼩さなOSSを書くのが趣味 • GitHub: @k1LoW • X: @k1LoW • SWE at

    Tailor Inc. / Fukuoka.go ⼩⼭健⼀郎 ⾃⼰紹介 2 Ken'ichiro Oyama https://findy-code.io/media/articles/codesidechat-k1LoW \ github.com/yuin/goldmarkを応援できます!/ \ 毎⽇「応援」ボタン押してください!/
  2. • セッションタイトルは「analysis パッケージの仕組みの上でMulti linter with configを実現する」 • Multi linter with

    config ◦ 単⼀の設定ファイルを読んでその内容を各Linterの解析ロジックに反映させる機能 ▪ つまり Golangci-lint や RuboCop のような機能 Multi linter with config Motivation 5
  3. • Golangci-lint is a fast linters runner for Go ◦

    It runs linters in parallel, uses caching, supports YAML configuration, integrates with all major IDEs, and includes over a hundred linters. ◦ https://golangci-lint.run/ • ビルトインされた複数のLinterを同時に実⾏し、結果をまとめて出⼒してくれる • 設定は1つのファイル( .golanci.yml etc. )に統合されており、それぞれのLinterの調整が可能。 Golangci-lint Motivation 6
  4. • コーディングスタイルに特化した統合型Linter。 ◦ https://github.com/k1LoW/gostyle ▪ gostyle IS NOT Go Style

    ( https://google.github.io/styleguide/go/ ) ▪ いくつか存在するコーディングスタイルの記述からリントルール(analysis.Analyzer)を 作成(現在17ルールある) • Effective Go (https://go.dev/doc/effective_go) • Go Style (https://google.github.io/styleguide/go/) • Go Code Review Comments (https://go.dev/wiki/CodeReviewComments) ◦ エラーにリントルールの根拠となるコーディングスタイルのURLを含めることでコンセンサスを取り やすくしている gostyle Motivation 7
  5. • go vet の vet toolとして使⽤可能 • 設定ファイルはGitルートに .gostyle.yml もしくは

    .gostyle.yaml というファイル名で設置するか、 -gostyle.config に絶対パスでパスを渡す How to use gostyle ( As a vet tool ) Motivation 9
  6. go vet -vettool=`which mylint` [packages] で実⾏できる mylint を作成する 1. &analysis.Analyzer{}

    を必要な分作成する a. https://pkg.go.dev/golang.org/x/tools/go/analysis#Analyzer 2. unitchecker.Main() に作成した各&analysis.Analyzer{}を渡す go vet のカスタム vet tool の作成と使⽤⽅法 Multi linter with config using analysis package 12
  7. • Name string ... Analyzerの名前。go vet 経由のフラグ名にも使われる • Doc string

    ... Analyzerの説明 • URL string ... URL • Flags flag.FlagSet ... Analyzerが持つフラグ • Run func(*Pass) (any, error) ... Analyzerの解析処理のメイン部 • RunDespiteErrors bool ... 対象コードがパースエラーになっていても実⾏するかどうかのフラグ • Requires []*Analyzer ... このAnalyzerの前処理として実⾏が必要なAnalyzerのリスト • ResultType reflect.Type ... Run の返り値の型。他のAnalyzerにResultとして渡せる • FactTypes []Fact ... 使⽤するFactの型。他のAnalyzerにFactとして渡せる analysis.Analyzer の各フィールド概要 Multi linter with config using analysis package 14
  8. • 様々な「前処理」Analyzerを指定することで Run func(*Pass) (any, error) での解析処理を効率よ く実施できる ◦ golang.org/x/tools/go/analysis/passes/inspect.Analyzer

    ▪ 対象のASTを深さ優先探索で⾛査できる inspector.Inspector.Preorder など、複数の便 利な関数を提供する ◦ github.com/gostaticanalysis/comment/passes/commentmap.Analyzer ▪ コード内のコメントを活⽤しやすくする ◦ その他 golang.org/x/tools/go/analysis/passes/buildssa.Analyzer や golang.org/x/tools/go/analysis/passes/ctrlflow.Analyzer など Requires []*Analyzer
 Multi linter with config using analysis package 15
  9. • Name は gostyle に設定 • Flags 経由で config フラグを⽤意し、設定ファイルのパスを取得

    ◦ go vet 経由だとフラグは Name の値と合わせて -gostyle.config になる • Run 内で設定ファイルをパースして &Config{} に値を設定する。 ◦ 指定がない場合はGitルートで指定のファイル名( .gostyle.yml or .gostyle.yaml )を読む ◦ &Config{} には gostyle の全てのAnalyzerの設定が含まれている ▪ 必要に応じてデフォルト値を設定する • ResultType として設定 *Config を指定した上で、Run の返り値として返す config.Loader Multi linter with config using analysis package 17 前処理Analyzerとして設定ファイルを読む
  10. • &analysis.Analizer{} の Requires フィールドを活⽤ • 全てのLinter⽤Analyzerの前処理(Requires)として設定ファイル読み込み⽤Analyzer config.Loader を⽤意 •

    ResultType: reflect.TypeOf((*Config)(nil)) として、全てのLinter⽤Analyzerに設定を伝播さ せる How to implement "Multi linter with config" Multi linter with config using analysis package 19
  11. • 先ほどまでの gostyle の実⾏には go vet が必要。 ◦ 単独では実⾏できない。 •

    golangci-lint run のように gostyle も gostyle run で静的解析ができて欲しい • (かつ、 go vet 経由での実⾏機能も維持したい) gostyle run を実現する Run as a standalone CLI 23
  12. • (再掲)go vet 経由での実⾏機能も維持したい • main関数で分岐することで実現 ◦ gostyle v0.25.1 •

    cmd.Execute() でサブコマンドが run のとき に multichecker.Main を実⾏ Run as a standalone CLI 25 os.Args の値で分岐する "Run as a standalone CLI" and "Run as a vet tool"
  13. • Run は引数に pass *analysis.Pass を取る Run func(pass *Pass) (any,

    error)
 • pass.Reportf や pass.ReportRangef で解析結果をレポートできる ◦ 引数には ポジション(token.Pos) が必要 analysis.Analyzer での解析結果のレポート Inline ignoring 28 Run func(*Pass) (any, error)

  14. • コメント(type comment.Maps []ast.CommentMap)を収集してくれるAnalyzerを持つ ◦ github.com/gostaticanalysis/comment/passes/commentmap ◦ Requiresに登録しておくだけで収集したコメントを取得可能 ◦ コメント情報には

    token.Pos が保持されている • commentmapを使うことで、 レポート時の token.Pos と コメント情報の token.Pos を使ってレポート ⾏に関係するコメントを抽出できる レポート対象の⾏のコメントを特定する Inline ignoring 29 github.com/gostaticanalysis/comment を使う
  15. • github.com/gostaticanalysis/comment にはコメント操作ための関数がいくつか提供されている • IgnorePos はそのうちの1つ • //lint:ignore Check1[,Check2,...,CheckN] reason

    というstaticcheck styleのコメントを判定 可能 • https://pkg.go.dev/github.com/gostaticanalysis/comment#Maps.IgnorePos By github.com/gostaticanalysis/comment Inline ignoring 30 func (maps Maps) IgnorePos(pos token.Pos, check string) bool

  16. • レポート⽤のstruct reporter.Reporter を⽤意 • 内部で //nostyle:[analyzer name] のコメント判定をしてから pass.Reportf

    を呼ぶ ◦ Append -> Report
 gostyleでのInline ignoringの実装 Inline ignoring 31 github.com/k1LoW/gostyle/reporter.Reporter
  17. • analysis パッケージの仕組みの上での統合型Linterを作り⽅を紹介 ◦ Multi linter with config ◦ Run

    as a standalone CLI ◦ Inline ignoring • analysis パッケージで Golangci-lint や RuboCop のような機能が実現できる • まずはカスタムvet toolから。そして統合型のLinterへ まとめ 33