[インデックス 11034] ファイルの概要
このコミットは、Go言語のgo testコマンドにおいて、_test.goファイルのコンパイルエラーが発生した際に表示されるエラーメッセージの改善を目的としています。具体的には、エラーメッセージ内で*Package型の値を不適切に表示しようとしていた問題を修正し、よりクリーンなエラー出力になるように変更されています。
コミット
commit 5f5a7eb4bc6160a99ec3656ab87351aa1299341c
Author: Roger Peppe <rogpeppe@gmail.com>
Date: Thu Jan 5 13:19:25 2012 -0800
go test: don't try to print package with error message.
If there's a error compiling a _test.go file, the error
message tries to print a *Package with %s. There's no String
method on *Package, so the error message looks bad.
Since the error messages identify the file in question
anyway, this CL removes the package from the error message.
R=rsc, gri
CC=golang-dev
https://golang.org/cl/5520045
---
src/cmd/go/test.go | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n
diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go
index 1807e42f72..fb0ba7b4d0 100644
--- a/src/cmd/go/test.go
+++ b/src/cmd/go/test.go
@@ -228,7 +228,7 @@ func runTest(cmd *Command, args []string) {
for _, p := range pkgs {
buildTest, runTest, err := b.test(p)
if err != nil {
- errorf("%s: %s", p, err)
+ errorf("%s", err)
continue
}
builds = append(builds, buildTest)
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/5f5a7eb4bc6160a99ec3656ab87351aa1299341c
元コミット内容
このコミットは、go testコマンドが_test.goファイルのコンパイルに失敗した際に、エラーメッセージに*Package型の値を%sフォーマット指定子で含めようとしていた問題を修正します。*Package型にはString()メソッドが実装されていないため、この試みは不適切なエラーメッセージの表示を引き起こしていました。コミットの目的は、エラーメッセージから*Packageの情報を削除し、ファイル自体がエラーメッセージ内で識別されているため、より簡潔で適切なエラー出力を実現することです。
変更の背景
Go言語のgo testコマンドは、テストコードのコンパイルと実行を担当します。テスト対象のパッケージやテストファイル(_test.goで終わるファイル)にコンパイルエラーがある場合、go testはエラーメッセージを出力してユーザーに通知します。
このコミットが修正しようとしている問題は、src/cmd/go/test.go内のerrorf関数呼び出しにありました。具体的には、_test.goファイルのコンパイルエラーが発生した際に、errorf("%s: %s", p, err)という形式でエラーメッセージを生成していました。ここでpは*Package型の変数であり、errはエラー情報です。
Go言語のfmtパッケージ(errorfが内部的に利用するfmt.Errorfなど)では、%sフォーマット指定子を使用して値を文字列として表示しようとします。この際、もしその型がString() stringメソッドを実装していれば、そのメソッドが呼び出されて文字列表現が取得されます。しかし、当時の*Package型にはString()メソッドが実装されていませんでした。
このため、errorf("%s: %s", p, err)が実行されると、p(*Package型)の文字列表現が適切に得られず、例えば&{<package_struct_details>}のような、デバッグ情報としては不十分でユーザーにとっては読みにくい出力になっていました。コミットメッセージにある「the error message looks bad」とはこの状況を指しています。
エラーメッセージ自体は、どのファイルでエラーが発生したかを既に示しているため、*Packageの情報を冗長かつ不適切に含める必要はありませんでした。このコミットは、この冗長で不適切な部分を削除し、エラーメッセージを改善することを目的としています。
前提知識の解説
1. go testコマンド
go testは、Go言語の標準的なテストツールです。プロジェクト内のテストファイル(ファイル名が_test.goで終わるGoソースファイル)をコンパイルし、実行します。テストの成功/失敗、カバレッジ情報などを出力します。
2. Go言語におけるエラーハンドリング
Go言語では、エラーは通常、関数の最後の戻り値としてerror型の値で返されます。errorはインターフェースであり、Error() stringメソッドを実装する任意の型がerrorインターフェースを満たします。
3. fmtパッケージと%sフォーマット指定子、String()メソッド
Go言語のfmtパッケージは、フォーマットされたI/Oを提供します。fmt.Printfやfmt.Errorfなどの関数は、C言語のprintfに似たフォーマット指定子を使用します。
%s: 値を文字列として表示するためのフォーマット指定子です。String() stringメソッド: ある型がString() stringメソッドを実装している場合、fmtパッケージの関数(%sを含む)はそのメソッドを呼び出して、その型の値を文字列として表現します。例えば、type MyType int; func (m MyType) String() string { return fmt.Sprintf("MyType value: %d", m) }のように定義されたMyTypeの変数を%sで表示すると、MyType value: <値>のような出力が得られます。String()メソッドがない場合:String()メソッドが実装されていない型を%sで表示しようとすると、Goランタイムはその型のデフォルトの文字列表現(通常は構造体のアドレスやフィールド値を含むデバッグ形式)を出力します。これが、このコミットで問題となっていた「looks bad」な出力の原因です。
4. _test.goファイル
Go言語のテストファイルは、慣習的にファイル名の末尾に_test.goを付けます。これらのファイルは、通常のアプリケーションコードとは別にコンパイルされ、go testコマンドによって実行されます。
技術的詳細
このコミットの技術的な核心は、Go言語のfmtパッケージのフォーマットルールと、特定の型(この場合は*Package)がそのルールにどのように適合するか(または適合しないか)にあります。
src/cmd/go/test.goファイル内のrunTest関数は、テスト対象の各パッケージに対してb.test(p)を呼び出し、テストのビルドと実行を行います。このb.test(p)がエラーを返した場合、if err != nilブロックに入り、errorf関数が呼び出されてエラーメッセージが出力されます。
変更前のコードは以下の通りでした。
errorf("%s: %s", p, err)
ここで、pは*Package型の変数です。Package型は、Goのビルドシステムが扱うパッケージのメタデータを表す構造体です。当時の*Package型には、fmtパッケージが%sフォーマット指定子で期待するString() stringメソッドが実装されていませんでした。
そのため、errorfがpを文字列としてフォーマットしようとすると、Goランタイムは*Package型のデフォルトの文字列表現(例えば、&{Name: "main" Dir: "/path/to/pkg" ...}のような、構造体の内部表現)を出力していました。これは、ユーザーが求めているエラーの原因(コンパイルエラー)とは直接関係なく、むしろノイズとなっていました。
コミットメッセージが指摘するように、実際のエラーメッセージ(errの部分)には、コンパイルエラーが発生したファイル名などの重要な情報が既に含まれていました。したがって、*Packageの情報を追加することは冗長であり、かつその表示形式が不適切であったため、エラーメッセージの可読性を損なっていました。
このコミットは、この問題を解決するために、errorfの呼び出しからp(*Package)の引数とそれに対応するフォーマット指定子%s:を削除しました。
コアとなるコードの変更箇所
変更はsrc/cmd/go/test.goファイルの一箇所のみです。
--- a/src/cmd/go/test.go
+++ b/src/cmd/go/test.go
@@ -228,7 +228,7 @@ func runTest(cmd *Command, args []string) {
for _, p := range pkgs {
buildTest, runTest, err := b.test(p)
if err != nil {
- errorf("%s: %s", p, err)
+ errorf("%s", err)
continue
}
builds = append(builds, buildTest)
コアとなるコードの解説
変更された行は、src/cmd/go/test.goファイルのrunTest関数内のエラーハンドリング部分です。
元のコード:
errorf("%s: %s", p, err)
この行では、errorf関数(おそらくfmt.Errorfのラッパー)を呼び出し、2つの引数p(*Package型)とerr(error型)を渡していました。フォーマット文字列"%s: %s"は、最初の%sでpを、2番目の%sでerrを文字列として表示しようとしていました。前述の通り、pにはString()メソッドがなかったため、不適切な出力になっていました。
変更後のコード:
errorf("%s", err)
この変更により、errorf関数に渡される引数はerrのみとなり、フォーマット文字列も"%s"に簡略化されました。errorインターフェースを実装する型は、必ずError() stringメソッドを持つため、errは常に適切な文字列として表示されます。
この修正によって、_test.goファイルのコンパイルエラーが発生した場合のエラーメッセージは、冗長な*Packageの情報を含まず、純粋にエラーの内容のみを表示するようになります。これにより、エラーメッセージの可読性が向上し、ユーザーは問題の特定をより容易に行えるようになります。
関連リンク
- Go CL 5520045: https://golang.org/cl/5520045
参考にした情報源リンク
- 提供されたコミット情報 (
./commit_data/11034.txt) - Go言語の
fmtパッケージのドキュメント(一般的な知識として) - Go言語のエラーハンドリングに関する一般的な知識