Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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.Errorft.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.Errorft.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.Errorft.Fatalf

*testing.T オブジェクトが提供する主なエラー報告メソッドには、ErrorfFatalf があります。

  • 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 の呼び出しにおける書式指定文字列の誤りです。具体的には、以下のようなパターンが修正されています。

  1. %d (整数) の代わりに %v (任意の型) を使用すべきケース:

    • 例: t.Fatal("got %d want %d", b1.A, b0.A)t.Fatalf("got %d want %d", b1.A, b0.A) に変更されています。これは、t.Fatalt.Fatalf のエイリアスであり、go tool vett.Fatalt.Fatalf として認識し、書式指定子のチェックを行うためです。この特定の例では、b1.Ab0.A が整数型であるため、%d は正しいです。しかし、他の箇所では、例えば int64 のような大きな整数型に対して %d を使うと、プラットフォームによってはオーバーフローの警告が出る可能性があり、より汎用的な %v が推奨される場合があります。このコミットでは、t.Fatalt.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.Fatalt.Fatalf のエイリアスであるため、機能的な変更はありませんが、go tool vet がより明確に認識できるようにするための修正と考えられます。
  2. ポインタのデリファレンス忘れ:

    • 例: t.Errorf("expected hello got %s", x.V)t.Errorf("expected hello got %s", *x.V) に変更されています。x.V がポインタ型 (*string など) であるにもかかわらず、その値を表示するためにデリファレンス (*x.V) が行われていなかったため、%s が期待する文字列ではなくポインタのアドレスが表示されてしまう可能性がありました。
  3. 引数の順序の誤り:

    • 例: 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 に渡すように修正することで、意図した通りのエラーメッセージが表示されるようになります。
  4. 浮動小数点数に対する書式指定子の誤り:

    • 例: t.Errorf("mallocs = %d; want 0", n)t.Errorf("mallocs = %g; want 0", n) に変更されています。変数 n が浮動小数点数型(float64 など)であるにもかかわらず、整数用の書式指定子 %d が使用されていました。これを浮動小数点数用の %g に修正することで、正しい値が表示されるようになります。
  5. %q (引用符付き文字列) の代わりに %v (任意の型) を使用すべきケース:

    • 例: t.Errorf("FormFile file = %q, want nil", f)t.Errorf("FormFile file = %v, want nil", f) に変更されています。f がファイルハンドルやインターフェースなど、文字列として直接引用符で囲むのが適切でない型である場合、%q を使うと意図しない出力になる可能性があります。%v は任意の型を適切に表示するため、より汎用的に使用できます。
  6. エラーオブジェクトの表示:

    • 例: 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.errerror インターフェース型である場合、%d で表示しようとすると不適切です。error 型は通常、%v で表示することでエラーメッセージ文字列として適切にフォーマットされます。
  7. 冗長な引数の削除:

    • 例: 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 が検出した書式指定文字列と引数の不一致を修正しています。

  1. t.Fatal から t.Fatalf への変更:

    • t.Fatalt.Fatalf のエイリアスであり、機能的な違いはありません。この変更は、go tool vett.Fatalf をより明示的に認識し、書式指定子のチェックを厳密に行うための慣用的な修正と考えられます。
  2. x.V から *x.V への変更:

    • x.V がポインタ型 (*string など) である場合、%s 書式指定子でその指す文字列を表示するには、デリファレンス演算子 * を使って *x.V とする必要があります。元のコードではポインタのアドレスが表示される可能性がありましたが、修正後は期待通りに文字列の内容が表示されます。
  3. 引数の順序の修正:

    • 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 に渡すことで、正しいエラーメッセージが生成されます。
  4. %d から %g への変更:

    • t.Errorf("mallocs = %d; want 0", n) から t.Errorf("mallocs = %g; want 0", n) への変更は、変数 n が浮動小数点数型であるにもかかわらず、整数用の %d が使われていた問題を修正しています。浮動小数点数には %g (または %f, %e など) を使うのが適切です。

これらの修正により、テストが失敗した際に表示されるエラーメッセージがより正確になり、デバッグの効率が向上します。

関連リンク

参考にした情報源リンク