[インデックス 15417] ファイルの概要
このコミットは、Go言語の標準ライブラリ内の複数のテストファイルにおけるprintfフォーマット文字列の誤りを修正するものです。具体的には、go vetツールによって検出された、フォーマット指定子と引数の不一致や、不適切な引数渡しといった問題を修正しています。
コミット
commit 707ab1347f114934d65b713e22fdd62b4a83ca36
Author: Rob Pike <r@golang.org>
Date: Mon Feb 25 12:43:03 2013 -0800
all: fix some vet-found printf errors
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/7393059
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/707ab1347f114934d65b713e22fdd62b4a83ca36
元コミット内容
all: fix some vet-found printf errors
変更の背景
このコミットの背景には、Go言語の静的解析ツールであるgo vetの存在があります。go vetは、Goのソースコードを検査し、潜在的なバグや疑わしい構成を報告するツールです。特に、printf系の関数(fmt.Printf, fmt.Errorf, t.Fatalfなど)におけるフォーマット文字列と引数の不一致は、実行時エラーや予期せぬ出力につながる可能性があるため、go vetが重点的にチェックする項目の一つです。
このコミットは、go vetによって検出された、いくつかのprintf関連のエラーを修正することを目的としています。これにより、コードの堅牢性が向上し、テストの出力がより正確になることが期待されます。
前提知識の解説
go vetツール
go vetは、Go言語の標準ツールチェーンに含まれる静的解析ツールです。コンパイル時には検出されないが、実行時に問題を引き起こす可能性のあるコードの疑わしい構造を特定します。主なチェック項目には以下のようなものがあります。
printfフォーマット文字列のチェック:fmt.Printfなどの関数で、フォーマット指定子(例:%s,%d,%v)と対応する引数の型が一致しているか、引数の数が正しいかなどを検証します。- 到達不能なコード: 常に実行されないコードパスを検出します。
- ロックの誤用:
sync.Mutexなどの同期プリミティブの誤った使用を検出します。 - 構造体タグの誤り:
json:"field"のような構造体タグの構文エラーを検出します。
go vetは、開発者がコードレビューやテストの前に潜在的な問題を早期に発見し、修正するのに役立ちます。
fmtパッケージとprintf系関数
Go言語のfmtパッケージは、フォーマットされたI/Oを実装するための機能を提供します。特に、Printf系の関数は、C言語のprintfに似た構文で、指定されたフォーマット文字列に従って値を整形して出力します。
fmt.Printf(format string, a ...interface{}) (n int, err error): 標準出力にフォーマットされた文字列を出力します。fmt.Errorf(format string, a ...interface{}) error: フォーマットされたエラー文字列を生成し、errorインターフェースとして返します。t.Fatalf(format string, a ...interface{}):testingパッケージのメソッドで、テストが失敗したことを報告し、テストの実行を停止します。
これらの関数では、フォーマット指定子を使用して、引数をどのように表示するかを制御します。
%s: 文字列として値を表示します。%q: Goの構文で引用符で囲まれた文字列として値を表示します。文字列リテラルをデバッグ出力する際などに便利です。%T: 値の型をGoの構文で表示します。%v: 値をデフォルトのフォーマットで表示します。構造体や配列など、任意の型を表示する際に便利です。
t.Fatalとt.Fatalfの違い
testingパッケージにおいて、t.Fatalとt.Fatalfはどちらもテストを失敗させ、そのテスト関数を即座に終了させるために使用されます。
t.Fatal(args ...interface{}): 引数をデフォルトのフォーマット(%v)で出力し、テストを失敗させます。t.Fatalf(format string, args ...interface{}): フォーマット文字列と引数を使用してメッセージを整形し、テストを失敗させます。
t.Fatalfはfmt.Sprintfと同様のフォーマット機能を持つため、より詳細で構造化されたエラーメッセージを出力するのに適しています。
技術的詳細
このコミットでは、主に以下の3種類のprintf関連の修正が行われています。
-
t.Fatalからt.Fatalfへの変更とフォーマット引数の追加:src/pkg/archive/tar/reader_test.goでは、t.Fatal("Time parsing failure %s %s", ts, expected)という行がt.Fatalf("Time parsing failure %s %s", ts, expected)に変更されています。元のコードではt.Fatalにフォーマット文字列と引数が渡されていましたが、t.Fatalはfmt.Sprintのように引数をそのまま結合して出力するため、フォーマット指定子%sは無視され、意図しない出力になる可能性がありました。t.Fatalfはfmt.Sprintfのようにフォーマット文字列を解釈するため、この変更により意図した通りのフォーマットでエラーメッセージが出力されるようになります。 -
%cから%qへのフォーマット指定子の変更:src/pkg/encoding/gob/gobencdec_test.goでは、t.Fatalf("expectedXYZgot %c", y.G.s)がt.Fatalf("expectedXYZgot %q", y.G.s)に変更されています。y.G.sが文字列型であるにもかかわらず、%c(文字)フォーマット指定子が使用されていました。これは、文字列の最初の文字のみが表示されるか、あるいは予期せぬ結果を招く可能性があります。%qはGoの構文で引用符で囲まれた文字列として値を表示するため、文字列全体が正確にデバッグ出力されるようになります。 -
fmt.Errorfへの引数追加:src/pkg/encoding/json/decode_test.goでは、return fmt.Errorf("types: failed to unmarshal non-string value %q as an RFC 3339 time")がreturn fmt.Errorf("types: failed to unmarshal non-string value %q as an RFC 3339 time", b)に変更されています。元のコードでは、フォーマット文字列内に%q指定子があるにもかかわらず、対応する引数が渡されていませんでした。これはgo vetによって検出される典型的なエラーです。引数b(バイトスライス)を追加することで、%qがbの内容を引用符で囲まれた文字列として表示し、エラーメッセージがより詳細で役立つものになります。 -
%#Tから%Tへのフォーマット指定子の変更:src/pkg/net/http/request_test.goでは、t.Errorf("ContentLength(%#T) = %d; want %d", tt.r, req.ContentLength, tt.want)がt.Errorf("ContentLength(%T) = %d; want %d", tt.r, req.ContentLength, tt.want)に変更されています。%#TはGoの構文で型を完全修飾名で表示しますが、このコンテキストでは単に型名を表示する%Tで十分であり、より簡潔な出力となります。%#Tは通常、構造体のフィールド名を含めて型を表示する際に使用されますが、ここでは単にtt.rの型を表示する目的であるため、%Tが適切です。
これらの修正は、go vetが提供する静的解析の価値を明確に示しており、開発者が実行時エラーにつながる可能性のある一般的なプログラミングミスを早期に特定し、修正するのに役立っています。
コアとなるコードの変更箇所
src/pkg/archive/tar/reader_test.go
--- a/src/pkg/archive/tar/reader_test.go
+++ b/src/pkg/archive/tar/reader_test.go
@@ -338,7 +338,7 @@ func TestParsePAXTime(t *testing.T) {
t.Fatal(err)
}
if !ts.Equal(expected) {
- t.Fatal("Time parsing failure %s %s", ts, expected)
+ t.Fatalf("Time parsing failure %s %s", ts, expected)
}
}
}
src/pkg/encoding/gob/gobencdec_test.go
--- a/src/pkg/encoding/gob/gobencdec_test.go
+++ b/src/pkg/encoding/gob/gobencdec_test.go
@@ -348,7 +348,7 @@ func TestGobEncoderFieldsOfDifferentType(t *testing.T) {
t.Fatal("decode error:", err)
}
if y.G.s != "XYZ" {
- t.Fatalf("expected `XYZ` got %c", y.G.s)
+ t.Fatalf("expected `XYZ` got %q", y.G.s)
}
}
src/pkg/encoding/json/decode_test.go
--- a/src/pkg/encoding/json/decode_test.go
+++ b/src/pkg/encoding/json/decode_test.go
@@ -1158,7 +1158,7 @@ type Time3339 time.Time
func (t *Time3339) UnmarshalJSON(b []byte) error {
if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' {
- return fmt.Errorf("types: failed to unmarshal non-string value %q as an RFC 3339 time")
+ return fmt.Errorf("types: failed to unmarshal non-string value %q as an RFC 3339 time", b)
}
tm, err := time.Parse(time.RFC3339, string(b[1:len(b)-1]))
if err != nil {
src/pkg/net/http/request_test.go
--- a/src/pkg/net/http/request_test.go
+++ b/src/pkg/net/http/request_test.go
@@ -262,7 +262,7 @@ func TestNewRequestContentLength(t *testing.T) {
t.Fatal(err)
}
if req.ContentLength != tt.want {
- t.Errorf("ContentLength(%#T) = %d; want %d", tt.r, req.ContentLength, tt.want)
+ t.Errorf("ContentLength(%T) = %d; want %d", tt.r, req.ContentLength, tt.want)
}
}
}
コアとなるコードの解説
上記の変更箇所は、すべてGoのテストファイル内のエラー報告ロジックに関連しています。
-
src/pkg/archive/tar/reader_test.go:TestParsePAXTime関数内で、時間のパースに失敗した場合のエラーメッセージ出力が修正されています。t.Fatalからt.Fatalfへの変更により、%sフォーマット指定子が正しく解釈され、tsとexpectedの値が文字列として適切に表示されるようになります。 -
src/pkg/encoding/gob/gobencdec_test.go:TestGobEncoderFieldsOfDifferentType関数内で、gobデコード後の値の検証におけるエラーメッセージ出力が修正されています。y.G.sが文字列であるにもかかわらず%c(文字)フォーマット指定子が使われていたのを、%q(引用符付き文字列)に変更することで、文字列全体が正確に表示されるようになります。 -
src/pkg/encoding/json/decode_test.go:Time3339型のUnmarshalJSONメソッド内で、JSONのアンマーシャルに失敗した場合のエラーメッセージ生成が修正されています。fmt.Errorfのフォーマット文字列に%q指定子があるにもかかわらず引数が不足していたため、引数b(アンマーシャル対象のバイトスライス)を追加することで、エラーメッセージに具体的な非文字列値が表示されるようになります。 -
src/pkg/net/http/request_test.go:TestNewRequestContentLength関数内で、ContentLengthの検証におけるエラーメッセージ出力が修正されています。%#T(完全修飾型名)から%T(型名)への変更により、エラーメッセージがより簡潔になります。このコンテキストでは、tt.rの型名のみが重要であり、完全修飾名は冗長です。
これらの変更は、Goの標準ライブラリのテストコードの品質とデバッグ可能性を向上させるための、細かではあるが重要な修正です。go vetのような静的解析ツールが、このような潜在的な問題を早期に発見する上でいかに有効であるかを示しています。
関連リンク
- Go言語の
fmtパッケージ: https://pkg.go.dev/fmt - Go言語の
testingパッケージ: https://pkg.go.dev/testing go vetコマンドのドキュメント (Go 1.18以降): https://pkg.go.dev/cmd/vet- Go Code Review Comments - Printf format errors: https://go.dev/doc/effective_go#printf
参考にした情報源リンク
- https://golang.org/cl/7393059 (Go Gerrit Code Review)
- https://github.com/golang/go/commit/707ab1347f114934d65b713e22fdd62b4a83ca36 (GitHub Commit Page)
- Go言語の公式ドキュメント (
fmtパッケージ,testingパッケージ,go vetに関する情報) - Effective Go - Printf format errors: https://go.dev/doc/effective_go#printf
- Goの
vetコマンドについて解説しているブログ記事やチュートリアル (一般的な知識として参照)