[インデックス 17707] ファイルの概要
このコミットは、Go言語の標準ライブラリ内の複数のテストファイル (src/pkg/encoding/gob
, src/pkg/math/big
, src/pkg/net/http
, src/pkg/net
, src/pkg/regexp
, src/pkg/runtime
以下) における、go tool vet
によって発見された書式指定文字列の誤りを修正するものです。具体的には、t.Errorf
や t.Fatalf
といったテストヘルパー関数内で使用されているフォーマット文字列と、それに渡される引数の型が一致していない箇所が修正されています。
影響を受けるファイルは以下の通りです。
src/pkg/encoding/gob/encoder_test.go
src/pkg/encoding/gob/gobencdec_test.go
src/pkg/math/big/int_test.go
src/pkg/net/http/header_test.go
src/pkg/net/http/request_test.go
src/pkg/net/http/serve_test.go
src/pkg/net/ip_test.go
src/pkg/net/timeout_test.go
src/pkg/regexp/all_test.go
src/pkg/regexp/syntax/parse_test.go
src/pkg/runtime/crash_test.go
src/pkg/runtime/mfinal_test.go
コミット
commit fa7791e92228326d947ff65195bdc11a0a4852ef
Author: Rob Pike <r@golang.org>
Date: Fri Sep 27 10:09:15 2013 +1000
all: fix some mistakes found by go tool vet .
R=golang-dev, iant, adg
CC=golang-dev
https://golang.org/cl/14000043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/fa7791e92228326d947ff65195bdc11a0a4852ef
元コミット内容
このコミットは、go tool vet
というGo言語の静的解析ツールによって発見されたいくつかの誤りを修正するものです。具体的には、テストコード内のエラーメッセージの書式指定文字列と引数の不一致が修正されています。
変更の背景
Go言語には、コードの潜在的なバグや疑わしい構造を検出するための静的解析ツール go tool vet
があります。このツールは、コンパイル時には検出されないが実行時に問題を引き起こす可能性のあるパターン(例えば、fmt.Printf
系の関数で書式指定子と引数の型が一致しない場合など)を特定するのに役立ちます。
このコミットは、go tool vet
を実行した際に報告された、テストコード内の t.Errorf
や t.Fatalf
の呼び出しにおける書式指定の誤りを修正するために作成されました。これらの誤りは、テストが失敗した際に表示されるエラーメッセージが意図した通りに整形されない原因となりますが、プログラムの実行自体に直接的なクラッシュを引き起こすものではありません。しかし、正確なエラーメッセージはデバッグにおいて非常に重要であるため、これらの修正はコードの品質と保守性を向上させます。
前提知識の解説
go tool vet
go tool vet
は、Go言語のソースコードを静的に解析し、疑わしい構造や潜在的なバグを報告するツールです。コンパイラが検出できないような、しかし実行時に問題を引き起こす可能性のあるコードパターンを特定します。例えば、以下のような問題を検出できます。
fmt.Printf
系の関数における書式指定子と引数の型の不一致。- 到達不能なコード。
- ロックの誤用。
- 構造体タグの誤り。
range
ループにおける変数の誤用。
go tool vet .
のように実行することで、カレントディレクトリ以下のGoソースコードを解析します。
testing
パッケージと *testing.T
Go言語の標準ライブラリには、テストを記述するための testing
パッケージが提供されています。テスト関数は func TestXxx(t *testing.T)
の形式で定義され、*testing.T
型の引数 t
を受け取ります。この t
オブジェクトは、テストの実行を制御したり、テスト結果を報告したりするための様々なメソッドを提供します。
t.Errorf
と t.Fatalf
*testing.T
オブジェクトが提供する主なエラー報告メソッドには、Errorf
と Fatalf
があります。
t.Errorf(format string, args ...interface{})
: テスト中にエラーが発生したことを報告しますが、テストの実行は継続します。複数のエラーを報告したい場合や、テストの残りの部分も実行して他の問題がないか確認したい場合に便利です。t.Fatalf(format string, args ...interface{})
: テスト中に致命的なエラーが発生したことを報告し、現在のテストの実行を即座に停止します。これ以降のテストコードは実行されません。通常、テストの前提条件が満たされない場合や、これ以上テストを続行しても意味がない場合に用いられます。
これらのメソッドは、fmt.Printf
と同様に書式指定文字列と可変引数を受け取ります。
fmt
パッケージの書式指定子
Go言語の fmt
パッケージは、文字列の書式設定と出力のための機能を提供します。Printf
系の関数(fmt.Printf
, fmt.Sprintf
, t.Errorf
, t.Fatalf
など)では、書式指定子(verb)を使用して引数の値をどのように文字列に変換するかを制御します。主な書式指定子には以下のようなものがあります。
%v
: 値をデフォルトの書式で出力します。任意の型に適用できます。構造体や配列なども適切に表示されます。%d
: 整数値を10進数で出力します。%s
: 文字列を出力します。%q
: Goの構文で安全に引用符で囲まれた文字列を出力します。文字列リテラルとして解釈できる形式になります。%g
: 浮動小数点数を、指数表記または通常の表記で、より短く表現します。%T
: 引数のGo言語の型名を出力します。
go tool vet
は、これらの書式指定子と対応する引数の型が一致しない場合に警告を発します。例えば、整数を期待する %d
に文字列を渡したり、文字列を期待する %s
に数値を渡したりするケースです。
技術的詳細
このコミットで修正されているのは、主に t.Errorf
および t.Fatalf
の呼び出しにおける書式指定文字列の誤りです。具体的には、以下のようなパターンが修正されています。
-
%d
(整数) の代わりに%v
(任意の型) を使用すべきケース:- 例:
t.Fatal("got %d want %d", b1.A, b0.A)
がt.Fatalf("got %d want %d", b1.A, b0.A)
に変更されています。これは、t.Fatal
がt.Fatalf
のエイリアスであり、go tool vet
がt.Fatal
をt.Fatalf
として認識し、書式指定子のチェックを行うためです。この特定の例では、b1.A
とb0.A
が整数型であるため、%d
は正しいです。しかし、他の箇所では、例えばint64
のような大きな整数型に対して%d
を使うと、プラットフォームによってはオーバーフローの警告が出る可能性があり、より汎用的な%v
が推奨される場合があります。このコミットでは、t.Fatal
をt.Fatalf
に変更することで、go tool vet
がより厳密にチェックできるようにしている可能性があります。 src/pkg/encoding/gob/encoder_test.go
の例:t.Fatal("got %d want %d", b1.A, b0.A)
->t.Fatalf("got %d want %d", b1.A, b0.A)
。これはt.Fatal
がt.Fatalf
のエイリアスであるため、機能的な変更はありませんが、go tool vet
がより明確に認識できるようにするための修正と考えられます。
- 例:
-
ポインタのデリファレンス忘れ:
- 例:
t.Errorf("expected
hellogot %s", x.V)
がt.Errorf("expected
hellogot %s", *x.V)
に変更されています。x.V
がポインタ型 (*string
など) であるにもかかわらず、その値を表示するためにデリファレンス (*x.V
) が行われていなかったため、%s
が期待する文字列ではなくポインタのアドレスが表示されてしまう可能性がありました。
- 例:
-
引数の順序の誤り:
- 例:
t.Errorf("%s%v is not normalized", z, msg)
がt.Errorf("%s%v is not normalized", msg, z)
に変更されています。書式指定文字列%s%v
は、最初の%s
に文字列、次の%v
に任意の値を期待します。元のコードではz
(おそらくInt
型のオブジェクト) が%s
に渡され、msg
(文字列) が%v
に渡されていました。これをmsg
(文字列) を%s
に、z
を%v
に渡すように修正することで、意図した通りのエラーメッセージが表示されるようになります。
- 例:
-
浮動小数点数に対する書式指定子の誤り:
- 例:
t.Errorf("mallocs = %d; want 0", n)
がt.Errorf("mallocs = %g; want 0", n)
に変更されています。変数n
が浮動小数点数型(float64
など)であるにもかかわらず、整数用の書式指定子%d
が使用されていました。これを浮動小数点数用の%g
に修正することで、正しい値が表示されるようになります。
- 例:
-
%q
(引用符付き文字列) の代わりに%v
(任意の型) を使用すべきケース:- 例:
t.Errorf("FormFile file = %q, want nil", f)
がt.Errorf("FormFile file = %v, want nil", f)
に変更されています。f
がファイルハンドルやインターフェースなど、文字列として直接引用符で囲むのが適切でない型である場合、%q
を使うと意図しない出力になる可能性があります。%v
は任意の型を適切に表示するため、より汎用的に使用できます。
- 例:
-
エラーオブジェクトの表示:
- 例:
t.Fatalf("unexpected server Write: n=%d, err=%d; want n=%d, err=nil", res.n, res.err, len(msg))
がt.Fatalf("unexpected server Write: n=%d, err=%v; want n=%d, err=nil", res.n, res.err, len(msg))
に変更されています。res.err
がerror
インターフェース型である場合、%d
で表示しようとすると不適切です。error
型は通常、%v
で表示することでエラーメッセージ文字列として適切にフォーマットされます。
- 例:
-
冗長な引数の削除:
- 例:
t.Fatalf("expected no output:\\n%s", want, output)
がt.Fatalf("expected no output, got:\\n%s", output)
に変更されています。want
変数が空文字列であり、かつエラーメッセージ内で使用されていないため、冗長な引数として削除されています。
- 例:
これらの修正は、go tool vet
が提供する静的解析の価値を示す良い例であり、開発者がより堅牢でデバッグしやすいコードを書くのに役立ちます。
コアとなるコードの変更箇所
以下に、変更の代表的な例をいくつか示します。
src/pkg/encoding/gob/encoder_test.go
--- a/src/pkg/encoding/gob/encoder_test.go
+++ b/src/pkg/encoding/gob/encoder_test.go
@@ -687,7 +687,7 @@ func TestChanFuncIgnored(t *testing.T) {
t.Fatal("decode:", err)
}
if b1.A != b0.A {
- t.Fatal("got %d want %d", b1.A, b0.A)
+ t.Fatalf("got %d want %d", b1.A, b0.A)
}
if b1.C != nil || b1.CP != nil || b1.F != nil || b1.FPP != nil {
t.Fatal("unexpected value for chan or func")
src/pkg/encoding/gob/gobencdec_test.go
--- a/src/pkg/encoding/gob/gobencdec_test.go
+++ b/src/pkg/encoding/gob/gobencdec_test.go
@@ -428,7 +428,7 @@ func TestGobEncoderValueEncoder(t *testing.T) {
t.Fatal("decode error:", err)
}
if *x.V != "hello" || *x.BV != "Καλημέρα" || *x.TV != "こんにちは" {
- t.Errorf("expected `hello` got %s", x.V)
+ t.Errorf("expected `hello` got %s", *x.V)
}
}
src/pkg/math/big/int_test.go
--- a/src/pkg/math/big/int_test.go
+++ b/src/pkg/math/big/int_test.go
@@ -89,7 +89,7 @@ func testFunZZ(t *testing.T, msg string, f funZZ, a argZZ) {
var z Int
f(&z, a.x, a.y)
if !isNormalized(&z) {
- t.Errorf("%s%v is not normalized", z, msg)
+ t.Errorf("%s%v is not normalized", msg, z)
}
if (&z).Cmp(a.z) != 0 {
t.Errorf("%s%+v\n\tgot z = %v; want %v", msg, a, &z, a.z)
src/pkg/net/http/header_test.go
--- a/src/pkg/net/http/header_test.go
+++ b/src/pkg/net/http/header_test.go
@@ -204,6 +204,6 @@ func TestHeaderWriteSubsetMallocs(t *testing.T) {
testHeader.WriteSubset(&buf, nil)
})
if n > 0 {
- t.Errorf("mallocs = %d; want 0", n)
+ t.Errorf("mallocs = %g; want 0", n)
}
}
コアとなるコードの解説
上記の変更は、go tool vet
が検出した書式指定文字列と引数の不一致を修正しています。
-
t.Fatal
からt.Fatalf
への変更:t.Fatal
はt.Fatalf
のエイリアスであり、機能的な違いはありません。この変更は、go tool vet
がt.Fatalf
をより明示的に認識し、書式指定子のチェックを厳密に行うための慣用的な修正と考えられます。
-
x.V
から*x.V
への変更:x.V
がポインタ型 (*string
など) である場合、%s
書式指定子でその指す文字列を表示するには、デリファレンス演算子*
を使って*x.V
とする必要があります。元のコードではポインタのアドレスが表示される可能性がありましたが、修正後は期待通りに文字列の内容が表示されます。
-
引数の順序の修正:
t.Errorf("%s%v is not normalized", z, msg)
からt.Errorf("%s%v is not normalized", msg, z)
への変更は、書式指定文字列%s%v
に対応する引数の順序を修正しています。%s
は文字列を、%v
は任意の値を期待するため、msg
(文字列) を%s
に、z
(任意の型) を%v
に渡すことで、正しいエラーメッセージが生成されます。
-
%d
から%g
への変更:t.Errorf("mallocs = %d; want 0", n)
からt.Errorf("mallocs = %g; want 0", n)
への変更は、変数n
が浮動小数点数型であるにもかかわらず、整数用の%d
が使われていた問題を修正しています。浮動小数点数には%g
(または%f
,%e
など) を使うのが適切です。
これらの修正により、テストが失敗した際に表示されるエラーメッセージがより正確になり、デバッグの効率が向上します。
関連リンク
- Go言語の
fmt
パッケージ: https://pkg.go.dev/fmt - Go言語の
testing
パッケージ: https://pkg.go.dev/testing go tool vet
のドキュメント (Goコマンドのドキュメント内): https://pkg.go.dev/cmd/go#hdr-Run_go_tool_vet
参考にした情報源リンク
- GitHub上のコミットページ: https://github.com/golang/go/commit/fa7791e92228326d947ff65195bdc11a0a4852ef
- Go言語の公式ドキュメント (特に
fmt
およびtesting
パッケージ、go tool vet
に関する記述)