[インデックス 12162] ファイルの概要
このコミットは、Go言語のコマンドラインツール go
における go test
コマンドの挙動を改善するものです。具体的には、テスト実行時に発生したエラーが複数ある場合、これまでは最初に見つかったエラーのみが表示されていましたが、この変更により複数のエラー(最大10個)が表示されるようになります。
コミット
commit 548591b77d115a557e8e6351b78b96831002b306
Author: Robert Griesemer <gri@golang.org>
Date: Wed Feb 22 22:33:45 2012 -0800
go cmd: print more than one error when running go test
Fixes #3055.
R=rsc, r
CC=golang-dev
https://golang.org/cl/5683079
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/548591b77d115a557e8e6351b78b96831002b306
元コミット内容
go cmd: print more than one error when running go test
Fixes #3055.
このコミットは、go test
コマンドがテスト実行時に複数のエラーを報告するように変更します。これは、Go Issue #3055 を解決するためのものです。
変更の背景
Go言語の初期のツールチェインでは、go test
コマンドがテストのビルドや実行中にエラーを検出した場合、通常は最初に見つかったエラーのみを報告し、それ以上処理を続行しない傾向がありました。これは、開発者がコードの問題を特定し、修正する際に非効率的であるという問題を引き起こしていました。例えば、構文エラーが複数箇所にある場合、一つ修正するたびに再度 go test
を実行し、次のエラーを発見するという手間が発生していました。
Issue #3055 は、この「単一エラー報告」の挙動に対する不満を表明しており、より多くのエラー情報を一度に提供することで、開発者のデバッグ体験を向上させることを目的としていました。特に、go/scanner
パッケージが提供する ErrorList
のような、複数のエラーをまとめて扱うことができるメカニズムが存在するにもかかわらず、それが活用されていない点が指摘されていました。
このコミットは、この問題を解決し、go test
がよりユーザーフレンドリーなエラー報告を行うようにするためのものです。
前提知識の解説
go test
コマンド: Go言語の標準的なテスト実行ツールです。Goのソースコード内のテスト関数(TestXxx
で始まる関数)を検出し、ビルドし、実行します。テストの成功/失敗、カバレッジ情報などを報告します。go/scanner
パッケージ: Go言語のソースコードを字句解析(スキャン)する機能を提供するパッケージです。ソースコードをトークンに分割する過程で構文エラーなどを検出することがあります。scanner.ErrorList
:go/scanner
パッケージが提供する型で、複数のスキャンエラーを保持するためのスライス(リスト)です。コンパイルエラーや構文エラーが複数ある場合に、これらをまとめて報告するために使用されます。errorf
関数: Goコマンドラインツール内でエラーメッセージを出力するためのユーティリティ関数です。通常、標準エラー出力にメッセージを書き込み、プログラムの終了コードを非ゼロに設定します。- Goのビルドプロセス: Goのソースコードは、コンパイルされて実行可能なバイナリになります。この過程で、字句解析、構文解析、型チェックなどのフェーズがあり、それぞれでエラーが検出される可能性があります。
go test
も内部的にはテストコードをビルドするプロセスを含んでいます。
技術的詳細
この変更は、src/cmd/go/test.go
ファイル内の runTest
関数に焦点を当てています。この関数は、Goパッケージのテストをビルドおよび実行する主要なロジックを含んでいます。
変更前は、b.test(p)
から返されるエラー err
が nil
でない場合、単に errorf("%s", err)
を呼び出してエラーメッセージを出力し、次のパッケージの処理に移っていました。この挙動では、err
が scanner.ErrorList
のインスタンスであったとしても、そのリストに含まれる個々のエラーは展開されず、単一のエラー文字列として扱われていました。
このコミットでは、以下のロジックが追加されています。
if err != nil
のブロック内で、まずerr
がscanner.ErrorList
型に型アサーション可能かどうかをチェックします (if list, ok := err.(scanner.ErrorList); ok
)。- もし
err
がscanner.ErrorList
であった場合、そのエラーリストlist
の要素数がn
(ここでは10
に設定されている) を超えているかをチェックします。 - もし要素数が
n
を超えていれば、リストを最初のn
個のエラーに切り詰めます (list = list[:n]
)。これにより、あまりにも大量のエラーが出力されてコンソールが溢れるのを防ぎます。 - 切り詰められた(または元々の)
ErrorList
の各エラーについて、ループ (for _, err := range list
) を回し、個々のエラーをerrorf("%s", err)
を使って出力します。 ErrorList
のすべてのエラーを出力した後、continue
ステートメントによって現在のパッケージの処理をスキップし、次のパッケージのテスト処理へと進みます。これにより、ErrorList
が処理された後に、その下の一般的なerrorf
が再度呼び出されるのを防ぎます。
この変更により、go test
は、特に構文解析や字句解析の段階で発生する複数のエラーを、より詳細に開発者に伝えることができるようになりました。
コアとなるコードの変更箇所
変更は src/cmd/go/test.go
ファイルの runTest
関数内で行われています。
--- a/src/cmd/go/test.go
+++ b/src/cmd/go/test.go
@@ -11,6 +11,7 @@ import (
"go/build"
"go/doc"
"go/parser"
+ "go/scanner"
"go/token"
"os"
"os/exec"
@@ -299,6 +300,16 @@ func runTest(cmd *Command, args []string) {
for _, p := range pkgs {
buildTest, runTest, printTest, err := b.test(p)
if err != nil {
+ if list, ok := err.(scanner.ErrorList); ok {
+ const n = 10
+ if len(list) > n {
+ list = list[:n]
+ }
+ for _, err := range list {
+ errorf("%s", err)
+ }
+ continue
+ }
errorf("%s", err)
continue
}
コアとなるコードの解説
import "go/scanner"
:scanner.ErrorList
型を使用するために、go/scanner
パッケージがインポートに追加されています。if list, ok := err.(scanner.ErrorList); ok { ... }
:- これはGoの型アサーションの構文です。
err
インターフェース変数がscanner.ErrorList
型の具体的な値を持っているかどうかをチェックします。 ok
はブール値で、型アサーションが成功したかどうかを示します。成功した場合、list
にはerr
のscanner.ErrorList
型の値が代入されます。
- これはGoの型アサーションの構文です。
const n = 10
: 表示するエラーの最大数を定義しています。これにより、無限にエラーが出力されるのを防ぎ、コンソールの可読性を保ちます。if len(list) > n { list = list[:n] }
:ErrorList
に含まれるエラーの数がn
(10) を超える場合、スライスを切り詰めて最初のn
個のエラーのみを保持するようにします。
for _, err := range list { errorf("%s", err) }
:scanner.ErrorList
内の各エラーをループで処理し、それぞれをerrorf
関数を使って個別に標準エラー出力に表示します。これにより、複数のエラーメッセージがそれぞれ独立した行で表示され、開発者にとって読みやすくなります。
continue
:scanner.ErrorList
が処理された後、このcontinue
ステートメントが実行されます。これにより、現在のパッケージの残りの処理(buildTest
,runTest
,printTest
の呼び出しなど)はスキップされ、for _, p := range pkgs
ループの次のイテレーション(次のパッケージの処理)へと移ります。これは、ErrorList
が検出された時点で、そのパッケージのテストビルドは失敗しているため、これ以上処理を続けても意味がないと判断されるためです。また、このcontinue
がないと、ErrorList
の処理後に、その下の一般的なerrorf("%s", err)
が再度呼び出されてしまい、同じエラーが二重に報告される可能性があります。
この変更は、Goツールのエラー報告の質を向上させ、開発者がより効率的に問題を特定し、修正できるようにするための重要な改善です。
関連リンク
- Go Issue #3055: https://github.com/golang/go/issues/3055
- Go CL 5683079: https://golang.org/cl/5683079 (Gerrit Code Review)
参考にした情報源リンク
- Go言語の公式ドキュメント (go test, go/scanner パッケージに関する情報)
- Go言語のソースコード (特に
src/cmd/go/test.go
およびgo/scanner
パッケージ) - Go Issue Tracker (Issue #3055 の議論)
- Gerrit Code Review (CL 5683079 のレビューコメント)
- Go言語の型アサーションに関するドキュメントやチュートリアル