[インデックス 11925] ファイルの概要
このコミットは、Go言語の標準ライブラリにおけるbufio
、compress/gzip
、compress/zlib
パッケージのNewWriterXxx
系の関数シグネチャの変更に対応するためのgo fix
ツールの修正と、関連ドキュメントの更新を含んでいます。特に、これらの関数がエラーを返さなくなった変更に既存のコードを適応させるための自動修正機能が追加されました。
コミット
commit da8f037b57241b0b84fab9d4c9e69b53e7118850
Author: Nigel Tao <nigeltao@golang.org>
Date: Wed Feb 15 14:41:47 2012 +1100
fix: add fix for bufio, gzip, zlib NewWriterXxx changes.
Fixes #2979.
R=rsc, r
CC=golang-dev
https://golang.org/cl/5664046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/da8f037b57241b0b84fab9d4c9e69b53e7118850
元コミット内容
bufio
、gzip
、zlib
パッケージのNewWriterXxx
系の関数変更に対する修正を追加。
Issue #2979を修正。
変更の背景
このコミットは、Go 1のリリースに向けたAPIのクリーンアップの一環として行われました。具体的には、bufio
、compress/gzip
、compress/zlib
パッケージ内のNewWriter
やNewWriterSize
などの関数が、以前はWriter, error
のような形式でエラーを返していたのに対し、エラーを返さないようにシグネチャが変更されました。
このような変更は、Go言語のAPI設計思想に基づいています。Goでは、エラーは明示的に処理されるべきですが、常にエラーが発生するとは限らない初期化関数などでは、エラーを返さない設計が好まれる場合があります。この変更により、これらの関数の呼び出し元で不要なエラーチェックが削減され、コードが簡潔になることが期待されます。
しかし、既存のコードベースでは、これらの関数がエラーを返すことを前提に記述されているため、コンパイルエラーが発生する可能性があります。この問題を解決するため、go fix
ツールに自動修正機能を追加し、開発者が容易にコードを新しいAPIに適応できるようにすることが目的です。
関連するコードレビューは以下の通りです。
http://codereview.appspot.com/5639057
(compress/* packages error handling during API cleanups for Go 1)http://codereview.appspot.com/5642054
(bufio: NewReaderSize and NewWriterSize no longer return errors)
これらのコードレビューは、Go 1のAPI安定化プロセスにおいて、compress
パッケージやbufio
パッケージのNewWriter
関連関数のエラー返却に関する変更が議論され、最終的にエラーを返さない形に落ち着いた経緯を示しています。
前提知識の解説
go fix
ツール:go fix
はGo言語のツールチェーンに含まれるコマンドラインツールで、Go言語のAPI変更や言語仕様の変更に伴い、古いGoコードを新しいGoコードに自動的に書き換える機能を提供します。これにより、Go言語のバージョンアップに伴うコードの修正作業を大幅に軽減できます。bufio
パッケージ:bufio
パッケージは、バッファリングされたI/O操作を実装するための機能を提供します。bufio.NewReader
やbufio.NewWriter
などの関数は、io.Reader
やio.Writer
をラップして、効率的な読み書きを可能にします。compress/gzip
パッケージ:compress/gzip
パッケージは、gzip形式の圧縮データストリームを読み書きするための機能を提供します。gzip.NewWriter
は、io.Writer
をラップしてgzip圧縮データを書き込むための*gzip.Writer
を返します。compress/zlib
パッケージ:compress/zlib
パッケージは、zlib形式の圧縮データストリームを読み書きするための機能を提供します。zlib.NewWriter
は、io.Writer
をラップしてzlib圧縮データを書き込むための*zlib.Writer
を返します。- Goのエラーハンドリング: Go言語では、関数が複数の値を返すことができ、慣習的に最後に戻り値としてエラーを返します。エラーがない場合は
nil
を返します。また、不要な戻り値は_
(ブランク識別子)を使って破棄することができます。例えば、w, _ := gzip.NewWriter(writer)
のように記述されます。
技術的詳細
このコミットの技術的な核心は、go fix
ツールがどのようにして古いAPI呼び出しを新しいAPI呼び出しに変換するかという点にあります。
-
APIシグネチャの変更:
bufio.NewReaderSize(r io.Reader, size int) (*Reader, error)
->bufio.NewReaderSize(r io.Reader, size int) *Reader
bufio.NewWriterSize(w io.Writer, size int) (*Writer, error)
->bufio.NewWriterSize(w io.Writer, size int) *Writer
gzip.NewWriter(w io.Writer) (*Writer, error)
->gzip.NewWriter(w io.Writer) *Writer
zlib.NewWriter(w io.Writer) (*Writer, error)
->zlib.NewWriter(w io.Writer) *Writer
zlib.NewWriterDict(w io.Writer, level int, dict []byte) (*Writer, error)
->zlib.NewWriterLevelDict(w io.Writer, level int, dict []byte) (*Writer, error)
(関数名も変更)
-
型名の変更:
gzip.Compressor
->gzip.Writer
gzip.Decompressor
->gzip.Reader
-
go fix
の動作:go fix
ツールは、Goの抽象構文木(AST: Abstract Syntax Tree)を解析し、特定のパターンに合致するコードを自動的に書き換えます。- エラー変数の削除: 変更前のAPIでは、
w, _ := NewWriter(...)
のように、2つの戻り値のうち2番目のエラー値を_
で破棄しているケースが多く見られました。go fix
は、このような代入文を検出し、エラーを返さなくなった新しいAPIに合わせてw := NewWriter(...)
のように、_
の部分を削除します。ただし、w, err := NewWriter(...)
のようにエラー変数を明示的に宣言している場合は、go fix
は自動修正を行わず、コンパイルエラーとして開発者に手動での修正を促します。これは、err
変数が後続のコードで利用されている可能性があるため、go fix
が安全に削除できないためです。 - 型名の置換:
gzip.Compressor
とgzip.Decompressor
という古い型名が使用されている箇所を検出し、それぞれgzip.Writer
とgzip.Reader
に置換します。 - 関数名の置換:
zlib.NewWriterDict
がzlib.NewWriterLevelDict
にリネームされたことに対応し、関数呼び出しを新しい名前に変更します。
- エラー変数の削除: 変更前のAPIでは、
この修正は、Go言語の進化において、APIの変更が既存のコードベースに与える影響を最小限に抑えるための重要なメカニズムであるgo fix
ツールの役割を明確に示しています。
コアとなるコードの変更箇所
このコミットでは、主に以下の4つのファイルが変更されています。
-
doc/go1.html
およびdoc/go1.tmpl
: Go 1のリリースノートに相当するドキュメントファイルです。bufio
、compress/flate
、compress/gzip
、compress/zlib
パッケージの変更点について記述が追加・修正されています。特に、NewWriterXxx
系の関数がエラーを返さなくなったこと、そしてgo fix
ツールがこれらの変更に対応することについて言及されています。また、go fix
ツールが警告を出すケースについても修正が加えられています。 -
src/cmd/fix/newwriter.go
(新規追加):go fix
ツールに新しい修正ルール「newWriter
」を追加するGoソースファイルです。このファイルには、bufio
、compress/gzip
、compress/zlib
パッケージのNewWriterXxx
系の関数呼び出しと、gzip.Compressor
/gzip.Decompressor
の型名を自動的に修正するためのロジックが実装されています。 -
src/cmd/fix/newwriter_test.go
(新規追加):newwriter.go
で実装されたgo fix
の修正ルールをテストするためのGoソースファイルです。修正前と修正後のコードスニペットを定義し、go fix
が期待通りにコードを変換するかどうかを検証します。
コアとなるコードの解説
src/cmd/fix/newwriter.go
がこのコミットの主要な変更点です。
package main
import (
"go/ast"
)
func init() {
register(newWriterFix)
}
var newWriterFix = fix{
"newWriter",
"2012-02-14",
newWriter,
`Adapt bufio, gzip and zlib NewWriterXxx calls for whether they return errors.
Also rename gzip.Compressor and gzip.Decompressor to gzip.Writer and gzip.Reader.
http://codereview.appspot.com/5639057 and
http://codereview.appspot.com/5642054
`,
}
func newWriter(f *ast.File) bool {
// 修正対象のパッケージがインポートされているか確認
if !imports(f, "bufio") && !imports(f, "compress/gzip") && !imports(f, "compress/zlib") {
return false // 関連パッケージがインポートされていなければ修正不要
}
fixed := false
walk(f, func(n interface{}) {
switch n := n.(type) {
case *ast.SelectorExpr: // セレクタ式 (例: gzip.Compressor) の処理
if isTopName(n.X, "gzip") {
switch n.Sel.String() {
case "Compressor":
n.Sel = &ast.Ident{Name: "Writer"} // gzip.Compressor -> gzip.Writer
fixed = true
case "Decompressor":
n.Sel = &ast.Ident{Name: "Reader"} // gzip.Decompressor -> gzip.Reader
fixed = true
}
} else if isTopName(n.X, "zlib") {
if n.Sel.String() == "NewWriterDict" {
n.Sel = &ast.Ident{Name: "NewWriterLevelDict"} // zlib.NewWriterDict -> zlib.NewWriterLevelDict
fixed = true
}
}
case *ast.AssignStmt: // 代入文 (例: w, _ = gzip.NewWriter(w)) の処理
// 2つの左辺値と1つの右辺値を持つ代入文を対象とする
if len(n.Lhs) != 2 || len(n.Rhs) != 1 {
return
}
// 2番目の左辺値がブランク識別子 '_' であるか確認
i, ok := n.Lhs[1].(*ast.Ident)
if !ok {
return
}
if i.String() != "_" {
return // '_' でなければ修正しない (例: w, err := ...)
}
// 右辺値が関数呼び出しであるか確認
c, ok := n.Rhs[0].(*ast.CallExpr)
if !ok {
return
}
// 関数呼び出しがセレクタ式 (例: gzip.NewWriter) であるか確認
s, ok := c.Fun.(*ast.SelectorExpr)
if !ok {
return
}
sel := s.Sel.String()
switch {
// bufio.NewReaderSize または bufio.NewWriterSize の呼び出し
case isTopName(s.X, "bufio") && (sel == "NewReaderSize" || sel == "NewWriterSize"):
// No-op. (ここでは特に何もしないが、後続の処理でLhsを修正する)
// gzip.NewWriter の呼び出し
case isTopName(s.X, "gzip") && sel == "NewWriter":
// No-op.
// zlib.NewWriter の呼び出し
case isTopName(s.X, "zlib") && sel == "NewWriter":
// No-op.
default:
return // 上記以外の関数呼び出しは修正しない
}
// 左辺値から2番目の要素 (エラーを破棄していた '_') を削除
n.Lhs = n.Lhs[:1]
fixed = true
}
})
return fixed
}
このコードは、Goのgo/ast
パッケージを使用してソースコードの抽象構文木を走査し、特定のパターンに合致するノードを修正します。
newWriter
関数:go fix
の修正ロジック本体です。- まず、対象のファイルが
bufio
、compress/gzip
、compress/zlib
のいずれかのパッケージをインポートしているかを確認します。インポートしていなければ、修正の必要がないため処理を終了します。 walk
関数(go fix
フレームワークの一部)を使ってASTを再帰的に走査します。*ast.SelectorExpr
の処理: これはpackage.Name
のような形式の式を表します。gzip.Compressor
やgzip.Decompressor
といった型名が使用されている場合、それぞれgzip.Writer
、gzip.Reader
に書き換えます。zlib.NewWriterDict
が使用されている場合、zlib.NewWriterLevelDict
に書き換えます。
*ast.AssignStmt
の処理: これは代入文を表します。w, _ = functionCall()
のように、2つの左辺値(変数とブランク識別子_
)と1つの右辺値(関数呼び出し)を持つ代入文を検出します。- 右辺値の関数呼び出しが
bufio.NewReaderSize
、bufio.NewWriterSize
、gzip.NewWriter
、zlib.NewWriter
のいずれかである場合、左辺値からブランク識別子_
を削除し、w = functionCall()
のように1つの左辺値のみを持つ代入文に変換します。これにより、エラーを返さなくなった新しいAPIシグネチャに対応します。
- まず、対象のファイルが
src/cmd/fix/newwriter_test.go
は、このnewWriter
関数の動作を検証するためのテストケースを定義しています。特に、bw, err := bufio.NewWriterSize(w, 256)
のようにエラー変数を明示的に宣言しているケースはgo fix
が修正しないこと(Unfixable
とコメントされている)もテストケースで確認されています。
関連リンク
- Go issue #2979: https://github.com/golang/go/issues/2979
- Go Code Review 5639057: http://codereview.appspot.com/5639057
- Go Code Review 5642054: http://codereview.appspot.com/5642054
参考にした情報源リンク
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH_U18hg3Zy25-49br0Wf0wRz9_N34U7I00WZk56SRSqkRkkkhOOQ0L8D17NhofO8_6nKtQOsRXoG5dFQBEVYRvnQbao9FoaiDr9m9mUp78I8C9N14oD9rVyg-eLqieFpnHEOUOJoo=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGSCm3D4B_Ys7ZGEZxA-yIFQyuq0o6zHVxtwsuMi3lL-s8K31tfLMVtOqGIsDZmBc3Y_NVwbsUsJ5y6cTtnrhLcA5ZPwg7rJjdQV1ULpkk72B2gfjqbJWhFon4cmXmMRyFosA=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH40Eb4k1sxwYXP6skkogGSt13KME8athwW8muFjGlpgO6QoZeJkSO6_Ypyvnhk9wHJmo8fIBbAB5jJzyyh3I065-2y9kpYfrie6QQwVwbk8d7-Mb3Y-oLd-hxz_BlbHAqwsgJKhekgt6pPw7iq26k=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQG-uBd8kjQyJVFoc5abP_q_IFCHvbQY_vYt2So1q6msh4iKZUD5DRvNH11Dd-ST4E-MoEoedgGBUmEV2cK89vBbSobIcudEfjuPfgRo8CAEJnKJc1y-hTy9OIU=