[インデックス 16603] ファイルの概要
このコミットは、Go言語のテストスイートにおけるfixedbugs
ディレクトリ内の複数のテストファイルに対して、gccgo
コンパイラが出力するエラーメッセージに一致するように修正を加えるものです。具体的には、テストコード内の// ERROR
コメントに記述されている期待されるエラーメッセージに、gccgo
が生成する可能性のある代替メッセージを追加しています。これにより、gc
コンパイラとgccgo
コンパイラのどちらでテストを実行しても、エラーメッセージのパターンマッチングが正しく行われるようになります。
コミット
commit 004dd3d742846f0c4c2fc94e483d407a5a3957a1
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date: Thu Jun 20 08:21:14 2013 +0200
test: match gccgo error messages
R=iant, golang-dev
CC=golang-dev
https://golang.org/cl/10365052
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/004dd3d742846f0c4c2fc94e483d407a5a3957a1
元コミット内容
test: match gccgo error messages
R=iant, golang-dev
CC=golang-dev
https://golang.org/cl/10365052
変更の背景
Go言語には、公式のコンパイラであるgc
(Go Compiler)と、GCC(GNU Compiler Collection)のフロントエンドとして実装されているgccgo
の2つの主要なコンパイラが存在します。これら2つのコンパイラは、Go言語の仕様に準拠していますが、エラーメッセージの出力形式や詳細度において差異が生じることがあります。
Goのテストスイート、特にtest/fixedbugs
ディレクトリ内のテストは、特定のコードが意図的にコンパイルエラーを引き起こすことを期待し、そのエラーメッセージが期待されるパターンに一致するかどうかを検証します。この検証は、テストファイル内の該当行に// ERROR "expected message"
のようなコメントを記述することで行われます。
しかし、gc
とgccgo
の間でエラーメッセージが異なる場合、gc
で成功するテストがgccgo
では失敗する、あるいはその逆の状況が発生します。このコミットの背景には、gccgo
が生成するエラーメッセージがgc
のそれと異なるため、テストがgccgo
環境で正しく動作しないという問題がありました。この変更は、両方のコンパイラでテストがパスするように、期待されるエラーメッセージのパターンを拡張することを目的としています。
前提知識の解説
Go言語のコンパイラ: gc
とgccgo
gc
(Go Compiler): Go言語の公式かつ標準的なコンパイラです。Go言語の開発チームによって開発・保守されており、Goの新しい機能が最も早く実装されます。コンパイル速度とGo言語のセマンティクスへの厳密な準拠に最適化されています。gccgo
: GCCの一部として実装されたGo言語のフロントエンドです。GCCの強力な最適化バックエンドを利用できるため、生成されるバイナリのパフォーマンスが向上する可能性があります。しかし、GCCのアーキテクチャに起因するエラーメッセージの形式や詳細度がgc
とは異なる場合があります。
Goテストにおけるエラーメッセージの検証
Go言語のテストフレームワークでは、特定のコンパイルエラーやランタイムエラーが発生することを期待するテストケースを記述できます。これは、問題のあるコード行の末尾に// ERROR "pattern"
という形式のコメントを追加することで実現されます。テストランナーは、コンパイラが出力するエラーメッセージが指定されたパターンに一致するかどうかをチェックします。
このパターンマッチングは、正規表現に似た形式で行われます。本コミットで導入されている|
(パイプ)記号は、複数の代替パターンをOR条件で指定するために使用されます。例えば、// ERROR "message1|message2"
は、エラーメッセージが"message1"または"message2"のいずれかに一致すればテストがパスすることを示します。
技術的詳細
このコミットで行われた技術的な変更は、Goテストスイート内のfixedbugs
ディレクトリにある既存のテストファイルにおける// ERROR
コメントの修正です。具体的には、各テストファイルで期待されるエラーメッセージの正規表現パターンに、gccgo
コンパイラが生成する可能性のある代替エラーメッセージを追加しています。
変更のパターンは以下の通りです。
// ERROR "original message"
↓// ERROR "original message|gccgo message"
この修正により、テストはgc
コンパイラが生成する元のエラーメッセージにも、gccgo
が生成する代替エラーメッセージにも対応できるようになります。これは、異なるコンパイラ間でエラーメッセージの文言が完全に一致しない場合でも、テストの堅牢性を保つための一般的な手法です。
例えば、bug205.go
の変更では、println(t["hi"]);
に対するエラーメッセージが"non-integer slice index"
から"non-integer slice index|must be integer"
に変更されています。これは、gc
が"non-integer slice index"というメッセージを出すのに対し、gccgo
は"must be integer"というメッセージを出す可能性があるためです。
同様に、他のファイルでも以下のような変更が行われています。
bug459.go
:"loop"
->"loop|depends upon itself"
issue3783.go
:"not a type"
->"not a type|expected type"
issue3925.go
:"missing key"
->"missing key|must have keys"
issue3925.go
:"cannot use"
->"cannot use|incompatible type"
issue4097.go
:"is not a constant"
->"is not a constant|is not constant"
issue4458.go
:"no method foo"
->"no method foo|requires named type or pointer to named"
issue4545.go
:"invalid operation"
->"invalid operation|non-integer type"
これらの変更は、Go言語のテストインフラストラクチャが、複数のコンパイラ実装(特にgc
とgccgo
)間での互換性を維持するために、エラーメッセージの柔軟なマッチングを可能にしていることを示しています。
コアとなるコードの変更箇所
このコミットでは、以下の7つのテストファイルが変更されています。
test/fixedbugs/bug205.go
test/fixedbugs/bug459.go
test/fixedbugs/issue3783.go
test/fixedbugs/issue3925.go
test/fixedbugs/issue4097.go
test/fixedbugs/issue4458.go
test/fixedbugs/issue4545.go
それぞれのファイルにおける具体的な変更は以下の通りです。
diff --git a/test/fixedbugs/bug205.go b/test/fixedbugs/bug205.go
index 769837d04e..1e0d9d1f34 100644
--- a/test/fixedbugs/bug205.go
+++ b/test/fixedbugs/bug205.go
@@ -11,8 +11,8 @@ var s string;
var m map[string]int;
func main() {
- println(t["hi"]); // ERROR "non-integer slice index"
- println(s["hi"]); // ERROR "non-integer string index"
- println(m[0]); // ERROR "as type string in map index"
+ println(t["hi"]); // ERROR "non-integer slice index|must be integer"
+ println(s["hi"]); // ERROR "non-integer string index|must be integer"
+ println(m[0]); // ERROR "cannot use.*as type string"
}
diff --git a/test/fixedbugs/bug459.go b/test/fixedbugs/bug459.go
index 80abe5d518..014f2ef01f 100644
--- a/test/fixedbugs/bug459.go
+++ b/test/fixedbugs/bug459.go
@@ -9,7 +9,7 @@
package flag
-var commandLine = NewFlagSet() // ERROR "loop"
+var commandLine = NewFlagSet() // ERROR "loop|depends upon itself"
type FlagSet struct {
}
diff --git a/test/fixedbugs/issue3783.go b/test/fixedbugs/issue3783.go
index 35df5d8f65..d7a4a2e8f3 100644
--- a/test/fixedbugs/issue3783.go
+++ b/test/fixedbugs/issue3783.go
@@ -8,5 +8,5 @@ package foo
var i int
-func (*i) bar() // ERROR "not a type"
+func (*i) bar() // ERROR "not a type|expected type"
diff --git a/test/fixedbugs/issue3925.go b/test/fixedbugs/issue3925.go
index 2f8786fc78..a62d4392e6 100644
--- a/test/fixedbugs/issue3925.go
+++ b/test/fixedbugs/issue3925.go
@@ -12,12 +12,12 @@ package foo
var _ = map[string]string{
"1": "2",
- "3", "4", // ERROR "missing key"
+ "3", "4", // ERROR "missing key|must have keys"
}
var _ = []string{
"foo",
"bar",
- 20, // ERROR "cannot use"
+ 20, // ERROR "cannot use|incompatible type"
}
diff --git a/test/fixedbugs/issue4097.go b/test/fixedbugs/issue4097.go
index fa942c9db7..c2b7d9b4fb 100644
--- a/test/fixedbugs/issue4097.go
+++ b/test/fixedbugs/issue4097.go
@@ -7,5 +7,5 @@
package foo
var s [][10]int
-const m = len(s[len(s)-1]) // ERROR "is not a constant"
+const m = len(s[len(s)-1]) // ERROR "is not a constant|is not constant"
diff --git a/test/fixedbugs/issue4458.go b/test/fixedbugs/issue4458.go
index 8ee3e879ea..820f18cb8d 100644
--- a/test/fixedbugs/issue4458.go
+++ b/test/fixedbugs/issue4458.go
@@ -16,5 +16,5 @@ func (T) foo() {}
func main() {
av := T{}
pav := &av
- (**T).foo(&pav) // ERROR "no method foo"
+ (**T).foo(&pav) // ERROR "no method foo|requires named type or pointer to named"
}
diff --git a/test/fixedbugs/issue4545.go b/test/fixedbugs/issue4545.go
index 3f2de16d20..501caadb0f 100644
--- a/test/fixedbugs/issue4545.go
+++ b/test/fixedbugs/issue4545.go
@@ -13,7 +13,7 @@ import "fmt"\n \n func main() {\n var s uint\n-\tfmt.Println(1.0 + 1<<s) // ERROR "invalid operation"\n-\tx := 1.0 + 1<<s // ERROR "invalid operation"\n+\tfmt.Println(1.0 + 1<<s) // ERROR "invalid operation|non-integer type"\n+\tx := 1.0 + 1<<s // ERROR "invalid operation|non-integer type"\n \t_ = x\n }\n```
## コアとなるコードの解説
上記の変更はすべて、Goのテストファイル内で期待されるコンパイルエラーメッセージを指定する`// ERROR`コメントのパターンを修正しています。
例えば、`test/fixedbugs/bug205.go`の以下の行を見てみましょう。
```go
println(t["hi"]); // ERROR "non-integer slice index|must be integer"
この行は、t
がスライス型であるにもかかわらず、文字列"hi"
をインデックスとして使用しようとしているため、コンパイルエラーになることを期待しています。
gc
コンパイラは、この場合に"non-integer slice index"
というエラーメッセージを出力します。- 一方、
gccgo
コンパイラは、"must be integer"
という異なる、しかし意味的には同じエラーメッセージを出力する可能性があります。
|
(パイプ)演算子を使用することで、テストランナーはどちらのメッセージが検出されてもテストを成功と判断します。これにより、gc
とgccgo
の両方のコンパイラで同じテストスイートが正しく実行され、期待されるエラーが検出されるようになります。
このアプローチは、コンパイラの実装詳細に起因するエラーメッセージの差異を吸収し、テストの移植性と堅牢性を高めるために非常に重要です。
関連リンク
- Go CL 10365052: https://golang.org/cl/10365052
参考にした情報源リンク
- Go言語の
gc
とgccgo
コンパイラのエラーメッセージの違いに関するStack Overflowの議論: - GCCのエラーメッセージに関する情報:
- Go言語のテストに関するドキュメント(一般的な情報源として):