[インデックス 17808] ファイルの概要
このコミットは、Go言語の標準ライブラリである database/sql
パッケージ内のテストコードにおける書式指定子(fmt
verbs)の誤りを修正するものです。具体的には、t.Errorf
関数内で使用されている %s
が %T
に変更されています。
コミット
commit e5babeff8a2096a5404aed68a1e26b9cba317ae3
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Wed Oct 16 16:30:39 2013 -0700
database/sql: fix some test fmt verbs
Found by vet.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/14762044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e5babeff8a2096a5404aed68a1e26b9cba317ae3
元コミット内容
database/sql: fix some test fmt verbs
このコミットは、database/sql
パッケージ内のテストコードにおいて、fmt
パッケージの書式指定子(format verbs)が誤って使用されている箇所を修正します。この問題は vet
ツールによって発見されました。
変更の背景
Go言語には、コードの潜在的なバグや疑わしい構造を検出するための静的解析ツール vet
が存在します。この vet
ツールが、database/sql
パッケージのテストコード内で fmt.Errorf
や t.Errorf
のような書式設定関数が呼び出される際に、引数と書式指定子が一致していない箇所を検出しました。
具体的には、エラーメッセージの書式設定において、変数の「値」を表示するために %s
(文字列) を使用すべき箇所で、変数の「型」を表示するために %T
(型) を使用すべきでした。この不一致は、テストが失敗した際に表示されるエラーメッセージが期待通りにならない原因となります。vet
ツールはこのような一般的なプログラミングミスを早期に発見し、開発者が修正できるように支援します。
前提知識の解説
Go言語の fmt
パッケージと書式指定子
Go言語の fmt
パッケージは、C言語の printf
ファミリー関数に似た書式設定機能を提供します。文字列、数値、ブール値、構造体など、さまざまな型の値を整形して出力するために使用されます。
主要な書式指定子には以下のようなものがあります。
%v
: 値をデフォルトの書式で出力します。%T
: 値の型を出力します。%s
: 文字列を出力します。%d
: 10進数を出力します。%q
: Goの構文で引用符で囲まれた文字列を出力します。
このコミットでは、%s
と %T
の使い分けが問題となっています。%s
は string
型の値を期待しますが、%T
は任意の型の引数を受け取り、その引数の型名を文字列として出力します。
Go言語の testing
パッケージと t.Errorf
Go言語の標準ライブラリには、ユニットテストを記述するための testing
パッケージが含まれています。テスト関数は func TestXxx(t *testing.T)
の形式で定義されます。
*testing.T
型のインスタンス t
は、テストの実行中に様々な操作を行うためのメソッドを提供します。その中でも t.Errorf
は、テストが失敗した際にエラーメッセージを出力し、テストを失敗としてマークするために使用されます。t.Errorf
は fmt.Errorf
と同様に書式設定文字列と可変個引数を受け取ります。
Go言語の vet
ツール
vet
はGo言語の標準的な静的解析ツールで、Goのソースコードを検査し、疑わしい構造や潜在的なバグを報告します。vet
はコンパイルエラーにはならないが、実行時に問題を引き起こす可能性のあるコードパターンを検出するのに役立ちます。
vet
が検出する一般的な問題には以下のようなものがあります。
fmt
パッケージの書式指定子と引数の不一致- 構造体のタグの誤り
- ロックの誤用
- 到達不能なコード
このコミットのケースでは、vet
が t.Errorf
の書式指定子と引数の不一致を検出しました。
技術的詳細
このコミットの技術的な詳細は、Go言語の fmt
パッケージの書式設定ルールと、vet
ツールがどのようにそれらのルールを適用して問題を検出するかに関連しています。
元のコードでは、t.Errorf
の書式文字列内で %s
が使用されていました。例えば、%s(%T(%v))
のような形式です。ここで、%s
は tt.c
という変数を表示するために使われていました。しかし、tt.c
は converter
インターフェースのインスタンスであり、その型は string
ではありません。
vet
ツールは、fmt
パッケージの書式設定関数(Errorf
, Printf
など)の呼び出しを解析し、書式指定子と対応する引数の型が一致しているかをチェックします。%s
は string
型の引数を期待しますが、tt.c
は converter
インターフェース型であるため、vet
はこの不一致を警告として報告します。
この修正では、%s
を %T
に変更しています。%T
は引数の型名を出力する書式指定子であり、tt.c
の型を表示するのに適しています。これにより、テストが失敗した際のエラーメッセージが、期待通りに tt.c
の型情報を表示するようになります。
この変更は、コードのロジック自体には影響を与えませんが、デバッグ時のエラーメッセージの正確性と有用性を向上させます。vet
のような静的解析ツールは、このような細かなミスを自動的に検出し、開発者がより堅牢なコードを書くのを助けます。
コアとなるコードの変更箇所
変更は2つのファイルにわたっています。
src/pkg/database/sql/convert_test.go
src/pkg/database/sql/driver/types_test.go
それぞれのファイルで、t.Errorf
の呼び出しにおける書式指定子が修正されています。
src/pkg/database/sql/convert_test.go
の変更点:
--- a/src/pkg/database/sql/convert_test.go
+++ b/src/pkg/database/sql/convert_test.go
@@ -267,14 +267,14 @@ func TestValueConverters(t *testing.T) {
goterr = err.Error()
}
if goterr != tt.err {
- t.Errorf("test %d: %s(%T(%v)) error = %q; want error = %q",
+ t.Errorf("test %d: %T(%T(%v)) error = %q; want error = %q",
i, tt.c, tt.in, tt.in, goterr, tt.err)
}
if tt.err != "" {
continue
}
if !reflect.DeepEqual(out, tt.out) {
- t.Errorf("test %d: %s(%T(%v)) = %v (%T); want %v (%T)",
+ t.Errorf("test %d: %T(%T(%v)) = %v (%T); want %v (%T)",
i, tt.c, tt.in, tt.in, out, out, tt.out, tt.out)
}
}
src/pkg/database/sql/driver/types_test.go
の変更点:
--- a/src/pkg/database/sql/driver/types_test.go
+++ b/src/pkg/database/sql/driver/types_test.go
@@ -51,14 +51,14 @@ func TestValueConverters(t *testing.T) {
goterr = err.Error()
}
if goterr != tt.err {
- t.Errorf("test %d: %s(%T(%v)) error = %q; want error = %q",
+ t.Errorf("test %d: %T(%T(%v)) error = %q; want error = %q",
i, tt.c, tt.in, tt.in, goterr, tt.err)
}
if tt.err != "" {
continue
}
if !reflect.DeepEqual(out, tt.out) {
- t.Errorf("test %d: %s(%T(%v)) = %v (%T); want %v (%T)",
+ t.Errorf("test %d: %T(%T(%v)) = %v (%T); want %v (%T)",
i, tt.c, tt.in, tt.in, out, out, tt.out, tt.out)
}
}
コアとなるコードの解説
両方のファイルで、t.Errorf
の書式文字列が変更されています。
元のコード:
t.Errorf("test %d: %s(%T(%v)) error = %q; want error = %q", i, tt.c, tt.in, tt.in, goterr, tt.err)
t.Errorf("test %d: %s(%T(%v)) = %v (%T); want %v (%T)", i, tt.c, tt.in, tt.in, out, out, tt.out, tt.out)
修正後のコード:
t.Errorf("test %d: %T(%T(%v)) error = %q; want error = %q", i, tt.c, tt.in, tt.in, goterr, tt.err)
t.Errorf("test %d: %T(%T(%v)) = %v (%T); want %v (%T)", i, tt.c, tt.in, tt.in, out, out, tt.out, tt.out)
変更点は、書式文字列の最初の %s
が %T
に置き換えられている点です。
%s
(変更前): これはtt.c
の「値」を文字列として表示しようとします。しかし、tt.c
はconverter
インターフェース型であり、直接文字列に変換されることを意図していません。fmt
パッケージは、String() string
メソッドを持つ型であればそのメソッドを呼び出して文字列化しますが、そうでない場合はデフォルトの書式(通常は%v
と同じ)で表示しようとします。vet
はこの潜在的な不一致を警告します。%T
(変更後): これはtt.c
の「型」を文字列として表示します。これにより、エラーメッセージはtt.c
の具体的な型情報(例:*sql.NullStringConverter
など)を含むようになり、デバッグ時にどのconverter
が問題を引き起こしているかをより明確に特定できるようになります。
この修正により、vet
の警告が解消され、テスト失敗時のエラーメッセージがより正確で有用な情報を提供するようになります。
関連リンク
- Go言語の
fmt
パッケージのドキュメント: https://pkg.go.dev/fmt - Go言語の
testing
パッケージのドキュメント: https://pkg.go.dev/testing - Go言語の
vet
ツールに関するドキュメント(go vet
コマンド): https://pkg.go.dev/cmd/go#hdr-Go_vet - Go言語の
database/sql
パッケージのドキュメント: https://pkg.go.dev/database/sql
参考にした情報源リンク
- Go言語の公式ドキュメント
go vet
の一般的な使用例と検出パターンに関する情報源- Go言語のフォーマット動詞に関するチュートリアルや記事