[インデックス 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言語のエラーハンドリングに関する一般的な知識