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

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

このコミットは、Go言語の標準ツールであるgo vetによって検出されたエラーを修正するものです。具体的には、fmt.Printft.Fatalfのようなフォーマット文字列を使用する関数呼び出しにおいて、引数の型とフォーマット指定子が一致しないという問題を解決しています。これにより、実行時の予期せぬ出力やエラーを防ぎ、コードの堅牢性を向上させています。

コミット

  • コミットハッシュ: 21be71a41966c4b8d9107dec47bed1b3867487bb
  • 作者: Rob Pike r@golang.org
  • コミット日時: 2012年2月16日 木曜日 17:21:21 +1100

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

https://github.com/golang/go/commit/21be71a41966c4b8d9107dec47bed1b3867487bb

元コミット内容

all: errors caught by go vet

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/5674069

変更の背景

このコミットの主な背景は、Go言語の静的解析ツールであるgo vetが検出した潜在的なバグやコードの不整合を修正することにあります。go vetは、コンパイル時には検出されないが、実行時に問題を引き起こす可能性のあるコードパターンを特定するために設計されています。

この特定のケースでは、go vetfmt.Printf系の関数におけるフォーマット文字列の誤用を指摘しました。具体的には、数値を出力しようとしている箇所で汎用的な%v(任意の値をデフォルトのフォーマットで出力)が使われていたり、その逆のケースがあったりしました。このような不一致は、プログラムの実行結果が意図しないものになったり、デバッグが困難になったりする原因となります。

go vetの導入と活用は、Goプロジェクト全体のコード品質と信頼性を高めるための継続的な取り組みの一環です。開発プロセスに静的解析を組み込むことで、早期に問題を特定し、修正することが可能になります。

前提知識の解説

go vet

go vetは、Go言語のソースコードを静的に解析し、疑わしい構成要素や潜在的なエラーを報告するツールです。コンパイラが検出できないような、しかし実行時に問題を引き起こす可能性のあるパターン(例: Printfフォーマット文字列の誤用、到達不能なコード、ロックの誤用など)を特定します。go vetはGo SDKに含まれており、Go開発者がコードの品質と堅牢性を維持するために広く利用されています。

fmt.Printfとフォーマット指定子

Go言語のfmtパッケージは、フォーマットされたI/Oを実装するための機能を提供します。fmt.Printf関数は、C言語のprintfに似ており、フォーマット文字列とそれに続く引数を受け取ります。フォーマット文字列内の「フォーマット指定子」(例: %v, %d, %sなど)は、対応する引数がどのように出力されるかを制御します。

  • %v: 値をデフォルトのフォーマットで出力します。構造体や配列など、任意の型に対して使用できます。
  • %d: 整数値を10進数で出力します。
  • %s: 文字列を出力します。
  • %T: 値の型を出力します。

フォーマット指定子と引数の型が一致しない場合、go vetは警告を発し、実行時には予期せぬ出力やパニックが発生する可能性があります。

testing.Tt.Fatalf

Go言語の標準テストフレームワークでは、テスト関数は*testing.T型の引数を受け取ります。このtオブジェクトは、テストの実行を制御し、結果を報告するためのメソッドを提供します。

  • t.Fatalf(format string, args ...interface{}): テストを失敗としてマークし、指定されたフォーマット文字列と引数でエラーメッセージを出力し、現在のテスト関数を即座に終了させます。これは、テストが続行できない致命的なエラーが発生した場合に用いられます。

技術的詳細

このコミットで修正されたエラーは、主にfmt.Printf系の関数におけるフォーマット文字列の不一致に起因します。go vetは、これらの関数呼び出しを解析し、提供されたフォーマット指定子が対応する引数の型と互換性があるかどうかをチェックします。

例えば、fmt.Printf("... %d ...", someString)のように、整数を期待する%d指定子に対して文字列型の引数が渡された場合、go vetはこれをエラーとして報告します。同様に、fmt.Printf("... %v ...", someInt)のように、特定の型(この場合は整数)を期待するが、汎用的な%vが使われている場合も、より適切な指定子(%d)への変更を促すことがあります。これは、%vが常に間違っているわけではありませんが、より具体的な指定子を使用することで、意図が明確になり、将来的なコードの変更やデバッグが容易になるためです。

このコミットでは、以下の2つのファイルで具体的な修正が行われています。

  1. misc/cgo/test/basic.go: t.Fatalfの呼び出しにおいて、エラーオブジェクトを出力する際に%vを使用するように修正されています。エラーオブジェクトは通常、その内容を適切に表示するために汎用的な%vが適しています。
  2. test/map.go: fmt.Printfの呼び出しにおいて、整数型の変数を出力する際に%i(これはGoのfmtパッケージには存在しない)から正しい整数フォーマット指定子である%dに修正されています。これは典型的なタイプミスであり、go vetがこのような間違いを検出できることを示しています。

これらの修正は、go vetがGoコードベース全体の品質と正確性を維持するためにいかに重要であるかを示しています。

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

misc/cgo/test/basic.go

--- a/misc/cgo/test/basic.go
+++ b/misc/cgo/test/basic.go
@@ -111,7 +111,7 @@ func testErrno(t *testing.T) {
 		t.Fatalf("C.fopen: should fail")
 	}
 	if err != os.ENOENT {
-		t.Fatalf("C.fopen: unexpected error: ", err)
+		t.Fatalf("C.fopen: unexpected error: %v", err)
 	}\n
 }

test/map.go

--- a/test/map.go
+++ b/test/map.go
@@ -487,7 +487,7 @@ func testbasic() {
 
 		tmipM[i][i]++
 		if mipM[i][i] != (i+1)+1 {
-			fmt.Printf("update mipM[%d][%d] = %i\n", i, i, mipM[i][i])
+			fmt.Printf("update mipM[%d][%d] = %d\n", i, i, mipM[i][i])
 		}
 	}

コアとなるコードの解説

misc/cgo/test/basic.goの変更

元のコードでは、t.Fatalf("C.fopen: unexpected error: ", err)となっていました。これは、t.Fatalfが可変引数を受け取るものの、フォーマット文字列の後に直接引数を置く形式であり、フォーマット指定子がないため、go vetが警告を発する可能性があります。特に、エラーオブジェクトerrを適切に表示するためには、%vのようなフォーマット指定子を使用するのがGoの慣習です。

修正後のコードt.Fatalf("C.fopen: unexpected error: %v", err)では、フォーマット文字列内に%vが追加され、その後にerrが引数として渡されています。これにより、errオブジェクトがそのデフォルトの文字列表現にフォーマットされて出力されるようになり、go vetのエラーが解消され、より意図通りのエラーメッセージが表示されるようになります。

test/map.goの変更

元のコードでは、fmt.Printf("update mipM[%d][%d] = %i\n", i, i, mipM[i][i])となっていました。ここで問題なのは、%iというフォーマット指定子です。Goのfmtパッケージには%iという指定子は存在せず、整数を出力するためには%dを使用するのが正しいです。これは典型的なタイプミスであり、コンパイルエラーにはなりませんが、go vetがこのような潜在的な問題を検出します。

修正後のコードfmt.Printf("update mipM[%d][%d] = %d\n", i, i, mipM[i][i])では、誤った%iが正しい%dに修正されています。これにより、mipM[i][i]という整数値が正しく10進数としてフォーマットされて出力されるようになり、go vetのエラーが解消されます。

これらの変更は、Goのフォーマット文字列の正しい使用法を徹底し、go vetがコードの品質保証に果たす役割を明確に示しています。

関連リンク

  • Go Gerrit Change: https://golang.org/cl/5674069 Goプロジェクトでは、GitHubにプッシュされる前に、Gerritというコードレビューシステムで変更が管理されます。このリンクは、このコミットに対応するGerrit上の変更セットを示しています。

参考にした情報源リンク