[インデックス 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("expected
XYZgot %c", y.G.s)
がt.Fatalf("expected
XYZgot %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
コマンドについて解説しているブログ記事やチュートリアル (一般的な知識として参照)