
2026/03/12 3:39
「go:fix」は、インライン化とソースレベルでのインライナー(inliner)を修正します。
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
Go 1.26 の
サブコマンドは、関数呼び出しをその本体のコピーで安全に置き換えることができるソースレベルインラインャーを含むように書き直されました。go fix
インラインャーはソースレベルで引数を更新し、サイドエフェクトの順序、名前の影響(シェドウィング)、未使用変数、および遅延呼び出しといった複雑な問題に対処します。コード量は約 7,000 行です。直接置き換えが意味を変更する場合(例:境界外定数やサイドエフェクトのため)、明示的な「パラメータバインディング」宣言を挿入するか、ケースでは関数リテラルにフォールバックします。defer
このインラインャーは Google のモノレポで 18,000 件以上の変更リストを処理し、数十億行にわたるコードから数百万件の非推奨呼び出しを排除しました。同じエンジンが IDE のリファクタリング(「シグネチャ変更」、「未使用パラメータ削除」)やディレクティブによるセルフサービス API マイグレーション(例://go:fix inlineをioutil.ReadFileに置き換える)を動かしています。os.ReadFile
ツールは安全であり、実行される変換がプログラムの振る舞いを変更しないことを保証しますが、スタイル面で最適とは限らないため、一部の保守的な結果については手作業でのクリーンアップが必要になる場合があります。開発者や IDE は対話的または自動でこの機能をトリガーでき、大規模かつ安全なコードマイグレーションを実現し、メンテナンス負荷を削減します。
本文
Go 1.26 では、Go コードを最新かつモダンに保つための go fix サブコマンドが新しく実装されました。
この記事はその中でも特に注目すべき機能 ― ソースレベルでのインライン化 に焦点を当てています。
ソースレベルインライン化とは?
2023 年に、関数呼び出しを呼び出し先(callee)の本体へ置換するアルゴリズムを構築しました。
この変換では引数をパラメータに直接代入し、ソースコードそのものを書き換えます。
コンパイラレベルのインライン化(中間表現で行う)とは異なり、持続的に ソースファイルを更新します。
実際に gopls の「Inline call」リファクタリングや VS Code の「Source Action…」メニューを使うと見たことがあるはずです。
スクリーンショットの before‑after では、関数
six 内で呼び出されていた sum がインライン式に変換されます。
このインライナーは多くのソース変換ツールの基盤となっています:
- gopls は「Change signature」や「Remove unused parameter」といったリファクタリングで使用しています。
- 新しい
コマンドのアナライザの一つでもあり、go fix
ディレクティブを使って セルフサービス な API 置換が可能です。//go:fix inline
例 1 – ioutil.ReadFile
のリネーム
ioutil.ReadFileGo 1.16 で
ioutil.ReadFile は非推奨となり、代わりに os.ReadFile が使われるようになりました。古い関数を次のように注釈します。
package ioutil import "os" // ReadFile reads the file named by filename… // Deprecated: As of Go 1.16, this function simply calls [os.ReadFile]. //go:fix inline func ReadFile(filename string) ([]byte, error) { return os.ReadFile(filename) }
go fix を実行すると、呼び出し側のコードが次のように変換されます。
-import "io/ioutil" +import "os" - data, err := ioutil.ReadFile("hello.txt") + data, err := os.ReadFile("hello.txt")
呼び出しはインライン化され、単に別の関数呼び出しに置き換わるだけなので安全です。
例 2 – API デザイン上の欠陥修正
古いパッケージ
oldmath に以下のような問題があるとします:
package oldmath import "newmath" // Sub returns x - y. // Deprecated: the parameter order is confusing. func Sub(y, x int) int { return newmath.Sub(x, y) } // Inf returns positive infinity. // Deprecated: there are two infinite values; be explicit. func Inf() float64 { return newmath.Inf(+1) } // Neg returns -x. // Deprecated: this function is unnecessary. func Neg(x int) int { return newmath.Sub(0, x) }
各関数に
//go:fix inline を付けると、go fix は古い API への呼び出しをすべて置換します。
-import "oldmath" +import "newmath" -var nine = oldmath.Sub(1, 10) // diagnostic: "call of oldmath.Sub should be inlined" +var nine = newmath.Sub(10, 1)
インラインアナライザは型や定数にも対応します:
//go:fix inline type Rational = newmath.Rational //go:fix inline const Pi = newmath.Pi
oldmath.Rational や oldmath.Pi への参照がすべて自動で更新されます。
インライナーの技術的課題
ソースレベルインライン化は「呼び出しを本体に置き換える」だけではありません。
実装は約 7,000 行に及ぶコンパイラ風ロジックで、以下の 6 つの重要な側面を扱います:
-
パラメータ削除
- 単純なリテラルなら直接置き換え。そうでない場合は「パラメータバインディング」宣言を発行し、重複した魔法値を避ける。
-
副作用
- 引数に副作用がある場合は評価順序を保持。安全なら直接インライン化、そうでなければ各引数を先にバインドする。
-
失敗しやすい定数式
- 定数置換によってコンパイル時エラー(範囲外など)が起きないように注意。
-
シャドウイング
- 引数と呼び出し先本体の識別子が同一シンボルを指すよう、必要に応じてバインディングやインポートを挿入。
-
未使用変数
- 呼び出し元で最後に参照される変数が誤って削除されないように使用状況を追跡。
-
defer
- 呼び出し先が
を使う場合は、即時実行関数リテラルで本体を包む。ただしバッチモードではこうした呼び出しのインライン化は拒否される。defer
- 呼び出し先が
ソースレベルインライン化の将来
インライナーは 音響性(プログラム挙動を変えないこと)を目指しています。
最適化コンパイラと同様に完全な整形は不可能で、ツールが保守的になり手動での修正が必要になるケースもあります。
しかしユーザーは次のように利用できます:
- IDE で「Inline call」リファクタリングを対話的に実行。
- 自身の関数に
ディレクティブを追加。//go:fix inline
を走らせて安全な変換を一括適用。go fix
ぜひエディタや
go fix でインライナーを試し、フィードバックやさらなる改善アイデアを共有してください。