メインコンテンツへスキップ

Go Proposal Weekly Digest

Go言語のproposal更新を毎週お届け

#77986likely_accept

x/tools/go/analysis/passes/modernize: vars for atomic, embedlit, errorsastype, plusbuild, stringscut, stditerators

ステータス変更: active likely_accept

要約

AIによる要約であり、誤りを含む場合があります。

概要

golang.org/x/tools/go/analysis/passes/modernize パッケージにすでに実装済みで内部的に使用されている6つのアナライザー(atomicembedliterrorsastypeplusbuildstringscutstditerators)を、公開された(エクスポートされた)変数としてAPIに追加するproposalです。機能の追加ではなく、既存機能のAPIとしての公開が目的です。

ステータス変更

activelikely_accept
提案された変数一覧がissueコメントで明確に整理された(adonovanによるdiff形式の提示)ことを受け、aclementsがProposal Review Meetingにてlikely acceptと判断しました。機能自体はすでにmodernize.Suiteスライス変数を通じて間接的にアクセス可能であり、設計上のリスクが低く、ユーザーの要望も明確であったためです。

技術的背景

現状の問題点

modernizeパッケージにはSuiteという[]*analysis.Analyzer型のスライス変数があり、全アナライザーがまとめて登録されています。しかし個々のアナライザーはパッケージ内部の非公開変数(atomicTypesAnalyzererrorsastypeAnalyzerなど)として定義されており、外部パッケージから個別に参照することができません。
goplsのような内部ツールは特別なハック(内部パッケージへの直接参照)を使うことで回避していましたが、外部の静的解析ツールや独自のvetツールでは同様の方法が使えません。Suiteスライスからインデックスで取り出す方法も技術的には可能ですが、非常に壊れやすい実装です。

提案された解決策

以下の公開変数をmodernizeパッケージに追加します。

package modernize
var (
    AtomicTypesAnalyzer  *analysis.Analyzer
    ErrorsastypeAnalyzer *analysis.Analyzer
    PlusBuildAnalyzer    *analysis.Analyzer
    StringscutAnalyzer   *analysis.Analyzer
    StditeratorsAnalyzer *analysis.Analyzer
    EmbedLitAnalyzer     *analysis.Analyzer
)

各アナライザーの役割は以下のとおりです。

  • AtomicTypesAnalyzer: sync/atomicパッケージの関数呼び出しを、Go 1.19で導入された型安全なアトミックラッパー型(atomic.Int32など)に置き換えを提案
  • ErrorsastypeAnalyzer: Go 1.26で追加されたerrors.AsType[T]ジェネリック関数を用いた、errors.Asの書き換えを提案
  • PlusBuildAnalyzer: Go 1.17以前の//+build形式のビルドタグを、Go 1.17以降の//go:build形式に統一(旧形式を除去)する変換を提案
  • StringscutAnalyzer: strings.Indexを用いた文字列分割パターンを、Go 1.18で追加されたstrings.Cutに置き換えを提案
  • StditeratorsAnalyzer: go/typesreflectパッケージなどのLen()/At()スタイルのループを、Go 1.23で導入されたイテレーター(range x.All())に置き換えを提案
  • EmbedLitAnalyzer: embedに関連するコードの近代化を提案

これによって何ができるようになるか

独自の静的解析ツールやvetツールを構築する開発者が、modernizeパッケージの特定アナライザーを個別に参照・組み込めるようになります。

コード例

// Before: 特定アナライザーを取り出す脆弱な方法
import "golang.org/x/tools/go/analysis/passes/modernize"
// Suiteスライスの中から名前で探す(壊れやすい)
var atomicAnalyzer *analysis.Analyzer
for _, a := range modernize.Suite {
    if a.Name == "atomictypes" {
        atomicAnalyzer = a
    }
}
// After: エクスポートされた変数を直接参照
import "golang.org/x/tools/go/analysis/passes/modernize"
multichecker.Main(
    modernize.AtomicTypesAnalyzer,
    modernize.StringscutAnalyzer,
    myownanalyzer.Analyzer,
)

各アナライザーが検出・提案するコード変換の例(AtomicTypesAnalyzer):

// Before: 型安全でない旧スタイルのアトミック操作
var x int32
atomic.AddInt32(&x, 1)
// After: Go 1.19のアトミックラッパー型を使用
var x atomic.Int32
x.Add(1)

議論のハイライト

  • 提案の動機はgoplsの内部ハック解消: goplsが内部パッケージのアナライザー変数に直接アクセスする仕組みを使っており、それを解消するための正式なAPI公開が求められていた
  • 外部ベンダーツールの需要: cespare(Contributor)が、内部のvetツールにこれらのアナライザーを組み込みたいが、x/toolsをフォークせずに実現できないという問題を報告し、需要の高さを示した
  • 提案のスコープ拡大: 当初はatomicアナライザー単体(issue #77352)の公開が目的だったが、adonovanが「proposal churnを減らすため」として未公開の全アナライザー変数をまとめて公開する形に変更した
  • Suiteによる回避策の存在: adonovanmodernize.Suiteスライスから今すぐ取り出せると指摘しているが、これはAPIの保証がなく実用的ではないと認識されている
  • 純粋なAPI公開: 機能追加や振る舞いの変更はなく、既存のコードをエクスポートするのみであるため、リスクが低く迅速にlikely acceptとなった

関連リンク