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

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

このコミットは、Go言語の組み込み関数(builtin functions)の呼び出し結果が使用されない場合に、コンパイラが正しくエラーを報告するかどうかを検証するための新しいテストファイル test/fixedbugs/issue4463.go を追加します。特に、ステートメントコンテキスト、go ステートメント(ゴルーチン)、および defer ステートメント内での組み込み関数の使用がテストされています。

コミット

commit dfe29798012e5b064d21263dad5faba5b04a94e4
Author: Ian Lance Taylor <iant@golang.org>
Date:   Mon Dec 3 18:49:47 2012 -0800

    test: add test for unused calls to builtin functions
    
    R=gri
    CC=golang-dev
    https://golang.org/cl/6871054

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

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

元コミット内容

test: add test for unused calls to builtin functions

R=gri
CC=golang-dev
https://golang.org/cl/6871054

変更の背景

Go言語では、一部の組み込み関数(例: append, len, cap, make, new など)は値を返しますが、その戻り値が使用されない場合、コンパイラは「結果が使用されていません (not used)」というエラーを報告します。これは、プログラマが意図せず戻り値を無視してしまうことによる潜在的なバグを防ぐための重要なチェックです。

このコミットは、Goコンパイラがこれらの「未使用の結果」を、通常のステートメントコンテキストだけでなく、go ステートメント(新しいゴルーチン内で実行される関数呼び出し)や defer ステートメント(関数の終了時に実行される関数呼び出し)内でも正しく検出できることを保証するために追加されました。特に、append のような関数は、スライスが再割り当てされる可能性があるため、その戻り値を常に使用する必要があります。このテストは、このような重要なコンパイラチェックの堅牢性を高めることを目的としています。

前提知識の解説

Go言語の組み込み関数 (Built-in Functions)

Go言語には、言語仕様の一部として定義され、特別な意味を持ついくつかの組み込み関数があります。これらは import なしで直接使用できます。大きく分けて以下の種類があります。

  • 型変換: complex, real, imag
  • メモリ管理: make (スライス、マップ、チャネルの作成), new (型のゼロ値を持つ新しいインスタンスの作成)
  • データ構造操作: append (スライスへの要素追加), cap (容量の取得), len (長さの取得), copy (スライスのコピー), delete (マップからの要素削除)
  • 並行処理: close (チャネルのクローズ)
  • エラー処理/パニック: panic (パニックの発生), recover (パニックからの回復)
  • 入出力: print, println (デバッグ用出力)
  • unsafeパッケージ: unsafe.Alignof, unsafe.Offsetof, unsafe.Sizeof (低レベルなメモリ操作)

これらの関数の中には、値を返すものと返さないものがあります。値を返す関数で、その戻り値が変数に代入されたり、他の式で使用されたりしない場合、Goコンパイラは通常「結果が使用されていません (not used)」というエラーを報告します。

未使用の結果 (Unused Results)

Go言語では、関数が値を返すにもかかわらず、その戻り値がコード内で利用されない場合、コンパイラは警告またはエラーを生成することがあります。これは、プログラマが戻り値を処理し忘れることによる論理的な誤りを防ぐためのものです。例えば、append 関数は新しいスライスを返す可能性があり、その戻り値を元のスライス変数に再代入しないと、変更が反映されないことがあります。

// 悪い例: appendの戻り値が使われていない
s := []int{1, 2, 3}
append(s, 4) // sは変更されない!

// 良い例: appendの戻り値を使っている
s := []int{1, 2, 3}
s = append(s, 4) // sは変更される

errorcheck ディレクティブ

Go言語のテストスイートでは、特定のコンパイラエラーや警告が期待されるテストケースを作成するために、// errorcheck ディレクティブが使用されます。このディレクティブは、テストファイルの先頭に記述され、そのファイルがコンパイルされたときに、指定されたエラーメッセージが期待される行で発生するかどうかをチェックします。

例えば、// ERROR "not used" というコメントがコード行の末尾にある場合、その行で「not used」という文字列を含むコンパイラエラーが発生することが期待されます。テスト実行時にこのエラーが検出されなければテストは失敗し、逆にエラーが検出されればテストは成功します。

技術的詳細

このコミットで追加されたテストは、Goコンパイラの静的解析能力を検証するものです。コンパイラは、コードの実行フローを分析し、関数呼び出しの結果が後続のコードで利用されているかどうかを判断します。

具体的には、以下のシナリオで「未使用の結果」の検出をテストしています。

  1. 通常のステートメントコンテキスト: 関数呼び出しが単独のステートメントとして存在し、その戻り値がどこにも代入されたり、式の一部として使用されたりしない場合。
  2. go ステートメント: 新しいゴルーチン内で実行される関数呼び出しの場合。ゴルーチンは非同期に実行されるため、その中で発生する「未使用の結果」も正しく検出される必要があります。
  3. defer ステートメント: 遅延実行される関数呼び出しの場合。defer された関数も、その戻り値が使用されない場合にエラーが報告されるべきです。

テストファイル issue4463.go では、append, cap, complex, imag, len, make, new, real, unsafe.Alignof, unsafe.Offsetof, unsafe.Sizeof といった、戻り値を持つ組み込み関数が意図的に未使用の状態で呼び出されています。これらの呼び出しの隣には // ERROR "not used" コメントが付加されており、コンパイラが期待通りにエラーを報告するかどうかを検証します。

一方で、close, copy, delete, panic, print, println, recover といった、戻り値を持たないか、または戻り値が通常は無視される組み込み関数もテストされています。これらの関数呼び出しには // ERROR コメントが付いておらず、コンパイラがエラーを報告しないことが期待されます。

このテストは、Goコンパイラがコードの潜在的な問題を早期に発見し、開発者がより堅牢でバグの少ないコードを書くのを助けるための重要な機能が正しく動作していることを保証します。

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

diff --git a/test/fixedbugs/issue4463.go b/test/fixedbugs/issue4463.go
new file mode 100644
index 0000000000..578173aba5
--- /dev/null
+++ b/test/fixedbugs/issue4463.go
@@ -0,0 +1,87 @@
+// errorcheck
+
+// Copyright 2012 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.
+
+// Issue 4463: test builtin functions in statement context and in
+// go/defer functions.
+
+package p
+
+import "unsafe"
+
+func F() {
+	var a []int
+	var c chan int
+	var m map[int]int
+	var s struct{ f int }
+
+	append(a, 0)			// ERROR "not used"
+	cap(a)				// ERROR "not used"
+	complex(1, 2)			// ERROR "not used"
+	imag(1i)			// ERROR "not used"
+	len(a)				// ERROR "not used"
+	make([]int, 10)			// ERROR "not used"
+	new(int)			// ERROR "not used"
+	real(1i)			// ERROR "not used"
+	unsafe.Alignof(a)		// ERROR "not used"
+	unsafe.Offsetof(s.f)		// ERROR "not used"
+	unsafe.Sizeof(a)		// ERROR "not used"
+
+	close(c)
+	copy(a, a)
+	delete(m, 0)
+	panic(0)
+	print("foo")
+	println("bar")
+	recover()
+
+	(close(c))
+	(copy(a, a))
+	(delete(m, 0))
+	(panic(0))
+	(print("foo"))
+	(println("bar"))
+	(recover())
+
+	go append(a, 0)			// ERROR "not used"
+	go cap(a)			// ERROR "not used"
+	go complex(1, 2)		// ERROR "not used"
+	go imag(1i)			// ERROR "not used"
+	go len(a)			// ERROR "not used"
+	go make([]int, 10)		// ERROR "not used"
+	go new(int)			// ERROR "not used"
+	go real(1i)			// ERROR "not used"
+	go unsafe.Alignof(a)		// ERROR "not used"
+	go unsafe.Offsetof(s.f)		// ERROR "not used"
+	go unsafe.Sizeof(a)		// ERROR "not used"
+
+	go close(c)
+	go copy(a, a)
+	go delete(m, 0)
+	go panic(0)
+	go print("foo")
+	go println("bar")
+	go recover()
+
+	defer append(a, 0)		// ERROR "not used"
+	defer cap(a)			// ERROR "not used"
+	defer complex(1, 2)		// ERROR "not used"
+	defer imag(1i)			// ERROR "not used"
+	defer len(a)			// ERROR "not used"
+	defer make([]int, 10)		// ERROR "not used"
+	defer new(int)			// ERROR "not used"
+	defer real(1i)			// ERROR "not used"
+	defer unsafe.Alignof(a)		// ERROR "not used"
+	defer unsafe.Offsetof(s.f)	// ERROR "not used"
+	defer unsafe.Sizeof(a)		// ERROR "not used"
+
+	defer close(c)
+	defer copy(a, a)
+	defer delete(m, 0)
+	defer panic(0)
+	defer print("foo")
+	defer println("bar")
+	defer recover()
+}

コアとなるコードの解説

追加された test/fixedbugs/issue4463.go ファイルは、Goコンパイラが組み込み関数の未使用の結果を正しく検出するかどうかを検証するための包括的なテストケースです。

  1. // errorcheck ディレクティブ: ファイルの先頭にある // errorcheck は、このファイルがコンパイルされたときに、特定のコンパイラエラーが期待されることを示します。

  2. 変数宣言: F() 関数内で、テストに使用する様々な型の変数が宣言されています。

    • var a []int: スライス
    • var c chan int: チャネル
    • var m map[int]int: マップ
    • var s struct{ f int }: 構造体(unsafe.Offsetof のテスト用)
  3. ステートメントコンテキストでのテスト: 最初のブロックでは、組み込み関数が通常のステートメントとして呼び出されています。

    • append(a, 0): append は新しいスライスを返す可能性があるため、その結果が使用されない場合はエラーが期待されます。

    • cap(a), complex(1, 2), imag(1i), len(a), make([]int, 10), new(int), real(1i): これらも値を返す関数であり、結果が使用されない場合はエラーが期待されます。

    • unsafe.Alignof(a), unsafe.Offsetof(s.f), unsafe.Sizeof(a): unsafe パッケージの関数も値を返すため、同様にエラーが期待されます。

    • これらの行の隣には // ERROR "not used" コメントがあり、コンパイラが「not used」エラーを報告することを期待しています。

    • close(c), copy(a, a), delete(m, 0), panic(0), print("foo"), println("bar"), recover(): これらの関数は戻り値を持たないか、または戻り値が通常は無視されるため、エラーは期待されません。

    • 括弧で囲まれた呼び出し (close(c)) なども、構文的に有効であり、エラーを報告しないことが期待されます。

  4. go ステートメントでのテスト: 次のブロックでは、同じ組み込み関数が go キーワードを使ってゴルーチン内で呼び出されています。

    • go append(a, 0) など、戻り値を持つ関数がゴルーチン内で未使用の場合も、コンパイラは「not used」エラーを報告することが期待されます。
    • go close(c) など、戻り値を持たない関数はエラーを報告しないことが期待されます。
  5. defer ステートメントでのテスト: 最後のブロックでは、組み込み関数が defer キーワードを使って遅延実行されるように呼び出されています。

    • defer append(a, 0) など、戻り値を持つ関数が defer された場合も、コンパイラは「not used」エラーを報告することが期待されます。
    • defer close(c) など、戻り値を持たない関数はエラーを報告しないことが期待されます。

このテストは、Goコンパイラが異なる実行コンテキスト(通常のステートメント、ゴルーチン、遅延実行)においても、組み込み関数の戻り値の利用状況を正確に分析し、プログラマに潜在的なバグを警告できることを保証します。

関連リンク

参考にした情報源リンク

  • Go言語の組み込み関数に関する公式ドキュメント: https://go.dev/ref/spec#Built-in_functions
  • Go言語の unsafe パッケージに関する公式ドキュメント: https://pkg.go.dev/unsafe
  • Go言語のテストにおける errorcheck ディレクティブに関する情報(Goのソースコードやテストガイドラインに記載されていることが多い)