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

[インデックス 14425] ファイルの概要

このコミットは、Go言語の標準ライブラリであるarchive/zipcrypto/tlsnet/httpパッケージ内のテストコードにおける出力フォーマットの誤りを修正するものです。具体的には、fmt.Printf系の関数(t.Fatalft.Errorfなど)のフォーマット文字列と引数の型が一致していない箇所を修正しています。これらのエラーは、Goの静的解析ツールであるgo vetによって検出されました。

コミット

commit 4f250132f734f3686e1bff3b632e4598a82931a8
Author: Robin Eklind <r.eklind.87@gmail.com>
Date:   Fri Nov 16 17:24:43 2012 -0800

    archive/zip, crypto/tls, net/http: Fix print format errors.
    
    All of the errors were located using "go vet ./..." in "src/pkg".
    
    R=golang-dev, iant
    CC=golang-dev
    https://golang.org/cl/6856056

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/4f250132f734f3686e1bff3b632e4598a82931a8

元コミット内容

archive/zip, crypto/tls, net/http: Fix print format errors.

All of the errors were located using "go vet ./..." in "src/pkg".

R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/6856056

変更の背景

この変更の背景には、Go言語のコード品質と堅牢性を向上させるという目的があります。fmt.Printf系の関数におけるフォーマット文字列と引数の不一致は、実行時に予期せぬ出力やパニックを引き起こす可能性があります。特にテストコードにおいては、エラーメッセージの正確性がデバッグの効率に直結するため、このような問題は早期に修正されるべきです。

go vetツールは、このような潜在的なバグや疑わしいコードパターンを静的に検出するために設計されています。このコミットは、go vetsrc/pkgディレクトリ以下のすべてのパッケージに対して実行した結果として検出されたエラーを修正したものです。これにより、Go標準ライブラリのテストコードの信頼性が向上し、将来的な開発における同様の問題の発生を未然に防ぐことができます。

前提知識の解説

Go言語のfmtパッケージとフォーマット動詞

Go言語のfmtパッケージは、フォーマットされたI/O(入力/出力)を実装するための機能を提供します。PrintfSprintfErrorfなどの関数は、C言語のprintfに似たフォーマット文字列を使用して、様々な型の値を整形して出力することができます。

フォーマット文字列には、値をどのように表示するかを指定する「フォーマット動詞(verb)」が含まれます。主要なフォーマット動詞には以下のようなものがあります。

  • %v: 値のデフォルトフォーマット。任意の型に対応し、構造体なども表示できます。
  • %s: 文字列(string)型。
  • %d: 10進数整数(decimal integer)型。
  • %q: Goの構文で安全に引用符で囲まれた文字列(string)またはバイトスライス([]byte)。
  • %T: 値の型名。

これらのフォーマット動詞と、実際に渡される引数の型が一致しない場合、go vetは警告を発します。例えば、整数を期待する%dに文字列を渡したり、文字列を期待する%sに整数を渡したりすると、実行時に予期せぬ結果(例: %!s(int=123)のような出力)になったり、パニックが発生する可能性があります。

go vetツール

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

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

  • Printf系のフォーマット文字列と引数の不一致: 本コミットで修正された問題です。
  • 到達不能なコード: 常に実行されないコードパス。
  • ロックの誤用: sync.Mutexなどのロックが正しく使用されていない場合。
  • 構造体タグの誤り: json:"field_name"のような構造体タグの構文エラー。
  • 未使用の変数やインポート: コンパイラが警告を出す場合もありますが、go vetはより詳細な分析を行います。

go vet ./...というコマンドは、現在のディレクトリとそのサブディレクトリにあるすべてのGoパッケージに対してgo vetを実行することを意味します。このツールは、Go開発者がコードの品質と信頼性を維持するために広く利用されています。

技術的詳細

このコミットは、主にGoのテストフレームワークであるtestingパッケージのt.Fatalfおよびt.Errorf関数におけるフォーマット文字列の修正に焦点を当てています。これらの関数は内部的にfmt.Printfと同様のメカニズムを使用しており、フォーマット動詞と引数の型が一致している必要があります。

具体的な修正内容は以下の通りです。

  1. archive/zip/zip_test.go:

    • t.Fatal("error closing zip writer: %v", err)t.Fatalf("error closing zip writer: %v", err) に変更。
      • t.Fatalは引数をそのまま出力するのに対し、t.Fatalffmt.Sprintfのようにフォーマット文字列と引数を解釈します。この変更により、errが正しく%vとしてフォーマットされるようになります。
    • t.Fatal("got %v, expected ErrFormat", err)t.Fatalf("got %v, expected ErrFormat", err) に変更。
      • 上記と同様の理由です。
  2. crypto/tls/prf_test.go:

    • t.Errorf("#%d: bad master secret %s, want %s", s, test.masterSecret)t.Errorf("#%d: bad master secret %s, want %s", i, s, test.masterSecret) に変更。
      • 元のコードでは、最初の%dに対応する引数がs(文字列)になっていました。これは型が不一致です。修正後は、ループインデックスであるi(整数)が渡されるようになり、%diの型が一致します。
  3. net/http/transfer_test.go:

    • t.Fatalf(first Read = %n (%q), %v; want 3 ("foo"), n, got, err)t.Fatalf(first Read = %d (%q), %v; want 3 ("foo"), n, got, err) に変更。
      • %nというフォーマット動詞はGoのfmtパッケージには存在しません。これはおそらくタイプミスで、整数nを表示するために%dが意図されていました。%dに修正することで、nの値が正しく10進数として表示されるようになります。
    • t.Fatalf(second Read = %n (%q), %v; want 3 ("bar"), n, got, err)t.Fatalf(second Read = %d (%q), %v; want 3 ("bar"), n, got, err) に変更。
      • 上記と同様の修正です。

これらの修正は、Goのfmtパッケージのフォーマットルールに厳密に従うことで、テスト出力の正確性を保証し、go vetによる警告を解消することを目的としています。

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

--- a/src/pkg/archive/zip/zip_test.go
+++ b/src/pkg/archive/zip/zip_test.go
@@ -186,12 +186,12 @@ func testInvalidHeader(h *FileHeader, t *testing.T) {
 		t.Fatalf("error writing content: %v", err)
 	}
 	if err := z.Close(); err != nil {
-		t.Fatal("error closing zip writer: %v", err)
+		t.Fatalf("error closing zip writer: %v", err)
 	}
 
 	b := buf.Bytes()
 	if _, err = NewReader(bytes.NewReader(b), int64(len(b))); err != ErrFormat {
-		t.Fatal("got %v, expected ErrFormat", err)
+		t.Fatalf("got %v, expected ErrFormat", err)
 	}
 }
 
--- a/src/pkg/crypto/tls/prf_test.go
+++ b/src/pkg/crypto/tls/prf_test.go
@@ -51,7 +51,7 @@ func TestKeysFromPreMasterSecret(t *testing.T) {
 
 		masterSecret := masterFromPreMasterSecret(test.version, in, clientRandom, serverRandom)
 		if s := hex.EncodeToString(masterSecret); s != test.masterSecret {
-			t.Errorf("#%d: bad master secret %s, want %s", s, test.masterSecret)
+			t.Errorf("#%d: bad master secret %s, want %s", i, s, test.masterSecret)
 			continue
 		}
 
--- a/src/pkg/net/http/transfer_test.go
+++ b/src/pkg/net/http/transfer_test.go
@@ -20,13 +20,13 @@ func TestBodyReadBadTrailer(t *testing.T) {
 	n, err := b.Read(buf[:3])
 	got := string(buf[:n])
 	if got != "foo" || err != nil {
-		t.Fatalf(`first Read = %n (%q), %v; want 3 ("foo")`, n, got, err)
+		t.Fatalf(`first Read = %d (%q), %v; want 3 ("foo")`, n, got, err)
 	}
 
 	n, err = b.Read(buf[:])
 	got = string(buf[:n])
 	if got != "bar" || err != nil {
-		t.Fatalf(`second Read = %n (%q), %v; want 3 ("bar")`, n, got, err)
+		t.Fatalf(`second Read = %d (%q), %v; want 3 ("bar")`, n, got, err)
 	}
 
 	n, err = b.Read(buf[:])

コアとなるコードの解説

archive/zip/zip_test.go

このファイルでは、t.Fatalt.Fatalfに修正されています。t.Fatalは引数をそのままログに出力し、テストを終了させます。一方、t.Fatalffmt.Sprintfのようにフォーマット文字列と引数を解釈し、フォーマットされたメッセージをログに出力してからテストを終了させます。元のコードでは%vというフォーマット動詞が使われていましたが、t.Fatalはこれを解釈しないため、意図したエラーメッセージが出力されませんでした。t.Fatalfに変更することで、err変数の内容が正しく%vフォーマットで表示されるようになります。

crypto/tls/prf_test.go

ここでは、t.Errorfの引数が修正されています。元のコードでは、#%dというフォーマット動詞に対して文字列型の変数sが渡されていました。これは型不一致であり、go vetによって検出されます。修正後は、ループのインデックスである整数型の変数iが渡されるようになり、%dと引数の型が一致するようになりました。これにより、エラーメッセージが正しくフォーマットされ、どのテストケースでエラーが発生したかが明確になります。

net/http/transfer_test.go

このファイルでは、t.Fatalfのフォーマット文字列内の%n%dに修正されています。%nはGoのfmtパッケージには存在しないフォーマット動詞であり、これはタイプミスである可能性が高いです。nは読み込んだバイト数を示す整数値であるため、10進数整数を表す%dが正しいフォーマット動詞です。この修正により、nの値が正しく数値として表示されるようになり、テストの出力が意図通りになります。

これらの変更はすべて、Goの標準ライブラリのテストコードにおける出力の正確性と、go vetによる静的解析の指摘事項への対応を目的としています。

関連リンク

参考にした情報源リンク