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

[インデックス 12193] ファイルの概要

このコミットは、Go言語のテストスイートにおけるリネームテストの修正に関するものです。具体的には、以前のリネームテストの修正が不完全であり、iota の誤用によってテストが正しく機能していなかった問題を解決しています。このコミットにより、リネームテストが再び意図した通りに動作するようになります。

コミット

commit d45ee4cb5f44b2ebc79a65f1fcbc4d3f81fbdd40
Author: Rob Pike <r@golang.org>
Date:   Fri Feb 24 15:06:32 2012 +1100

    test: fix the fix of the rename tests.
    Now they actually test again instead of just setting iota to zero.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/5700058

 test/rename.go  | 79 +++++++++++++++++++++++++++++----------------------------
 test/rename1.go | 77 +++++++++++++++++++++++++++----------------------------
 2 files changed, 78 insertions(+), 78 deletions(-)

diff --git a/test/rename.go b/test/rename.go
index 817a8de79a..e544274553 100644
--- a/test/rename.go
+++ b/test/rename.go
@@ -56,43 +56,44 @@ func main() {
 }
 
 const (
-	append = iota
-	bool
-	byte
-	complex
-	complex64
-	complex128
-	cap
-	close
-	delete
-	error
-	false
-	float32
-	float64
-	imag
-	int
-	int8
-	int16
-	int32
-	int64
-	len
-	make
-	new
-	nil
-	panic
-	print
-	println
-	real
-	recover
-	rune
-	string
-	true
-	uint
-	uint8
-	uint16
-	uint32
-	uint64
-	uintptr
-	NUM
-	iota = 0
+	// cannot use iota here, because iota = 38 below
+	append     = 1
+	bool       = 2
+	byte       = 3
+	complex    = 4
+	complex64  = 5
+	complex128 = 6
+	cap        = 7
+	close      = 8
+	delete     = 9
+	error      = 10
+	false      = 11
+	float32    = 12
+	float64    = 13
+	imag       = 14
+	int        = 15
+	int8       = 16
+	int16      = 17
+	int32      = 18
+	int64      = 19
+	len        = 20
+	make       = 21
+	new        = 22
+	nil        = 23
+	panic      = 24
+	print      = 25
+	println    = 26
+	real       = 27
+	recover    = 28
+	rune       = 29
+	string     = 30
+	true       = 31
+	uint       = 32
+	uint8      = 33
+	uint16     = 34
+	uint32     = 35
+	uint64     = 36
+	uintptr    = 37
+	iota       = 38
+	NUM        = 39
 )
diff --git a/test/rename1.go b/test/rename1.go
index 48262fd2b5..53db68de16 100644
--- a/test/rename1.go
+++ b/test/rename1.go
@@ -19,43 +19,42 @@ func main() {\n }\n 
 const (\n-\tappend = iota\n-\tbool\n-\tbyte\n-\tcomplex\n-\tcomplex64\n-\tcomplex128\n-\tcap\n-\tclose\n-\tdelete\n-\terror\n-\tfalse\n-\tfloat32\n-\tfloat64\n-\timag\n-\tint\n-\tint8\n-\tint16\n-\tint32\n-\tint64\n-\tlen\n-\tmake\n-\tnew\n-\tnil\n-\tpanic\n-\tprint\n-\tprintln\n-\treal\n-\trecover\n-\trune\n-\tstring\n-\ttrue\n-\tuint\n-\tuint8\n-\tuint16\n-\tuint32\n-\tuint64\n-\tuintptr\n-\tNUM\n-\tiota = "123"\n+\tappend     = 1\n+\tbool       = 2\n+\tbyte       = 3\n+\tcomplex    = 4\n+\tcomplex64  = 5\n+\tcomplex128 = 6\n+\tcap        = 7\n+\tclose      = 8\n+\tdelete     = 9\n+\terror      = 10\n+\tfalse      = 11\n+\tfloat32    = 12\n+\tfloat64    = 13\n+\timag       = 14
+\tint        = 15
+\tint8       = 16
+\tint16      = 17
+\tint32      = 18
+\tint64      = 19
+\tlen        = 20
+\tmake       = 21
+\tnew        = 22
+\tnil        = 23
+\tpanic      = 24
+\tprint      = 25
+\tprintln    = 26
+\treal       = 27
+\trecover    = 28
+\trune       = 29
+\tstring     = 30
+\ttrue       = 31
+\tuint       = 32
+\tuint8      = 33
+\tuint16     = 34
+\tuint32     = 35
+\tuint64     = 36
+\tuintptr    = 37
+\tiota       = "38"\n )

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/d45ee4cb5f44b2ebc79a65f1fcbc4d3f81fbdd40

元コミット内容

このコミットの元となった問題は、Go言語のリネームテストが正しく機能していなかったことにあります。コミットメッセージにある「fix the fix」という表現から、以前にもこのテストに関する修正が行われたものの、それが不完全であったか、あるいは新たな問題を引き起こしたことが示唆されます。具体的には、iota の誤った使用方法により、テストが意図した定数値を生成せず、常にゼロに設定されてしまっていたようです。これにより、テストが本来検出するはずのエラーや挙動の検証が行われず、テストとしての意味をなしていませんでした。

変更の背景

Go言語のコンパイラやツールチェインの開発において、リファクタリングやコードの変更は頻繁に行われます。その際、既存のコードのセマンティクス(意味)が変更されないことを保証するために、リネーム(名前変更)のテストは非常に重要です。例えば、変数名や関数名が変更された際に、その変更がコードの他の部分に予期せぬ影響を与えないか、あるいはコンパイラが正しく新しい名前を認識するかなどを検証します。

このコミットが行われた背景には、以前のリネームテストの修正が不適切であったという問題があります。テストコード内で iota というGo言語の特殊な定数生成メカニズムが誤って使用されており、その結果、テストが期待する定数値ではなく、常に 0 を生成していました。これにより、テストは常に同じ(おそらく無効な)値で実行され、実際のリネーム操作が正しく行われているかを検証できていませんでした。このコミットは、この「修正の修正」を行い、リネームテストが再び有効に機能するようにすることを目的としています。

前提知識の解説

Go言語の constiota

Go言語では、const キーワードを使用して定数を宣言します。複数の定数をまとめて宣言する場合、定数ブロックを使用できます。

const (
    A = 1
    B = 2
    C = 3
)

iota は、Go言語の定数宣言で使用される特殊な識別子で、連続する整数値を自動的に生成するために使われます。iotaconst ブロック内で使用され、その値は const ブロックの開始時に 0 にリセットされ、以降の定数宣言ごとに 1 ずつ増加します。

例:

const (
    A = iota // A = 0
    B        // B = 1 (iota は自動的にインクリメントされる)
    C        // C = 2
)

iota は、定数ブロック内で明示的に値を割り当てたり、iota を含む行がスキップされたりすると、その挙動が変わることがあります。特に、iota を含む行の後に iota に明示的に値を割り当てると、その時点での iota の値が上書きされ、以降の iota の挙動に影響を与えます。

Go言語のテストフレームワーク

Go言語には、標準ライブラリとして testing パッケージが提供されており、これを用いてユニットテストやベンチマークテストを記述します。テストファイルは通常、テスト対象のファイルと同じディレクトリに _test.go というサフィックスを付けて配置されます。テスト関数は Test で始まり、*testing.T 型の引数を取ります。

リネームテストのような特定の言語機能のテストは、コンパイラやツールの正確性を保証するために非常に重要です。これらのテストは、言語仕様の変更や実装のバグによって、既存のコードの挙動が変わらないことを確認するために実行されます。

技術的詳細

このコミットの核心は、test/rename.gotest/rename1.go という2つのテストファイルにおける const ブロック内の iota の誤用を修正することです。

元のコードでは、以下のような const ブロックがありました。

const (
    append = iota
    bool
    // ... 多数の定数 ...
    uintptr
    NUM
    iota = 0 // ここが問題
)

ここで問題となるのは、const ブロックの最後に iota = 0 という行があることです。Go言語の iota の挙動として、const ブロック内で iota に明示的に値を割り当てると、その時点での iota の値がその割り当てられた値に設定されます。そして、その行以降の iota の値は、その新しい値からインクリメントされていきます。

しかし、このケースでは iota = 0const ブロックの「最後」に配置されています。これにより、iota0 にリセットされますが、その後に続く定数宣言がないため、この iota = 0 の行自体が、その前の iota を使用して宣言されたすべての定数に影響を与えることはありません。

本当の問題点: この iota = 0 の行が問題なのは、iotaconst ブロックの開始時に 0 にリセットされ、各行でインクリメントされるという性質を悪用している点です。もし、この iota = 0const ブロックの途中にあった場合、それ以降の iota の値は 0 から再開されます。しかし、このコードでは iota = 0 がブロックの最後にあり、その前の iota を使った定数宣言は、その時点での iota の値(0, 1, 2, ...)を正しく受け取ります。

コミットメッセージの「instead of just setting iota to zero」という記述から推測すると、以前の「fix」では、この iota = 0 が何らかの形で、その前の定数宣言に影響を与え、すべての定数が 0 になってしまっていた、あるいはテストが 0 を期待するようなロジックになっていた可能性があります。

実際の挙動の推測: Go言語の iota の挙動として、const ブロック内で iota に明示的に値を割り当てると、その行の iota の値がその割り当てられた値になり、次の行から再びインクリメントが始まります。しかし、iota = 0const ブロックの最後にあった場合、その前の iota を使った定数宣言は、その時点での iota の値(0, 1, 2, ...)を正しく受け取ります。

このコミットの修正は、iota の自動インクリメントに依存するのではなく、各定数に明示的に整数値を割り当てることで、この問題を回避しています。

const (
    // cannot use iota here, because iota = 38 below
    append     = 1
    bool       = 2
    // ...
    uintptr    = 37
    iota       = 38 // test/rename.go
    NUM        = 39
)

そして、test/rename1.go では iota = "38" と文字列を割り当てています。これは、iota が整数型であるべきところを文字列にすることで、コンパイルエラーを意図的に引き起こし、リネームテストが特定の不正なケースを検出できるようにするためのものと考えられます。つまり、iota が数値として扱われるべき場所で文字列が割り当てられた場合に、コンネームツールが正しくエラーを報告するかどうかをテストしている可能性があります。

この修正により、各定数は明確な数値を持つようになり、iota の挙動に起因する不具合が解消され、テストが期待通りに機能するようになります。

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

変更は test/rename.gotest/rename1.go の2つのファイルにわたっています。

test/rename.go の変更

--- a/test/rename.go
+++ b/test/rename.go
@@ -56,43 +56,44 @@ func main() {
 }
 
 const (
-	append = iota
-	bool
-	byte
-	complex
-	complex64
-	complex128
-	cap
-	close
-	delete
-	error
-	false
-	float32
-	float64
-	imag
-	int
-	int8
-	int16
-	int32
-	int64
-	len
-	make
-	new
-	nil
-	panic
-	print
-	println
-	real
-	recover
-	rune
-	string
-	true
-	uint
-	uint8
-	uint16
-	uint32
-	uint64
-	uintptr
-	NUM
-	iota = 0
+	// cannot use iota here, because iota = 38 below
+	append     = 1
+	bool       = 2
+	byte       = 3
+	complex    = 4
+	complex64  = 5
+	complex128 = 6
+	cap        = 7
+	close      = 8
+	delete     = 9
+	error      = 10
+	false      = 11
+	float32    = 12
+	float64    = 13
+	imag       = 14
+	int        = 15
+	int8       = 16
+	int16      = 17
+	int32      = 18
+	int64      = 19
+	len        = 20
+	make       = 21
+	new        = 22
+	nil        = 23
+	panic      = 24
+	print      = 25
+	println    = 26
+	real       = 27
+	recover    = 28
+	rune       = 29
+	string     = 30
+	true       = 31
+	uint       = 32
+	uint8      = 33
+	uint16     = 34
+	uint32     = 35
+	uint64     = 36
+	uintptr    = 37
+	iota       = 38
+	NUM        = 39
 )

test/rename1.go の変更

--- a/test/rename1.go
+++ b/test/rename1.go
@@ -19,43 +19,42 @@ func main() {\n }\n 
 const (\n-\tappend = iota
-\tbool
-\tbyte
-\tcomplex
-\tcomplex64
-\tcomplex128
-\tcap
-\tclose
-\tdelete
-\terror
-\tfalse
-\tfloat32
-\tfloat64
-\timag
-\tint
-\tint8
-\tint16
-\tint32
-\tint64
-\tlen
-\tmake
-\tnew
-\tnil
-\tpanic
-\tprint
-\tprintln
-\treal
-\trecover
-\trune
-\tstring
-\ttrue
-\tuint
-\tuint8
-\tuint16
-\tuint32
-\tuint64
-\tuintptr
-\tNUM
-\tiota = "123"\n+\tappend     = 1
+\tbool       = 2
+\tbyte       = 3
+\tcomplex    = 4
+\tcomplex64  = 5
+\tcomplex128 = 6
+\tcap        = 7
+\tclose      = 8
+\tdelete     = 9
+\terror      = 10
+\tfalse      = 11
+\tfloat32    = 12
+\tfloat64    = 13
+\timag       = 14
+\tint        = 15
+\tint8       = 16
+\tint16      = 17
+\tint32      = 18
+\tint64      = 19
+\tlen        = 20
+\tmake       = 21
+\tnew        = 22
+\tnil        = 23
+\tpanic      = 24
+\tprint      = 25
+\tprintln    = 26
+\treal       = 27
+\trecover    = 28
+\trune       = 29
+\tstring     = 30
+\ttrue       = 31
+\tuint       = 32
+\tuint8      = 33
+\tuint16     = 34
+\tuint32     = 35
+\tuint64     = 36
+\tuintptr    = 37
+\tiota       = "38"\n )

コアとなるコードの解説

両方のファイルで、const ブロック内の定数宣言が iota を使用した自動割り当てから、明示的な数値割り当てに変更されています。

変更前: append = iota から始まり、各定数が iota の自動インクリメントされた値を受け取ります。そして、ブロックの最後に iota = 0 または iota = "123" がありました。

test/rename.goiota = 0 は、その行自体が iota の値を 0 に設定しますが、その後に続く定数宣言がないため、その前の定数には影響を与えません。しかし、この記述がテストの意図を曖昧にし、あるいは以前の修正で何らかの副作用を引き起こしていた可能性があります。コミットメッセージの「instead of just setting iota to zero」という部分から、以前の修正が iota0 に設定することでテストを無効化していたことが示唆されます。

test/rename1.goiota = "123" は、iota が整数型であるにもかかわらず文字列を割り当てているため、コンパイルエラーを引き起こすはずです。これは、リネームツールがこのような型不一致を正しく検出できるかをテストするための意図的なエラーケースと考えられます。

変更後: すべての定数(append から uintptr まで)に 1 から 37 までの明示的な整数値が割り当てられています。これにより、iota の自動インクリメントに依存することなく、各定数が確実に意図した値を持つようになります。

  • test/rename.go では、iota = 38 と明示的に数値が割り当てられています。これは、iota という名前の定数自体に 38 という値を割り当てることを意味します。コメント // cannot use iota here, because iota = 38 below は、この const ブロック内で iota を使って他の定数を宣言すると、この iota = 38 の行によって iota の値が 38 にリセットされてしまい、期待する連続値が得られなくなることを示唆しています。
  • test/rename1.go では、iota = "38" と文字列が割り当てられています。これは、前述の通り、リネームツールが型エラーを正しく処理できるかをテストするためのものです。

この修正により、テストは iota の複雑な挙動に左右されることなく、常に正しい定数値で実行されるようになります。これにより、リネームテストがGo言語のコンパイラやツールの正確性をより確実に検証できるようになります。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (上記「関連リンク」に記載)
  • Gerrit Change 5700058: https://go-review.googlesource.com/c/go/+/5700058 (このコミットのGerrit上のレビューページ)
  • Go言語の iota に関する一般的な解説記事 (Web検索による)