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

[インデックス 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"のようなコメントを記述することで行われます。

しかし、gcgccgoの間でエラーメッセージが異なる場合、gcで成功するテストがgccgoでは失敗する、あるいはその逆の状況が発生します。このコミットの背景には、gccgoが生成するエラーメッセージがgcのそれと異なるため、テストがgccgo環境で正しく動作しないという問題がありました。この変更は、両方のコンパイラでテストがパスするように、期待されるエラーメッセージのパターンを拡張することを目的としています。

前提知識の解説

Go言語のコンパイラ: gcgccgo

  • 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言語のテストインフラストラクチャが、複数のコンパイラ実装(特にgcgccgo)間での互換性を維持するために、エラーメッセージの柔軟なマッチングを可能にしていることを示しています。

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

このコミットでは、以下の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"という異なる、しかし意味的には同じエラーメッセージを出力する可能性があります。

|(パイプ)演算子を使用することで、テストランナーはどちらのメッセージが検出されてもテストを成功と判断します。これにより、gcgccgoの両方のコンパイラで同じテストスイートが正しく実行され、期待されるエラーが検出されるようになります。

このアプローチは、コンパイラの実装詳細に起因するエラーメッセージの差異を吸収し、テストの移植性と堅牢性を高めるために非常に重要です。

関連リンク

参考にした情報源リンク