[インデックス 1046] ファイルの概要
このコミットは、Go言語のテストスイートにおけるメソッドの不正な使用例を、既存の test/method.go
から新しいテストファイル test/method2.go
へ移動させるものです。これにより、テストの目的が明確化され、特定のコンパイルエラー(レシーバーに関するエラー)を意図的にテストするための分離が行われています。
コミット
commit d289e6344f6ed40d5e2e6646e32fc8a685f38c75
Author: Russ Cox <rsc@golang.org>
Date: Tue Nov 4 09:45:27 2008 -0800
move invalid method uses to new test
R=iant
DELTA=24 (13 added, 10 deleted, 1 changed)
OCL=18424
CL=18439
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d289e6344f6ed40d5e2e6646e32fc8a685f38c75
元コミット内容
不正なメソッドの使用例を新しいテストファイルに移動。
変更の背景
このコミットが行われた2008年11月は、Go言語がまだ活発に開発されていた初期段階にあたります。Go言語の設計、特に型システムやメソッドの定義に関するルールが固まりつつある時期でした。
test/method.go
は、Go言語におけるメソッドの基本的な動作を検証するためのテストファイルであったと推測されます。しかし、その中に「不正なメソッドの使用」に関するテストケースが含まれていると、本来の「正しいメソッドの動作検証」という目的と混在し、テストの意図が不明瞭になる可能性があります。
このコミットの背景には、以下のような意図があったと考えられます。
- テストの分離と明確化: 正しい動作を検証するテストと、コンパイルエラーやランタイムエラーを意図的に引き起こすテストを分離することで、それぞれのテストの目的を明確にし、管理しやすくする。
- コンパイラの挙動テストの強化: Goコンパイラが、不正なコードに対して適切にエラーを報告するかどうかを検証するための専用のテストケースを設ける。特に、
test/method2.go
にはERROR "receiver"
というコメントがあり、これはコンパイラが特定のレシーバーの定義に対してエラーを出すことを期待していることを示しています。 - テストスイートの整理: 開発が進むにつれてテストケースが増加し、既存のファイルが肥大化するのを防ぐための整理の一環。
前提知識の解説
このコミットを理解するためには、Go言語の以下の基本的な概念を理解しておく必要があります。
-
Go言語のメソッド:
- Go言語において、メソッドは特定の型に関連付けられた関数です。
- メソッドは「レシーバー」と呼ばれる特別な引数を持ちます。レシーバーは、メソッドがどの型の値に対して呼び出されるかを定義します。
- レシーバーには「値レシーバー」と「ポインタレシーバー」の2種類があります。
- 値レシーバー:
func (t T) MethodName(...)
のように定義され、レシーバーの型の値のコピーに対してメソッドが呼び出されます。 - ポインタレシーバー:
func (t *T) MethodName(...)
のように定義され、レシーバーの型のポインタに対してメソッドが呼び出されます。これにより、メソッド内でレシーバーの値を変更できます。
- 値レシーバー:
- Goの仕様では、ポインタ型自体に直接メソッドを定義することはできません。例えば、
type P *int
と定義した場合、func (p P) MethodName()
のようなメソッド定義は不正とされます。メソッドは基底の型(この場合はint
)または構造体型に定義されるのが一般的です。このコミットで移動されたコードは、まさにこの「ポインタ型への直接のメソッド定義」という不正なケースを扱っています。
-
Go言語のテストフレームワーク (
go test
):- Go言語には標準でテストフレームワークが組み込まれており、
go test
コマンドで実行されます。 - テストファイルは通常
_test.go
というサフィックスを持ちます。 - コンパイルエラーを期待するテストでは、
// errchk
ディレクティブが使用されることがあります。これは、そのファイルがコンパイル時に特定のエラーを発生させることを期待していることをコンパイラに伝えるためのものです。// ERROR "..."
コメントは、期待されるエラーメッセージのパターンを示します。
- Go言語には標準でテストフレームワークが組み込まれており、
-
golden.out
ファイル:- Go言語のテストスイートでは、
test/golden.out
のようなファイルが使用されることがあります。これは、テストの実行結果(標準出力やエラー出力など)の「ゴールデンマスター」または「期待される出力」を記録するファイルです。 - テスト実行時に生成された出力が
golden.out
の内容と一致するかどうかを比較することで、テストの合否を判定します。このコミットでは、新しいテストファイルmethod2.go
の追加に伴い、golden.out
にそのテストの出力(特にエラーメッセージ)が追加されています。
- Go言語のテストスイートでは、
技術的詳細
このコミットの技術的な核心は、Go言語の型システムにおけるメソッドのレシーバーに関する厳格なルールをテストすることにあります。
test/method.go
から削除され、test/method2.go
に移動されたコードは、以下のパターンを含んでいます。
type P *int
type P1 *int
func (p P) val() int { return 5 }
func (p *P1) val() int { return 6 }
Go言語の仕様では、メソッドのレシーバーは、基底の型(int
, string
など)または構造体型(struct
)である必要があります。ポインタ型(*int
や *T
)を直接レシーバーの型として使用することはできません。
type P *int
はint
へのポインタ型*int
のエイリアスです。func (p P) val() int
は、P
型(つまり*int
型)を値レシーバーとしてメソッドを定義しようとしています。func (p *P1) val() int
は、P1
型(つまり*int
型)へのポインタをポインタレシーバーとしてメソッドを定義しようとしています。これは**int
となり、これもGoのメソッドレシーバーとしては不正です。
test/method2.go
では、これらの不正なメソッド定義に対して // ERROR "receiver"
というコメントが付けられています。これは、Goコンパイラがこれらの行で「レシーバーが不正である」というコンパイルエラーを正しく報告することを期待していることを意味します。
test/golden.out
の変更は、test/method2.go
が実行された際に、6g ./method2.go
(Goコンパイラ 6g
によるコンパイル) が予期せず成功した場合に BUG: errchk: command succeeded unexpectedly
というエラーを報告するように更新されています。これは、method2.go
がコンパイルエラーを出すべきであり、もしコンパイルが成功してしまったらそれはバグである、というテストの意図を明確にしています。
また、test/golden.out
の別の変更 panic on line 74 PC=xxx
から panic on line 77 PC=xxx
への変更は、test/method.go
からコードが削除されたことにより、他のテストケースの行番号がずれたことによる調整である可能性が高いです。
コアとなるコードの変更箇所
test/golden.out
--- a/test/golden.out
+++ b/test/golden.out
@@ -37,6 +37,9 @@ Faulting address: 0x0
pc: xxx
+=========== ./method2.go
+BUG: errchk: command succeeded unexpectedly: 6g ./method2.go
+\
=========== ./peano.go
0! = 1
1! = 1
@@ -121,7 +124,7 @@ BUG: fails incorrectly
=========== bugs/bug095.go
found 2, expected 1
-panic on line 74 PC=xxx
+panic on line 77 PC=xxx
BUG wrong result
=========== bugs/bug098.go
test/method.go
--- a/test/method.go
+++ b/test/method.go
@@ -10,8 +10,6 @@ type S string
type S1 string
type I int
type I1 int
-type P *int
-type P1 *int
type T struct { x int }
type T1 T
@@ -19,8 +17,6 @@ func (s S) val() int { return 1 }
func (s *S1) val() int { return 2 }
func (i I) val() int { return 3 }
func (i *I1) val() int { return 4 }
-func (p P) val() int { return 5 }
-func (p *P1) val() int { return 6 }
//func (t T) val() int { return 7 }
func (t *T1) val() int { return 8 }
@@ -37,8 +33,6 @@ func main() {
var ps *S1;
var i I;
var pi *I1;
-\tvar p P;
-\tvar pp *P1;
var t T;
var pt *T1;
@@ -46,8 +40,6 @@ func main() {
if ps.val() != 2 { panicln("ps.val:", ps.val()) }
if i.val() != 3 { panicln("i.val:", i.val()) }
if pi.val() != 4 { panicln("pi.val:", pi.val()) }
-\tif p.val() != 5 { panicln("p.val:", p.val()) }
-\tif pp.val() != 6 { panicln("pp.val:", pp.val()) }
//\tif t.val() != 7 { panicln("t.val:", t.val()) }
if pt.val() != 8 { panicln("pt.val:", pt.val()) }
@@ -55,8 +47,6 @@ func main() {
if val(ps) != 2 { panicln("ps.val:", val(ps)) }
if val(i) != 3 { panicln("i.val:", val(i)) }
if val(pi) != 4 { panicln("pi.val:", val(pi)) }
-\tif val(p) != 5 { panicln("p.val:", val(p)) }
-\tif val(pp) != 6 { panicln("pp.val:", val(pp)) }
//\tif val(t) != 7 { panicln("t.val:", val(t)) }
if val(pt) != 8 { panicln("pt.val:", val(pt)) }
test/method2.go
(新規ファイル)
--- /dev/null
+++ b/test/method2.go
@@ -0,0 +1,14 @@
+// errchk $G $D/$F.go
+\
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+\
+package main
+\
+type T struct {a int}
+type P *T
+type P1 *T
+\
+func (p P) val() int { return 1 } // ERROR "receiver"
+func (p *P1) val() int { return 1 } // ERROR "receiver"
コアとなるコードの解説
-
test/method.go
からの削除:type P *int
とtype P1 *int
というポインタ型のエイリアスの定義が削除されました。- これらの型をレシーバーとするメソッド
func (p P) val() int
とfunc (p *P1) val() int
の定義も削除されました。 main
関数内のこれらの型に関連する変数宣言 (var p P; var pp *P1;
) と、それらのメソッド呼び出し (p.val()
,pp.val()
,val(p)
,val(pp)
) も削除されました。- これにより、
test/method.go
は、Go言語の仕様に準拠した正しいメソッドの使用例のみをテストするファイルとなりました。
-
test/method2.go
の新規追加:- このファイルは、
// errchk $G $D/$F.go
というディレクティブで始まっています。これは、このファイルがコンパイルエラーを発生させることを期待していることを示します。$G
はGoコンパイラ、$D/$F.go
は現在のディレクトリとファイル名を指します。 type T struct {a int}
という構造体型が定義されています。type P *T
とtype P1 *T
という、構造体T
へのポインタ型のエイリアスが定義されています。func (p P) val() int { return 1 } // ERROR "receiver"
: ここで、P
型(つまり*T
型)を値レシーバーとしてメソッドを定義しようとしています。Goの仕様ではこれは不正であり、// ERROR "receiver"
コメントはコンパイラが「receiver」に関するエラーを出すことを期待しています。func (p *P1) val() int { return 1 } // ERROR "receiver"
: ここで、P1
型へのポインタ(つまり**T
型)をポインタレシーバーとしてメソッドを定義しようとしています。これもGoの仕様では不正であり、同様に「receiver」に関するエラーが期待されます。- この新しいファイルは、Go言語のメソッドレシーバーのルールに違反するコードを意図的に含み、コンパイラがそれらを正しく検出してエラーを報告するかどうかを検証するための専用のテストケースとして機能します。
- このファイルは、
-
test/golden.out
の変更:=========== ./method2.go
のセクションが追加され、その下にBUG: errchk: command succeeded unexpectedly: 6g ./method2.go
という行が追加されました。これは、method2.go
がコンパイルエラーを出すべきであり、もしコンパイルが成功してしまったら、それはerrchk
テストのバグであるということを示しています。panic on line 74 PC=xxx
がpanic on line 77 PC=xxx
に変更されたのは、test/method.go
からコードが削除されたことにより、golden.out
に記録されている他のテストのパニック発生行番号がずれたため、その調整が行われたものです。
このコミットは、Go言語の型システムとコンパイラの堅牢性を確保するための、初期段階における重要なテストの整理と強化を示しています。
関連リンク
- Go言語のメソッドに関する公式ドキュメント(Go言語のバージョンアップに伴い、当時のドキュメントとは異なる可能性がありますが、概念は共通です):
参考にした情報源リンク
- Go言語の公式ドキュメント
- Gitのdiff出力
- 一般的なGo言語のテスト慣習に関する知識