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

[インデックス 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.Errorft.Errorf のような書式設定関数が呼び出される際に、引数と書式指定子が一致していない箇所を検出しました。

具体的には、エラーメッセージの書式設定において、変数の「値」を表示するために %s (文字列) を使用すべき箇所で、変数の「型」を表示するために %T (型) を使用すべきでした。この不一致は、テストが失敗した際に表示されるエラーメッセージが期待通りにならない原因となります。vet ツールはこのような一般的なプログラミングミスを早期に発見し、開発者が修正できるように支援します。

前提知識の解説

Go言語の fmt パッケージと書式指定子

Go言語の fmt パッケージは、C言語の printf ファミリー関数に似た書式設定機能を提供します。文字列、数値、ブール値、構造体など、さまざまな型の値を整形して出力するために使用されます。

主要な書式指定子には以下のようなものがあります。

  • %v: 値をデフォルトの書式で出力します。
  • %T: 値の型を出力します。
  • %s: 文字列を出力します。
  • %d: 10進数を出力します。
  • %q: Goの構文で引用符で囲まれた文字列を出力します。

このコミットでは、%s%T の使い分けが問題となっています。%sstring 型の値を期待しますが、%T は任意の型の引数を受け取り、その引数の型名を文字列として出力します。

Go言語の testing パッケージと t.Errorf

Go言語の標準ライブラリには、ユニットテストを記述するための testing パッケージが含まれています。テスト関数は func TestXxx(t *testing.T) の形式で定義されます。

*testing.T 型のインスタンス t は、テストの実行中に様々な操作を行うためのメソッドを提供します。その中でも t.Errorf は、テストが失敗した際にエラーメッセージを出力し、テストを失敗としてマークするために使用されます。t.Errorffmt.Errorf と同様に書式設定文字列と可変個引数を受け取ります。

Go言語の vet ツール

vet はGo言語の標準的な静的解析ツールで、Goのソースコードを検査し、疑わしい構造や潜在的なバグを報告します。vet はコンパイルエラーにはならないが、実行時に問題を引き起こす可能性のあるコードパターンを検出するのに役立ちます。

vet が検出する一般的な問題には以下のようなものがあります。

  • fmt パッケージの書式指定子と引数の不一致
  • 構造体のタグの誤り
  • ロックの誤用
  • 到達不能なコード

このコミットのケースでは、vett.Errorf の書式指定子と引数の不一致を検出しました。

技術的詳細

このコミットの技術的な詳細は、Go言語の fmt パッケージの書式設定ルールと、vet ツールがどのようにそれらのルールを適用して問題を検出するかに関連しています。

元のコードでは、t.Errorf の書式文字列内で %s が使用されていました。例えば、%s(%T(%v)) のような形式です。ここで、%stt.c という変数を表示するために使われていました。しかし、tt.cconverter インターフェースのインスタンスであり、その型は string ではありません。

vet ツールは、fmt パッケージの書式設定関数(Errorf, Printf など)の呼び出しを解析し、書式指定子と対応する引数の型が一致しているかをチェックします。%sstring 型の引数を期待しますが、tt.cconverter インターフェース型であるため、vet はこの不一致を警告として報告します。

この修正では、%s%T に変更しています。%T は引数の型名を出力する書式指定子であり、tt.c の型を表示するのに適しています。これにより、テストが失敗した際のエラーメッセージが、期待通りに tt.c の型情報を表示するようになります。

この変更は、コードのロジック自体には影響を与えませんが、デバッグ時のエラーメッセージの正確性と有用性を向上させます。vet のような静的解析ツールは、このような細かなミスを自動的に検出し、開発者がより堅牢なコードを書くのを助けます。

コアとなるコードの変更箇所

変更は2つのファイルにわたっています。

  1. src/pkg/database/sql/convert_test.go
  2. 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.cconverter インターフェース型であり、直接文字列に変換されることを意図していません。fmt パッケージは、String() string メソッドを持つ型であればそのメソッドを呼び出して文字列化しますが、そうでない場合はデフォルトの書式(通常は %v と同じ)で表示しようとします。vet はこの潜在的な不一致を警告します。
  • %T (変更後): これは tt.c の「型」を文字列として表示します。これにより、エラーメッセージは tt.c の具体的な型情報(例: *sql.NullStringConverter など)を含むようになり、デバッグ時にどの converter が問題を引き起こしているかをより明確に特定できるようになります。

この修正により、vet の警告が解消され、テスト失敗時のエラーメッセージがより正確で有用な情報を提供するようになります。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • go vet の一般的な使用例と検出パターンに関する情報源
  • Go言語のフォーマット動詞に関するチュートリアルや記事