go fixでGoのコードをモダン化する
Using go fix to modernize Go code by Alan Donovan
今月リリースされたGo 1.26には、完全に書き直された go fix サブコマンドが含まれています。go fix は、多くの場合、より新しい言語や標準ライブラリの機能を活用することで、コードを改善できる箇所を特定する一連のアルゴリズムを使用します。この記事ではまず、go fix を使ってGoのコードベースをモダン化する方法を紹介します。続く2番目のセクションでは、その背後にあるインフラストラクチャと、それがどのように進化しているかを掘り下げます。最後に、モジュールのメンテナーや組織が独自のガイドラインやベストプラクティスをコード化するのを助ける、「セルフサービス」分析ツールというテーマを紹介します。
go fixを実行する
go fix コマンドは、go build や go vet と同様に、パッケージを示す一連のパターンを受け取ります。次のコマンドはカレントディレクトリ配下のすべてのパッケージを修正します。
$ go fix ./...
成功すると、ソースファイルが黙って更新されます。生成されたファイルに触れる修正は破棄されます。そのような場合の適切な修正は、生成器自体のロジックに対して行うべきだからです。プロジェクトのビルドをより新しいGoツールチェインのリリースに更新するたびに、go fix を実行することをお勧めします。このコマンドは数百のファイルを修正することがあるため、変更がgo fixによる編集のみで構成されるように、クリーンなgitの状態から始めましょう。コードレビュアーに感謝されるはずです。
上記のコマンドが行うであろう変更をプレビューするには、-diff フラグを使います。
$ go fix -diff ./...
--- dir/file.go (old)
+++ dir/file.go (new)
- eq := strings.IndexByte(pair, '=')
- result[pair[:eq]] = pair[1+eq:]
+ before, after, _ := strings.Cut(pair, "=")
+ result[before] = after
…
次のコマンドを実行すると、利用可能なフィクサーの一覧を確認できます。
$ go tool fix help
…
Registered analyzers:
any replace interface{} with any
buildtag check //go:build and // +build directives
fmtappendf replace []byte(fmt.Sprintf) with fmt.Appendf
forvar remove redundant re-declaration of loop variables
hostport check format of addresses passed to net.Dial
inline apply fixes based on 'go:fix inline' comment directives
mapsloop replace explicit loops over maps with calls to maps package
minmax replace if/else statements with calls to min or max
…
特定のアナライザーの名前を追加すると、その完全なドキュメントが表示されます。
$ go tool fix help forvar
forvar: remove redundant re-declaration of loop variables
The forvar analyzer removes unnecessary shadowing of loop variables.
Before Go 1.22, it was common to write `for _, x := range s { x := x ... }`
to create a fresh variable for each iteration. Go 1.22 changed the semantics
of `for` loops, making this pattern redundant. This analyzer removes the
unnecessary `x := x` statement.
This fix only applies to `range` loops.
デフォルトでは、go fix コマンドはすべてのアナライザーを実行します。大規模なプロジェクトを修正する場合、修正件数の多いアナライザーからの修正を別々のコード変更として適用すると、コードレビューの負担を減らせるかもしれません。特定のアナライザーだけを有効にするには、その名前に対応するフラグを使います。たとえば any フィクサーだけを実行するには -any フラグを指定します。逆に、選択したアナライザー 以外 をすべて実行するには、-any=false のようにフラグを否定形にします。
go build や go vet と同様に、go fix コマンドの各実行では特定のビルド構成のみが解析されます。プロジェクトが異なるCPUやプラットフォーム向けにタグ付けされたファイルを多用している場合、カバレッジを高めるために GOARCH と GOOS の値を変えて複数回コマンドを実行するとよいでしょう。
$ GOOS=linux GOARCH=amd64 go fix ./...
$ GOOS=darwin GOARCH=arm64 go fix ./...
$ GOOS=windows GOARCH=amd64 go fix ./...
コマンドを複数回実行することは、後述するように相乗効果のある修正の機会にもなります。
モダナイザー
Go 1.18でのジェネリクスの導入は、言語仕様の変更が非常に少なかった時代の終わりを告げ、特にライブラリにおいて、より速い(とはいえ依然として慎重な)変更の時代の始まりを意味しました。マップのキーをスライスに集めるといった、Goプログラマが日常的に書いていた多くのありふれたループは、今ではmaps.Keysのようなジェネリック関数の呼び出しとして手軽に表現できるようになりました。その結果、こうした新機能によって既存のコードを単純化する機会が数多く生まれています。
2024年12月、LLMコーディングアシスタントの熱狂的な普及の最中、私たちはこうしたツールが(当然といえば当然ですが)たとえ同じ考えを表現するもっと新しくて良い方法があっても、学習に使われた大量のGoコードに似たスタイルでGoコードを生成する傾向があることに気づきました。あまり目立ちませんが、同じツールは「常にGo 1.25の最新のイディオムを使う」といった一般的な指示を出しても、新しい書き方を使うのを拒むことがしばしばありました。ある機能を明示的に使うよう指示しても、モデルがその機能の存在自体を否定するケースさえありました。(さらに苛立たしい詳細については、私の2025年GopherConでの講演を参照してください。)将来のモデルが最新のイディオムで学習されるようにするには、そうしたイディオムが学習データ、つまりオープンソースのGoコードの世界的なコーパスに反映されている必要があります。
この1年で、モダン化の機会を特定するための数十個のアナライザーを構築してきました。ここでは、それらが提案する修正の例を3つ紹介します。
minmax は if 文をGo 1.21の min や max 関数の利用で置き換えます。
x := f()
if x < 0 {
x = 0
}
if x > 100 {
x = 100
}
⟶
x := min(max(f(), 0), 100)
rangeint は3節からなる for ループを、Go 1.22の整数に対する range ループで置き換えます。
for i := 0; i < n; i++ {
f()
}
⟶
for range n {
f()
}
stringscut(その -diff の出力は先ほど見ました)は、strings.Index とスライシングの利用をGo 1.18の strings.Cut で置き換えます。
i := strings.Index(s, ":")
if i >= 0 {
return s[:i]
}
⟶
before, _, ok := strings.Cut(s, ":")
if ok {
return before
}
これらのモダナイザーは、入力中に即座にフィードバックを提供するgoplsと、複数のパッケージ全体を1つのコマンドで一度にモダン化できる go fix の両方に含まれています。コードを明確にすることに加えて、モダナイザーはGoプログラマが新しい機能について学ぶ助けにもなります。言語や標準ライブラリへの新しい変更それぞれを承認するプロセスの一環として、プロポーザルのレビューグループは今では、モダナイザーを伴うべきかどうかを検討しています。今後もリリースごとにモダナイザーを追加していく予定です。
例:Go 1.26の new(expr) のためのモダナイザー
Go 1.26には、小さいながらも広く役立つ言語仕様の変更が含まれています。組み込みの new 関数は新しい変数を作成し、そのアドレスを返します。これまでは、唯一の引数は new(string) のように型でなければならず、新しい変数は "" のような「ゼロ」値で初期化されていました。Go 1.26では、new 関数を任意の値とともに呼び出せるようになり、その値で初期化された変数を作成できるため、追加の文が不要になります。たとえば次のようになります。
ptr := new(string)
*ptr = "go1.25"
⟶
ptr := new("go1.26")
この機能は10年以上議論されてきた隙間を埋めるもので、言語への変更として最も人気のあるプロポーザルの1つを解決しました。これは、json.Marshalやプロトコルバッファのようなシリアライゼーションパッケージを扱う際によくあるように、ポインタ型 *T を使って型 T の省略可能な値を表現するコードで特に便利です。これはよくあるパターンなので、以下の newInt 関数のようなヘルパーとして捉えられることが多く、呼び出し側が式のコンテキストから抜け出して追加の文を導入する必要がなくなります。
type RequestJSON struct {
URL string
Attempts *int // (省略可能)
}
data, err := json.Marshal(&RequestJSON{
URL: url,
Attempts: newInt(10),
})
func newInt(x int) *int { return &x }
newInt のようなヘルパーはプロトコルバッファで非常に頻繁に必要とされるため、proto API自体がproto.Int64やproto.Stringなどとしてそれらを提供しています。しかしGo 1.26は、これらのヘルパーをすべて不要にします。
data, err := json.Marshal(&RequestJSON{
URL: url,
Attempts: new(10),
})
この機能を活用しやすくするために、go fix コマンドには newexpr というフィクサーが新たに含まれています。これは newInt のような「new的な」関数を認識し、関数本体を return new(x) に置き換える修正と、同じパッケージ内であれインポート元のパッケージであれ、すべての呼び出しを new(expr) の直接利用に置き換える修正を提案します。
新機能の時期尚早な利用を避けるため、モダナイザーは、対応するファイルが少なくとも必要な最小限のGoバージョン(この場合は1.26)を要求している場合、つまり囲んでいるgo.modファイル内のgo 1.26 ディレクティブか、ファイル自体の中の //go:build go1.26 というビルド制約がある場合にのみ修正を提案します。
ソースツリー内でこの形式のすべての呼び出しを更新するには、次のコマンドを実行します。
$ go fix -newexpr ./...
この時点で、うまくいけば、newInt のようなヘルパー関数はすべて未使用になり、安全に削除できるようになっているはずです(安定した公開APIの一部でない限り)。名前 new が他の宣言によってローカルにシャドウされている場合など、修正を提案するのが安全でない一部の呼び出しは残るかもしれません。未使用の関数を特定するためにdeadcodeコマンドを使うこともできます。
相乗効果のある修正
1つのモダン化を適用すると、別のモダン化を適用する機会が生まれることがあります。たとえば、x を0から100の範囲に収める次のコードスニペットでは、minmaxモダナイザーが max を使うよう修正を提案します。その修正を適用すると、今度は min を使うようにという2つ目の修正が提案されます。
x := f()
if x < 0 {
x = 0
}
if x > 100 {
x = 100
}
⟶
x := min(max(f(), 0), 100)
相乗効果は異なるアナライザーの間でも起こりえます。たとえば、ループの中で文字列を繰り返し連結してしまうというよくある間違いは、二次関数的な時間計算量を招くバグであり、サービス拒否攻撃の潜在的な攻撃経路にもなります。stringsbuilder モダナイザーはこの問題を認識し、Go 1.10の strings.Builder を使うことを提案します。
s := ""
for _, b := range bytes {
s += fmt.Sprintf("%02x", b)
}
use(s)
⟶
var s strings.Builder
for _, b := range bytes {
s.WriteString(fmt.Sprintf("%02x", b))
}
use(s.String())
この修正が適用されると、2つ目のアナライザーが WriteString と Sprintf の操作を fmt.Fprintf(&s, "%02x", b) として結合できることを認識し、よりクリーンかつ効率的なこの形にする2つ目の修正を提案するかもしれません。(この2つ目のアナライザーは、Dominik HonnefのstaticcheckにあるQF1012で、すでにgoplsでは有効になっていますが、go fix ではまだです。ただし、Go 1.27からstaticcheckのアナライザーをgoコマンドに追加する計画があります。)
したがって、go fix は不動点に達するまで複数回実行する価値があるかもしれません。通常は2回で十分です。
修正のマージと競合
go fix の1回の実行で、同じソースファイル内に数十個の修正が適用されることがあります。すべての修正は、同じ親を持つ一連のgitコミットになぞらえられるように、概念的には独立しています。go fix コマンドは、同じファイルを編集する一連のgitコミットをマージする作業になぞらえられる、単純な3-wayマージアルゴリズムを使って修正を順番に統合します。ある修正がそれまでに蓄積された編集リストと競合する場合、その修正は破棄され、ツールはいくつかの修正がスキップされたこと、そして再度ツールを実行すべきであることを警告します。
これにより、重複する編集から生じる 構文的 な競合は確実に検出されますが、もう一種類の競合もありえます。意味的 な競合は、2つの変更がテキスト上は独立していても、その意味が両立しない場合に起こります。例として、それぞれがローカル変数の最後から2番目の使用箇所を削除する2つの修正を考えてみましょう。それぞれの修正は単独では問題ありませんが、両方を一緒に適用すると、そのローカル変数は未使用になり、Goではこれはコンパイルエラーになります。どちらの修正も変数宣言を削除する責任を負っていませんが、誰かがそれをしなければならず、それは go fix の利用者です。
同様の意味的な競合は、一連の修正によってインポートが未使用になる場合にも起こります。このケースは非常によくあるため、go fix コマンドは未使用のインポートを検出して自動的に削除する最終パスを適用します。
意味的な競合は比較的まれです。幸いなことに、たいていはコンパイルエラーとして現れるため、見落とすことはまずありません。残念ながら、それが起きた場合は、go fix の実行後にいくらか手作業が必要になります。
それでは、これらのツールの背後にあるインフラストラクチャを掘り下げていきましょう。
Go analysisフレームワーク
Goの最も初期の頃から、go コマンドには静的解析のための2つのサブコマンド、go vet と go fix があり、それぞれが「チェッカー」と「フィクサー」という独自のアルゴリズム群を持っていました。チェッカーは、fmt.Printf("%d") 変換のオペランドに整数の代わりに文字列を渡すといった、コード中のありそうな間違いを報告します。フィクサーは、バグを修正したり、同じことをより良い方法(おそらくより明確に、簡潔に、あるいは効率的に)で表現したりするために、コードを安全に編集します。ある間違いを報告しつつ安全に修正もできるアルゴリズムが、両方のスイートに登場することもあります。
2017年、私たちは当時モノリシックだった go vet プログラムを再設計し、チェッカーのアルゴリズム(現在は「アナライザー」と呼ばれています)を、それらを実行するプログラムである「ドライバー」から分離しました。その成果がGo analysisフレームワークです。この分離により、アナライザーを一度書けば、さまざまな環境向けの多様なドライバーで実行できるようになります。たとえば次のようなものです。
- unitchecker:アナライザー群を、goコマンドのスケーラブルなインクリメンタルビルドシステムから、go buildにおけるコンパイラのように実行できるサブコマンドに変換します。これが
go fixとgo vetの基盤です。 - nogo:BazelやBlazeのような代替のビルドシステム向けの、同様のドライバーです。
- singlechecker:1つのアナライザーを、一連のパッケージ(プログラム全体の場合もあります)を読み込み、パースし、型チェックしたうえで解析する、スタンドアロンのコマンドに変換します。私たちはこれを、モジュールミラー(proxy.golang.org)のコーパスに対するその場限りの実験や計測によく使います。
- multichecker:同じことを、「万能ナイフ」的なCLIを持つアナライザー群に対して行います。
- gopls:VS Codeなどのエディタの裏側にある言語サーバーで、エディタでのキー入力のたびにアナライザーからのリアルタイムな診断を提供します。
- staticcheckツールが使用する、高度に設定可能なドライバー。(staticcheckは、他のドライバーでも実行できる大規模なアナライザー群も提供しています。)
- Tricorder:Googleのモノレポで使われ、コードレビューシステムと統合されているバッチ静的解析パイプラインです。
- goplsのMCPサーバー:LLMベースのコーディングエージェントが診断結果を利用できるようにし、より堅牢な「ガードレール」を提供します。
- analysistest:analysisフレームワークのテストハーネスです。
このフレームワークの利点の1つは、それ自体では診断の報告や修正の提案を行わず、代わりに他の多くのアナライザーにとって有用な中間的なデータ構造を計算することで、その構築コストを償却するようなヘルパーアナライザーを表現できることです。例としては、制御フローグラフ、関数本体のSSA表現、最適化されたAST走査のためのデータ構造などが挙げられます。
このフレームワークのもう1つの利点は、パッケージをまたいだ推論をサポートしていることです。アナライザーは関数などのシンボルに「ファクト」を付与でき、それによって、関数本体を解析する際に学習した情報を、その呼び出しが別のパッケージにあったり、後の解析が別のプロセスで行われたりしても、後でその関数の呼び出しを解析する際に利用できます。これにより、スケーラブルな手続き間解析を簡単に定義できます。たとえば、printfチェッカーは log.Printf のような関数が実は fmt.Printf の単なるラッパーであることを見分けられるので、log.Printf への呼び出しも同様の方法でチェックすべきだとわかります。この処理は帰納的に働くため、ツールは log.Printf をさらにラップする関数への呼び出しもチェックします。ファクトを多用するアナライザーの例としては、nilポインタ参照外しに起因する潜在的な間違いを報告するUberのnilawayが挙げられます。
go fix における「分離解析」のプロセスは、go build における分離コンパイルのプロセスになぞらえられます。コンパイラが依存関係グラフの末端からパッケージをビルドし、型情報をインポート元のパッケージへと伝えていくのとちょうど同じように、analysisフレームワークも依存関係グラフの末端から上に向かって動作し、ファクト(および型)をインポート元のパッケージへと伝えていきます。
2019年、Go向けの言語サーバーであるgoplsの開発を始めた際、私たちはアナライザーが診断を報告する際に修正を提案できる機能を追加しました。たとえばprintfアナライザーは、動的な msg の値に % 記号が含まれていた場合の誤ったフォーマットを避けるために、fmt.Printf(msg) を fmt.Printf("%s", msg) に置き換えることを提案します。この仕組みは、goplsの多くのクイックフィックスやリファクタリング機能の基盤になっています。
こうした発展がすべて go vet に対して起きている一方で、go fix は、Goの初期採用者たちが言語やライブラリの急速で時には非互換な進化の中でコードを保守するために使っていた、Goの互換性の約束以前のままの姿で取り残されていました。
Go 1.26のリリースにより、Go analysisフレームワークが go fix にもたらされました。go vet と go fix のコマンドは収束し、実装上はほぼ同一になっています。両者の違いは、使用するアルゴリズム群の採用基準と、計算された診断結果をどう扱うかだけです。Goのvetアナライザーは、誤検知の少ない、ありそうな間違いを検出しなければならず、その診断結果はユーザーに報告されます。Goのfixアナライザーは、正しさ、パフォーマンス、スタイルの点で後退を招くことなく安全に適用できる修正を生成しなければならず、その診断結果は報告されないこともありますが、修正は直接適用されます。この重点の違いを除けば、フィクサーを開発する作業はチェッカーを開発する作業と何ら変わりません。
解析インフラストラクチャの改善
go vet と go fix のアナライザーの数が増え続けるにつれて、私たちは各アナライザーのパフォーマンスを改善することと、新しいアナライザーを書きやすくすることの両方のために、インフラストラクチャへの投資を続けてきました。
たとえば、ほとんどのアナライザーは、range文や関数リテラルといった特定の種類のノードを探して、パッケージ内の各ファイルの構文木を走査することから始めます。既存のinspectorパッケージは、完全な走査に関するコンパクトなインデックスを事前計算しておくことで、このスキャンを効率化しており、後続の走査では関心のあるノードを含まない部分木を素早くスキップできます。最近、私たちはこれにCursorというデータ型を拡張しました。これにより、HTMLのDOM要素をたどるのと同じように、上下左右の4方向すべてでノード間を柔軟かつ効率的にたどれるようになり、「ループ本体の最初の文であるgo文をそれぞれ見つける」といったクエリを簡単かつ効率的に表現できます。
var curFile inspector.Cursor = ...
// ループ本体の最初の文であるgo文をそれぞれ見つける。
for curGo := range curFile.Preorder((*ast.GoStmt)(nil)) {
kind, index := curGo.ParentEdge()
if kind == edge.BlockStmt_List && index == 0 {
switch curGo.Parent().ParentEdgeKind() {
case edge.ForStmt_Body, edge.RangeStmt_Body:
...
}
}
}
多くのアナライザーは、fmt.Printf のような特定の関数への呼び出しを探すことから始めます。関数呼び出しはGoコードの中で最も数多く登場する式の1つなので、すべての呼び出し式を調べてそれが fmt.Printf への呼び出しかどうかを判定するよりも、シンボル参照のインデックスをあらかじめ計算しておくほうがずっと効率的です。これはtypeindexとそのヘルパーアナライザーによって行われています。こうしておけば fmt.Printf への呼び出しを直接列挙でき、コストはパッケージのサイズではなく呼び出しの数に比例するようになります。(net.Dialのような)あまり使われないシンボルを探すhostportのようなアナライザーでは、これによって簡単に1,000倍高速になることがあります。
この1年間のその他のインフラストラクチャの改善には、次のようなものがあります。
- アナライザーがインポートの循環を避けるために参照できる標準ライブラリの依存関係グラフ。たとえば、
strings自身にインポートされているパッケージの中にstrings.Cutへの呼び出しを導入することはできません。 - 囲んでいるgo.modファイルとビルドタグによって決まる、ファイルの有効なGoバージョンを問い合わせる機能。これにより、アナライザーが「新しすぎる」機能の利用を挿入してしまうことを防ぎます。
- 隣接するコメントなどの厄介なエッジケースを正しく扱う、より豊富なリファクタリング用プリミティブのライブラリ(たとえば「この文を削除する」など)。
私たちはここまで長い道のりを歩んできましたが、まだやるべきことがたくさん残っています。フィクサーのロジックを正しくするのは一筋縄ではいかないことがあります。ユーザーはざっとしたレビューだけで何百もの提案された修正を適用することが見込まれるため、フィクサーが目立たないエッジケースでも正しく動作することが重要です。ほんの一例を挙げると(さらにいくつかの例は私のGopherConでの講演を参照してください)、私たちは append([]string{}, slice...) のような呼び出しを、より明快な slices.Clone(slice) に置き換えるモダナイザーを作りましたが、slice が空の場合、Cloneの結果がnilになることが判明しました。これはまれにバグを引き起こしうる微妙な挙動の変化だったため、そのモダナイザーは go fix のスイートから除外せざるを得ませんでした。
アナライザーの作者が直面するこうした困難のいくつかは、(人間とLLMの両方向けの)より良いドキュメント、特に検討したりテストしたりすべき驚くようなエッジケースのチェックリストによって和らげられます。staticcheckやTree Sitterにあるものと同様の、構文木に対するパターンマッチングエンジンがあれば、修正が必要な箇所を効率的に特定するという面倒な作業を単純化できるでしょう。正確な修正を計算するための、より豊富な演算子のライブラリがあれば、よくある間違いを避ける助けになるでしょう。より良いテストハーネスがあれば、修正がビルドを壊さないこと、そして対象コードの動的な性質が保たれることを確認できるようになるでしょう。これらはすべて私たちのロードマップに載っています。
「セルフサービス」パラダイム
より根本的には、私たちは2026年、「セルフサービス」パラダイムへと目を向けています。
先ほど見た newexpr アナライザーは典型的なモダナイザーです。つまり、特定の機能に合わせて作られたオーダーメイドのアルゴリズムです。このオーダーメイドのモデルは言語や標準ライブラリの機能に対してはうまく機能しますが、サードパーティ製パッケージの利用箇所を更新する助けにはあまりなりません。自分自身の公開APIのためのモダナイザーを書いて自分のプロジェクトで実行することを妨げるものは何もありませんが、そのAPIの利用者にもそれを実行してもらう自動的な方法はありません。そのモダナイザーは、対象のAPIがGoのエコシステム全体で特に広く使われているのでない限り、goplsや go vet のスイートに含まれることはまずないでしょう。たとえそうであったとしても、コードレビューと承認を得たうえで、次のリリースを待たなければならないでしょう。
セルフサービスパラダイムのもとでは、Goプログラマは自分自身のAPI向けのモダン化を定義でき、その利用者は現在の中央集権的なパラダイムにおけるあらゆるボトルネックなしにそれを適用できるようになります。これは、Goコミュニティと世界的なGoコーパスが、私たちのチームがアナライザーへの貢献をレビューできる能力よりもはるかに速く成長している中で、特に重要です。
Go 1.26の go fix コマンドには、この新しいパラダイムの最初の成果のプレビューとして、アノテーション駆動のソースレベルインライナーが含まれています。これについては続編の記事で説明しています。今後1年間で、このパラダイムの中でさらに2つのアプローチを検討していく予定です。
まず、ソースツリーからモダナイザーを動的に読み込み、goplsまたは go fix のいずれかで安全に実行する可能性を検討していきます。このアプローチでは、たとえばSQLデータベース向けのAPIを提供するパッケージが、SQLインジェクションの脆弱性や重要なエラーの処理漏れといった、そのAPIの誤用に対するチェッカーを追加で提供できるようになります。同じ仕組みは、プロジェクトのメンテナーが、特定の問題のある関数への呼び出しを避けるとか、コードの重要な部分でより厳格なコーディング規律を強制するといった、内部的な管理ルールをコード化するためにも使えます。
次に、既存の多くのチェッカーは「Yしたら、Xすることを忘れずに!」というように非公式に説明できます。たとえば「ファイルを開いたら閉じる」「コンテキストを作成したらキャンセルする」「ミューテックスをロックしたらアンロックする」「yieldがfalseを返したらイテレータのループを抜ける」などです。こうしたチェッカーに共通するのは、すべての実行パスに対してある種の不変条件を強制するという点です。私たちは、Goプログラマが複雑な解析ロジックを必要とせず、単に自分自身のコードにアノテーションを付けるだけで新しい領域に簡単に適用できるように、これらの制御フローチェッカーを一般化し統合する方法を検討していく予定です。
これらの新しいツールが、Goプロジェクトの保守における労力を節約し、新しい機能についてより早く学び、その恩恵を受ける助けになれば幸いです。ぜひ自分のプロジェクトで go fix を試してみて、見つけた問題があれば報告してください。そして、新しいモダナイザー、フィクサー、チェッカー、あるいは静的解析へのセルフサービス的なアプローチについてのアイデアがあれば、ぜひ共有してください。
By Alan Donovan