[インデックス 15502] ファイルの概要
このコミットは、Go言語の標準ライブラリ内のテストファイルにおいて、go vet
ツールによって発見されたprintf
スタイルのフォーマット文字列の引数に関するバグを修正するものです。具体的には、fmt.Errorf
関数内で使用されているフォーマット動詞が、対応する引数の型と一致していない箇所が修正されています。これにより、実行時の予期せぬ出力や、go vet
による警告が解消され、コードの堅牢性と正確性が向上します。
コミット
commit 1bf66f081fb34893235a02b29a8eb559e17c248e
Author: Rob Pike <r@golang.org>
Date: Thu Feb 28 11:33:08 2013 -0800
all: fix a few more printf arg bugs found by go vet
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/7413045
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/1bf66f081fb34893235a02b29a8eb559e17c248e
元コミット内容
このコミットは、Go言語の標準ライブラリの以下の3つのテストファイルにおけるprintf
フォーマット文字列の誤りを修正しています。
src/pkg/math/big/rat_test.go
src/pkg/reflect/set_test.go
src/pkg/strings/strings_test.go
修正内容は、fmt.Errorf
などのフォーマット関数で使用されている動詞(例: %b
, %p
, %s
)が、対応する引数の型と合致するように変更することです。これにより、go vet
ツールが検出する警告が解消され、より正確なデバッグ出力が得られるようになります。
変更の背景
Go言語には、コードの潜在的なバグや疑わしい構造を検出するための静的解析ツールであるgo vet
が存在します。このツールは、特にfmt
パッケージのprintf
スタイルの関数呼び出しにおいて、フォーマット動詞と引数の型が一致しないといった一般的な誤りを検出する能力を持っています。
このコミットが行われた背景には、go vet
がGo標準ライブラリ内の既存のコードベースをスキャンし、いくつかのprintf
引数に関するバグ(または潜在的な問題)を特定したことがあります。これらの問題は、プログラムのクラッシュを引き起こすものではないかもしれませんが、誤った出力や、開発者が意図しない動作につながる可能性がありました。特にテストコードにおいては、正確なエラーメッセージはデバッグの効率に直結するため、これらの修正は重要です。
Rob Pike氏(Go言語の共同開発者の一人)によるこのコミットは、go vet
の検出結果に基づき、これらのフォーマットの不整合を修正し、Go言語の標準ライブラリ全体の品質と堅牢性を向上させることを目的としています。
前提知識の解説
1. Go言語のfmt
パッケージとprintf
スタイルフォーマット
Go言語のfmt
パッケージは、フォーマットされたI/O(入出力)を実装するための機能を提供します。C言語のprintf
関数に似た機能を提供し、様々なデータ型を文字列として整形して出力することができます。
fmt
パッケージの主な関数には以下のようなものがあります。
fmt.Printf
: 標準出力にフォーマットされた文字列を出力します。fmt.Sprintf
: フォーマットされた文字列を返します。fmt.Errorf
: フォーマットされたエラー文字列を返します。
これらの関数は、フォーマット文字列(例: "Value: %d"
)と、その後に続く引数を取ります。フォーマット文字列内の%
で始まる部分は「フォーマット動詞」と呼ばれ、対応する引数をどのように整形するかを指示します。
主要なフォーマット動詞の例:
%v
: 引数のデフォルトのフォーマット。任意の型に対応。%T
: 引数の型を出力。%t
: ブール値をtrue
またはfalse
として出力。%b
: 整数を2進数で出力。%d
: 整数を10進数で出力。%x
,%X
: 整数を16進数で出力。%f
,%F
,%e
,%E
,%g
,%G
: 浮動小数点数を様々な形式で出力。%s
: 文字列を出力。%q
: 文字列を二重引用符で囲み、Go構文でエスケープされた形式で出力。%p
: ポインタの値を16進数で出力(0x
プレフィックス付き)。%#v
: Go構文で表現可能な形式で値を出力。構造体などの中身も表示される。
フォーマット動詞と引数の型が一致しない場合、コンパイルエラーにはならないことが多いですが、実行時に予期せぬ出力(例: %!t(bool=true)
のようなエラーメッセージ)や、場合によってはパニックを引き起こす可能性があります。
2. go vet
ツール
go vet
は、Go言語のソースコードを静的に解析し、潜在的なバグや疑わしい構造を報告するツールです。GoのSDKに標準で含まれており、go vet ./...
のように実行することで、現在のモジュール内のすべてのパッケージを検査できます。
go vet
が検出する一般的な問題には以下のようなものがあります。
printf
フォーマット文字列の誤り: 本コミットの主題。フォーマット動詞と引数の型の不一致。- 構造体タグの誤り:
json:"field_name"
のような構造体タグの構文エラー。 - ロックの誤用:
sync.Mutex
などのミューテックスのロック/アンロックの不整合。 - 到達不能なコード:
return
文の後に続くコードなど、決して実行されないコード。 - 未使用の変数やインポート: コンパイラが警告するものの、
go vet
がより詳細なコンテキストで報告する場合がある。
go vet
は、コンパイラが検出できないが、実行時に問題を引き起こす可能性のある「疑わしい」コードパターンを特定するのに非常に役立ちます。CI/CDパイプラインにgo vet
を組み込むことで、コード品質を自動的に維持し、早期にバグを発見することができます。
技術的詳細
このコミットの技術的詳細は、Go言語のfmt
パッケージにおけるフォーマット動詞の厳密な適用と、go vet
による静的解析の有効性を示しています。
1. src/pkg/math/big/rat_test.go
の修正
元のコード:
t.Errorf("Rat.SetString(%q).Float64().exact = %b, want %b", input, exact, wasExact)
修正後:
t.Errorf("Rat.SetString(%q).Float64().exact = %t, want %t", input, exact, wasExact)
ここでは、exact
とwasExact
というブール型の変数を表示するために、元のコードでは%b
(2進数)が使用されていました。しかし、ブール値をtrue
またはfalse
として表示する正しいフォーマット動詞は%t
です。%b
は整数を2進数で表示するために使用されます。go vet
は、ブール型に対して%b
が使用されていることを検出し、警告を発しました。%t
への変更により、出力がより意図通りで読みやすくなります。
2. src/pkg/reflect/set_test.go
の修正
元のコード(複数箇所で同様の修正):
t.Errorf("#5 MapIndex(b1) = %p want %p", p, b2)
修正後:
t.Errorf("#5 MapIndex(b1) = %#x want %p", p, b2)
この修正は、ポインタの表示に関するものです。
%p
: ポインタの値を16進数で表示します(例:0xc000012000
)。%#x
:0x
プレフィックス付きの16進数で表示します。これは主に整数型に対して使用されますが、uintptr
のようなポインタを数値として扱う場合にも適切です。
p
はuintptr
型(ポインタを整数として表現したもの)であり、b2
はポインタ型です。
元のコードでは両方に%p
を使用していましたが、uintptr
に対して%p
を使用すると、go vet
は警告を発する可能性があります。uintptr
はポインタのアドレスを保持する整数型であり、その値を16進数で表示するには%x
または%#x
がより適切です。特に%#x
は0x
プレフィックスを付けてくれるため、ポインタのアドレスであることが視覚的に明確になります。
この修正により、uintptr
型のp
は%#x
で、実際のポインタ型のb2
は引き続き%p
で表示され、それぞれの型に合った正確なフォーマットが適用されます。
3. src/pkg/strings/strings_test.go
の修正
元のコード:
t.Errorf("ContainsRune(%s, %s) = %v, want %v",
ct.str, ct.r, !ct.expected, ct.expected)
修正後:
t.Errorf("ContainsRune(%q, %q) = %v, want %v",
ct.str, ct.r, !ct.expected, ct.expected)
ここでは、文字列ct.str
とルーン(Unicodeコードポイント)ct.r
を表示するために、元のコードでは%s
(文字列)が使用されていました。
%s
: 文字列をそのまま表示します。%q
: 文字列を二重引用符で囲み、Go構文でエスケープされた形式で表示します。これは、文字列に特殊文字(例: 改行、タブ)が含まれている場合や、文字列の開始と終了を明確にしたい場合に非常に有用です。特にテストのエラーメッセージでは、どの文字列が問題を引き起こしたのかを正確に把握するために、%q
が推奨されます。
ct.r
はルーン型(rune
はint32
のエイリアス)ですが、fmt
パッケージはrune
型を%q
でフォーマットすると、単一引用符で囲まれた文字リテラルとして表示します(例: 'a'
, '\n'
)。これは、文字列や文字のデバッグ出力において、その内容をより明確に表現するために非常に有効です。
この修正は、テストのエラーメッセージの可読性と正確性を向上させ、デバッグを容易にするためのものです。
コアとなるコードの変更箇所
このコミットは、Go言語の標準ライブラリ内の以下の3つのテストファイルに影響を与えています。
-
src/pkg/math/big/rat_test.go
TestFloat64SpecialCases
関数内のt.Errorf
呼び出しで、ブール値のフォーマット動詞を%b
から%t
に変更。
--- a/src/pkg/math/big/rat_test.go +++ b/src/pkg/math/big/rat_test.go @@ -753,7 +753,7 @@ func TestFloat64SpecialCases(t *testing.T) { // 4. Check exactness using slow algorithm. if wasExact := new(Rat).SetFloat64(f).Cmp(r) == 0; wasExact != exact { - t.Errorf("Rat.SetString(%q).Float64().exact = %b, want %b", input, exact, wasExact) + t.Errorf("Rat.SetString(%q).Float64().exact = %t, want %t", input, exact, wasExact) } } }
-
src/pkg/reflect/set_test.go
TestImplicitMapConversion
関数内の複数のt.Errorf
呼び出しで、uintptr
型のポインタ値を表示するフォーマット動詞を%p
から%#x
に変更。
--- a/src/pkg/reflect/set_test.go +++ b/src/pkg/reflect/set_test.go @@ -81,7 +81,7 @@ func TestImplicitMapConversion(t *testing.T) { if t.Failed() { t.Errorf("#5 after SetMapIndex(b1, b2): %p (!= %p), %t (map=%v)", x, b2, ok, m) } - if p := mv.MapIndex(ValueOf(b1)).Elem().Pointer(); p != uintptr(unsafe.Pointer(b2)) { - t.Errorf("#5 MapIndex(b1) = %p want %p", p, b2) + if p := mv.MapIndex(ValueOf(b1)).Elem().Pointer(); p != uintptr(unsafe.Pointer(b2)) { + t.Errorf("#5 MapIndex(b1) = %#x want %p", p, b2) } } { @@ -96,7 +96,7 @@ func TestImplicitMapConversion(t *testing.T) { if t.Failed() { t.Errorf("#6 after SetMapIndex(c1, c2): %p (!= %p), %t (map=%v)", x, c2, ok, m) } - if p := mv.MapIndex(ValueOf(c1)).Pointer(); p != ValueOf(c2).Pointer() { - t.Errorf("#6 MapIndex(c1) = %p want %p", p, c2) + if p := mv.MapIndex(ValueOf(c1)).Pointer(); p != ValueOf(c2).Pointer() { + t.Errorf("#6 MapIndex(c1) = %#x want %p", p, c2) } } { @@ -115,7 +115,7 @@ func TestImplicitMapConversion(t *testing.T) { if t.Failed() { t.Errorf("#7 after SetMapIndex(b1, b2): %p (!= %p), %t (map=%v)", x, b2, ok, m) } - if p := mv.MapIndex(ValueOf(b1)).Pointer(); p != uintptr(unsafe.Pointer(b2)) { - t.Errorf("#7 MapIndex(b1) = %p want %p", p, b2) + if p := mv.MapIndex(ValueOf(b1)).Pointer(); p != uintptr(unsafe.Pointer(b2)) { + t.Errorf("#7 MapIndex(b1) = %#x want %p", p, b2) } }
-
src/pkg/strings/strings_test.go
TestContainsRune
関数内のt.Errorf
呼び出しで、文字列とルーンのフォーマット動詞を%s
から%q
に変更。
--- a/src/pkg/strings/strings_test.go +++ b/src/pkg/strings/strings_test.go @@ -967,7 +967,7 @@ var ContainsRuneTests = []struct { func TestContainsRune(t *testing.T) { for _, ct := range ContainsRuneTests { if ContainsRune(ct.str, ct.r) != ct.expected { - t.Errorf("ContainsRune(%s, %s) = %v, want %v", + t.Errorf("ContainsRune(%q, %q) = %v, want %v", ct.str, ct.r, !ct.expected, ct.expected) } }
コアとなるコードの解説
このコミットのコアとなる変更は、Go言語のfmt
パッケージにおけるフォーマット動詞の適切な使用に集約されます。各変更は、go vet
が検出した特定の型とフォーマット動詞の不一致を解消し、より正確で意図通りのデバッグ出力を生成することを目的としています。
-
src/pkg/math/big/rat_test.go
:- 変更前:
t.Errorf("... = %b, want %b", ..., exact, wasExact)
- 変更後:
t.Errorf("... = %t, want %t", ..., exact, wasExact)
exact
とwasExact
はブール型(bool
)です。%b
は整数を2進数でフォーマットするための動詞であり、ブール値には不適切です。%t
はブール値をtrue
またはfalse
としてフォーマットするための正しい動詞です。この修正により、エラーメッセージがより明確で、誤解の余地がなくなります。
- 変更前:
-
src/pkg/reflect/set_test.go
:- 変更前:
t.Errorf("... = %p want %p", p, b2)
- 変更後:
t.Errorf("... = %#x want %p", p, b2)
- ここで
p
はuintptr
型(ポインタのアドレスを保持する整数型)であり、b2
は実際のポインタ型です。 %p
はポインタ型に対して使用されるのが一般的です。uintptr
は整数型であるため、%p
を使用するとgo vet
が警告を出すことがあります。%#x
は、0x
プレフィックス付きの16進数で整数をフォーマットします。uintptr
のようなアドレス値を表示する際には、これがより適切で、かつgo vet
の警告を回避できます。b2
は引き続きポインタ型であるため、%p
が適切に維持されています。この変更は、型のセマンティクスに合わせたフォーマットの厳密な適用を示しています。
- 変更前:
-
src/pkg/strings/strings_test.go
:- 変更前:
t.Errorf("ContainsRune(%s, %s) = %v, want %v", ct.str, ct.r, ...)
- 変更後:
t.Errorf("ContainsRune(%q, %q) = %v, want %v", ct.str, ct.r, ...)
ct.str
は文字列型、ct.r
はルーン型(rune
はint32
のエイリアス)です。%s
は文字列をそのまま出力しますが、文字列に空白や特殊文字が含まれる場合、どこからどこまでが文字列なのかが分かりにくくなることがあります。%q
は、文字列を二重引用符で囲み、Go構文でエスケープされた形式で出力します。これにより、文字列の内容が明確になり、特にデバッグ時に文字列の境界や特殊文字の有無を正確に把握できます。ルーン型に対して%q
を使用すると、単一引用符で囲まれた文字リテラルとして表示され、これもまたデバッグの助けとなります。
- 変更前:
これらの修正は、Go言語のfmt
パッケージのベストプラクティスに従い、go vet
のような静的解析ツールを活用してコード品質を向上させる典型的な例です。テストコードにおける正確なエラーメッセージは、将来のデバッグ作業の効率に大きく貢献します。
関連リンク
- Go言語
fmt
パッケージのドキュメント: https://pkg.go.dev/fmt go vet
コマンドのドキュメント: https://pkg.go.dev/cmd/vet- Go Code Review Comments -
Printf
format strings: https://go.dev/doc/effective_go#printf
参考にした情報源リンク
- 上記のGo言語公式ドキュメント
go vet
に関する一般的な情報源(Goコミュニティのブログや記事など)- Go言語の
fmt
パッケージのフォーマット動詞に関する一般的な知識 - コミットメッセージと差分から読み取れる情報
[インデックス 15502] ファイルの概要
このコミットは、Go言語の標準ライブラリ内のテストファイルにおいて、go vet
ツールによって発見されたprintf
スタイルのフォーマット文字列の引数に関するバグを修正するものです。具体的には、fmt.Errorf
関数内で使用されているフォーマット動詞が、対応する引数の型と一致していない箇所が修正されています。これにより、実行時の予期せぬ出力や、go vet
による警告が解消され、コードの堅牢性と正確性が向上します。
コミット
commit 1bf66f081fb34893235a02b29a8eb559e17c248e
Author: Rob Pike <r@golang.org>
Date: Thu Feb 28 11:33:08 2013 -0800
all: fix a few more printf arg bugs found by go vet
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/7413045
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/1bf66f081fb34893235a02b29a8eb559e17c248e
元コミット内容
このコミットは、Go言語の標準ライブラリの以下の3つのテストファイルにおけるprintf
フォーマット文字列の誤りを修正しています。
src/pkg/math/big/rat_test.go
src/pkg/reflect/set_test.go
src/pkg/strings/strings_test.go
修正内容は、fmt.Errorf
などのフォーマット関数で使用されている動詞(例: %b
, %p
, %s
)が、対応する引数の型と合致するように変更することです。これにより、go vet
ツールが検出する警告が解消され、より正確なデバッグ出力が得られるようになります。
変更の背景
Go言語には、コードの潜在的なバグや疑わしい構造を検出するための静的解析ツールであるgo vet
が存在します。このツールは、特にfmt
パッケージのprintf
スタイルの関数呼び出しにおいて、フォーマット動詞と引数の型が一致しないといった一般的な誤りを検出する能力を持っています。
このコミットが行われた背景には、go vet
がGo標準ライブラリ内の既存のコードベースをスキャンし、いくつかのprintf
引数に関するバグ(または潜在的な問題)を特定したことがあります。これらの問題は、プログラムのクラッシュを引き起こすものではないかもしれませんが、誤った出力や、開発者が意図しない動作につながる可能性がありました。特にテストコードにおいては、正確なエラーメッセージはデバッグの効率に直結するため、これらの修正は重要です。
Rob Pike氏(Go言語の共同開発者の一人)によるこのコミットは、go vet
の検出結果に基づき、これらのフォーマットの不整合を修正し、Go言語の標準ライブラリ全体の品質と堅牢性を向上させることを目的としています。
前提知識の解説
1. Go言語のfmt
パッケージとprintf
スタイルフォーマット
Go言語のfmt
パッケージは、フォーマットされたI/O(入出力)を実装するための機能を提供します。C言語のprintf
関数に似た機能を提供し、様々なデータ型を文字列として整形して出力することができます。
fmt
パッケージの主な関数には以下のようなものがあります。
fmt.Printf
: 標準出力にフォーマットされた文字列を出力します。fmt.Sprintf
: フォーマットされた文字列を返します。fmt.Errorf
: フォーマットされたエラー文字列を返します。
これらの関数は、フォーマット文字列(例: "Value: %d"
)と、その後に続く引数を取ります。フォーマット文字列内の%
で始まる部分は「フォーマット動詞」と呼ばれ、対応する引数をどのように整形するかを指示します。
主要なフォーマット動詞の例:
%v
: 引数のデフォルトのフォーマット。任意の型に対応。%T
: 引数の型を出力。%t
: ブール値をtrue
またはfalse
として出力。%b
: 整数を2進数で出力。%d
: 整数を10進数で出力。%x
,%X
: 整数を16進数で出力。%f
,%F
,%e
,%E
,%g
,%G
: 浮動小数点数を様々な形式で出力。%s
: 文字列を出力。%q
: 文字列を二重引用符で囲み、Go構文でエスケープされた形式で出力。%p
: ポインタの値を16進数で出力(0x
プレフィックス付き)。%#v
: Go構文で表現可能な形式で値を出力。構造体などの中身も表示される。
フォーマット動詞と引数の型が一致しない場合、コンパイルエラーにはならないことが多いですが、実行時に予期せぬ出力(例: %!t(bool=true)
のようなエラーメッセージ)や、場合によってはパニックを引き起こす可能性があります。
2. go vet
ツール
go vet
は、Go言語のソースコードを静的に解析し、潜在的なバグや疑わしい構造を報告するツールです。GoのSDKに標準で含まれており、go vet ./...
のように実行することで、現在のモジュール内のすべてのパッケージを検査できます。
go vet
が検出する一般的な問題には以下のようなものがあります。
printf
フォーマット文字列の誤り: 本コミットの主題。フォーマット動詞と引数の型の不一致。- 構造体タグの誤り:
json:"field_name"
のような構造体タグの構文エラー。 - ロックの誤用:
sync.Mutex
などのミューテックスのロック/アンロックの不整合。 - 到達不能なコード:
return
文の後に続くコードなど、決して実行されないコード。 - 未使用の変数やインポート: コンパイラが警告するものの、
go vet
がより詳細なコンテキストで報告する場合がある。
go vet
は、コンパイラが検出できないが、実行時に問題を引き起こす可能性のある「疑わしい」コードパターンを特定するのに非常に役立ちます。CI/CDパイプラインにgo vet
を組み込むことで、コード品質を自動的に維持し、早期にバグを発見することができます。
技術的詳細
このコミットの技術的詳細は、Go言語のfmt
パッケージにおけるフォーマット動詞の厳密な適用と、go vet
による静的解析の有効性を示しています。
1. src/pkg/math/big/rat_test.go
の修正
元のコード:
t.Errorf("Rat.SetString(%q).Float64().exact = %b, want %b", input, exact, wasExact)
修正後:
t.Errorf("Rat.SetString(%q).Float64().exact = %t, want %t", input, exact, wasExact)
ここでは、exact
とwasExact
というブール型の変数を表示するために、元のコードでは%b
(2進数)が使用されていました。しかし、ブール値をtrue
またはfalse
として表示する正しいフォーマット動詞は%t
です。%b
は整数を2進数で表示するために使用されます。go vet
は、ブール型に対して%b
が使用されていることを検出し、警告を発しました。%t
への変更により、出力がより意図通りで読みやすくなります。
2. src/pkg/reflect/set_test.go
の修正
元のコード(複数箇所で同様の修正):
t.Errorf("#5 MapIndex(b1) = %p want %p", p, b2)
修正後:
t.Errorf("#5 MapIndex(b1) = %#x want %p", p, b2)
この修正は、ポインタの表示に関するものです。
%p
: ポインタの値を16進数で表示します(例:0xc000012000
)。%#x
:0x
プレフィックス付きの16進数で表示します。これは主に整数型に対して使用されますが、uintptr
のようなポインタを数値として扱う場合にも適切です。
p
はuintptr
型(ポインタを整数として表現したもの)であり、b2
はポインタ型です。
元のコードでは両方に%p
を使用していましたが、uintptr
に対して%p
を使用すると、go vet
は警告を発する可能性があります。uintptr
はポインタのアドレスを保持する整数型であり、その値を16進数で表示するには%x
または%#x
がより適切です。特に%#x
は0x
プレフィックスを付けてくれるため、ポインタのアドレスであることが視覚的に明確になります。
この修正により、uintptr
型のp
は%#x
で、実際のポインタ型のb2
は引き続き%p
で表示され、それぞれの型に合った正確なフォーマットが適用されます。
3. src/pkg/strings/strings_test.go
の修正
元のコード:
t.Errorf("ContainsRune(%s, %s) = %v, want %v",
ct.str, ct.r, !ct.expected, ct.expected)
修正後:
t.Errorf("ContainsRune(%q, %q) = %v, want %v",
ct.str, ct.r, !ct.expected, ct.expected)
ここでは、文字列ct.str
とルーン(Unicodeコードポイント)ct.r
を表示するために、元のコードでは%s
(文字列)が使用されていました。
%s
: 文字列をそのまま表示します。%q
: 文字列を二重引用符で囲み、Go構文でエスケープされた形式で表示します。これは、文字列に特殊文字(例: 改行、タブ)が含まれている場合や、文字列の開始と終了を明確にしたい場合に非常に有用です。特にテストのエラーメッセージでは、どの文字列が問題を引き起こしたのかを正確に把握するために、%q
が推奨されます。
ct.r
はルーン型(rune
はint32
のエイリアス)ですが、fmt
パッケージはrune
型を%q
でフォーマットすると、単一引用符で囲まれた文字リテラルとして表示します(例: 'a'
, '\n'
)。これは、文字列や文字のデバッグ出力において、その内容をより明確に表現するために非常に有効です。
この修正は、テストのエラーメッセージの可読性と正確性を向上させ、デバッグを容易にするためのものです。
コアとなるコードの変更箇所
このコミットは、Go言語の標準ライブラリ内の以下の3つのテストファイルに影響を与えています。
-
src/pkg/math/big/rat_test.go
TestFloat64SpecialCases
関数内のt.Errorf
呼び出しで、ブール値のフォーマット動詞を%b
から%t
に変更。
--- a/src/pkg/math/big/rat_test.go +++ b/src/pkg/math/big/rat_test.go @@ -753,7 +753,7 @@ func TestFloat64SpecialCases(t *testing.T) { // 4. Check exactness using slow algorithm. if wasExact := new(Rat).SetFloat64(f).Cmp(r) == 0; wasExact != exact { - t.Errorf("Rat.SetString(%q).Float64().exact = %b, want %b", input, exact, wasExact) + t.Errorf("Rat.SetString(%q).Float64().exact = %t, want %t", input, exact, wasExact) } } }
-
src/pkg/reflect/set_test.go
TestImplicitMapConversion
関数内の複数のt.Errorf
呼び出しで、uintptr
型のポインタ値を表示するフォーマット動詞を%p
から%#x
に変更。
--- a/src/pkg/reflect/set_test.go +++ b/src/pkg/reflect/set_test.go @@ -81,7 +81,7 @@ func TestImplicitMapConversion(t *testing.T) { if t.Failed() { t.Errorf("#5 after SetMapIndex(b1, b2): %p (!= %p), %t (map=%v)", x, b2, ok, m) } - if p := mv.MapIndex(ValueOf(b1)).Elem().Pointer(); p != uintptr(unsafe.Pointer(b2)) { - t.Errorf("#5 MapIndex(b1) = %p want %p", p, b2) + if p := mv.MapIndex(ValueOf(b1)).Elem().Pointer(); p != uintptr(unsafe.Pointer(b2)) { + t.Errorf("#5 MapIndex(b1) = %#x want %p", p, b2) } } { @@ -96,7 +96,7 @@ func TestImplicitMapConversion(t *testing.T) { if t.Failed() { t.Errorf("#6 after SetMapIndex(c1, c2): %p (!= %p), %t (map=%v)", x, c2, ok, m) } - if p := mv.MapIndex(ValueOf(c1)).Pointer(); p != ValueOf(c2).Pointer() { - t.Errorf("#6 MapIndex(c1) = %p want %p", p, c2) + if p := mv.MapIndex(ValueOf(c1)).Pointer(); p != ValueOf(c2).Pointer() { + t.Errorf("#6 MapIndex(c1) = %#x want %p", p, c2) } } { @@ -115,7 +115,7 @@ func TestImplicitMapConversion(t *testing.T) { if t.Failed() { t.Errorf("#7 after SetMapIndex(b1, b2): %p (!= %p), %t (map=%v)", x, b2, ok, m) } - if p := mv.MapIndex(ValueOf(b1)).Pointer(); p != uintptr(unsafe.Pointer(b2)) { - t.Errorf("#7 MapIndex(b1) = %p want %p", p, b2) + if p := mv.MapIndex(ValueOf(b1)).Pointer(); p != uintptr(unsafe.Pointer(b2)) { + t.Errorf("#7 MapIndex(b1) = %#x want %p", p, b2) } }
-
src/pkg/strings/strings_test.go
TestContainsRune
関数内のt.Errorf
呼び出しで、文字列とルーンのフォーマット動詞を%s
から%q
に変更。
--- a/src/pkg/strings/strings_test.go +++ b/src/pkg/strings/strings_test.go @@ -967,7 +967,7 @@ var ContainsRuneTests = []struct { func TestContainsRune(t *testing.T) { for _, ct := range ContainsRuneTests { if ContainsRune(ct.str, ct.r) != ct.expected { - t.Errorf("ContainsRune(%s, %s) = %v, want %v", + t.Errorf("ContainsRune(%q, %q) = %v, want %v", ct.str, ct.r, !ct.expected, ct.expected) } }
コアとなるコードの解説
このコミットのコアとなる変更は、Go言語のfmt
パッケージにおけるフォーマット動詞の適切な使用に集約されます。各変更は、go vet
が検出した特定の型とフォーマット動詞の不一致を解消し、より正確で意図通りのデバッグ出力を生成することを目的としています。
-
src/pkg/math/big/rat_test.go
:- 変更前:
t.Errorf("... = %b, want %b", ..., exact, wasExact)
- 変更後:
t.Errorf("... = %t, want %t", ..., exact, wasExact)
exact
とwasExact
はブール型(bool
)です。%b
は整数を2進数でフォーマットするための動詞であり、ブール値には不適切です。%t
はブール値をtrue
またはfalse
としてフォーマットするための正しい動詞です。この修正により、エラーメッセージがより明確で、誤解の余地がなくなります。
- 変更前:
-
src/pkg/reflect/set_test.go
:- 変更前:
t.Errorf("... = %p want %p", p, b2)
- 変更後:
t.Errorf("... = %#x want %p", p, b2)
- ここで
p
はuintptr
型(ポインタのアドレスを保持する整数型)であり、b2
は実際のポインタ型です。 %p
はポインタ型に対して使用されるのが一般的です。uintptr
は整数型であるため、%p
を使用するとgo vet
が警告を出すことがあります。%#x
は、0x
プレフィックス付きの16進数で整数をフォーマットします。uintptr
のようなアドレス値を表示する際には、これがより適切で、かつgo vet
の警告を回避できます。b2
は引き続きポインタ型であるため、%p
が適切に維持されています。この変更は、型のセマンティクスに合わせたフォーマットの厳密な適用を示しています。
- 変更前:
-
src/pkg/strings/strings_test.go
:- 変更前:
t.Errorf("ContainsRune(%s, %s) = %v, want %v", ct.str, ct.r, ...)
- 変更後:
t.Errorf("ContainsRune(%q, %q) = %v, want %v", ct.str, ct.r, ...)
ct.str
は文字列型、ct.r
はルーン型(rune
はint32
のエイリアス)です。%s
は文字列をそのまま出力しますが、文字列に空白や特殊文字が含まれる場合、どこからどこまでが文字列なのかが分かりにくくなることがあります。%q
は、文字列を二重引用符で囲み、Go構文でエスケープされた形式で出力します。これにより、文字列の内容が明確になり、特にデバッグ時に文字列の境界や特殊文字の有無を正確に把握できます。ルーン型に対して%q
を使用すると、単一引用符で囲まれた文字リテラルとして表示され、これもまたデバッグの助けとなります。
- 変更前:
これらの修正は、Go言語のfmt
パッケージのベストプラクティスに従い、go vet
のような静的解析ツールを活用してコード品質を向上させる典型的な例です。テストコードにおける正確なエラーメッセージは、将来のデバッグ作業の効率に大きく貢献します。
関連リンク
- Go言語
fmt
パッケージのドキュメント: https://pkg.go.dev/fmt go vet
コマンドのドキュメント: https://pkg.go.dev/cmd/vet- Go Code Review Comments -
Printf
format strings: https://go.dev/doc/effective_go#printf
参考にした情報源リンク
- 上記のGo言語公式ドキュメント
- コミットメッセージと差分から読み取れる情報