[インデックス 14080] ファイルの概要
このコミットは、Go言語の実験的な型チェッカーパッケージ exp/types/staging
におけるテストドライバーの追加と改善に関するものです。特に、check_test.go
が複数のエラーを同じ位置で処理できるように変更され、開発中のテストの使いやすさが向上しています。
コミット
- コミットハッシュ:
328f0e7f2efd9ad96b4c09119a18e622e74b3802
- 作者: Robert Griesemer gri@golang.org
- 日付: Sun Oct 7 18:00:56 2012 -0700
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/328f0e7f2efd9ad96b4c09119a18e622e74b3802
元コミット内容
exp/types/staging: test drivers
This code has been reviewed before. The most significant
change is to check_test which now can handle more than
one error at the same error position (due to spurious
errors - should not happen in praxis once error handling
has been fine-tuned). This change makes check_test easier
to use during development.
R=rsc
CC=golang-dev
https://golang.org/cl/6584057
変更の背景
このコミットは、Go言語の型チェッカーの初期開発段階におけるテストインフラの強化を目的としています。exp/types/staging
パッケージは、Go言語の型システムと型チェックロジックを実験的に開発するためのステージングエリアでした。型チェッカーのような複雑なシステムでは、正確なエラー報告が非常に重要です。
元のコミットメッセージによると、このコードは以前にレビューされており、最も重要な変更点は check_test.go
にあります。以前のバージョンでは、同じソースコードの位置で複数のエラーが発生した場合(特に開発初期段階で「スプリアスエラー(偽のエラー)」が発生しやすい状況)、テストハーネスが適切に処理できない可能性がありました。この制限は、開発者が型チェッカーのロジックをデバッグし、エラー処理を微調整する際に不便をもたらしていました。
このコミットの目的は、check_test.go
を改良し、同じエラー位置で複数のエラーを処理できるようにすることで、開発中のテストの柔軟性と使いやすさを向上させることにあります。これにより、型チェッカーのエラー報告がより堅牢になり、開発者はより効率的に作業を進めることができるようになります。
前提知識の解説
このコミットを理解するためには、以下のGo言語の概念と標準ライブラリに関する知識が必要です。
-
Go言語の
exp
パッケージ:exp
(experimental)パッケージは、Go言語の標準ライブラリの一部として将来的に組み込まれる可能性のある、実験的なAPIや機能を含むパッケージです。これらのパッケージは、まだ安定しておらず、APIが変更される可能性があります。exp/types/staging
は、Go言語の型チェッカーの初期開発バージョンをホストするために使用されていました。 -
Go言語の型システムと型チェッカー: Go言語は静的型付け言語であり、プログラムの実行前に型チェックが行われます。型チェッカーは、プログラムが型規則に準拠しているかを確認し、型エラーを検出します。これは、コンパイル時に多くのバグを捕捉するために不可欠なプロセスです。
-
抽象構文木 (AST - Abstract Syntax Tree): コンパイラやインタープリタは、ソースコードを直接処理するのではなく、まずソースコードを抽象構文木(AST)と呼ばれるツリー構造に変換します。ASTは、プログラムの構造を抽象的に表現したもので、型チェッカーはASTを走査して型情報を収集し、型規則を適用します。
-
go/token
パッケージ: Go言語のソースコードを解析する際に使用されるトークン(キーワード、識別子、演算子など)と、それらのトークンのソースコード上の位置(ファイル名、行番号、列番号、オフセット)を管理するためのパッケージです。token.FileSet
は、複数のソースファイルをまとめて管理し、正確な位置情報を提供します。 -
go/scanner
パッケージ: Go言語のソースコードをトークンに分割する字句解析(スキャン)を行うパッケージです。エラーが発生した場合、scanner.ErrorList
にエラー情報を収集します。 -
go/parser
パッケージ: Go言語のソースコードを解析し、ASTを構築する構文解析(パース)を行うパッケージです。parser.ParseFile
関数は、指定されたファイルからASTを生成します。 -
go/ast
パッケージ: Go言語のASTのノード構造を定義するパッケージです。ASTの各要素(宣言、式、文など)は、ast
パッケージで定義された型として表現されます。ast.Inspect
関数は、ASTを走査するためのユーティリティです。 -
テストハーネスと期待されるエラー: ソフトウェアテストにおいて、テストハーネスはテストの実行環境を提供し、テストケースの実行を管理するフレームワークです。このコミットで言及されているテストハーネスは、型チェッカーが特定のエラーを正しく報告するかどうかを検証するために設計されています。 「期待されるエラー」とは、テスト対象のコードが意図的にエラーを発生させるように記述されており、そのエラーが型チェッカーによって正しく検出され、報告されることをテストで確認するものです。テストファイル内に特定のコメント形式(例:
/* ERROR "rx" */
)で期待されるエラーメッセージの正規表現を記述することで、テストハーネスが自動的に検証を行います。
技術的詳細
このコミットは、Go言語の型チェッカー exp/types/staging
パッケージのテストインフラストラクチャに焦点を当てています。具体的には、以下の3つの新しいテストファイルが追加されています。
-
src/pkg/exp/types/staging/check_test.go
: このファイルは、型チェッカーのテストハーネスの主要な実装を含んでいます。その主な機能は以下の通りです。- テストファイルの読み込みと解析: テストデータディレクトリ (
testdata/
) にあるGoソースファイル(.src
拡張子を持つ)を読み込み、go/parser
を使用してASTを構築します。これらのファイルは、gofmt
が変更しないように.go
ではなく.src
拡張子を使用しています。 - 期待されるエラーの収集: テストファイル内に記述された
/* ERROR "rx" */
形式のコメントを解析し、期待されるエラーメッセージの正規表現と、それが期待されるソースコード上の位置を収集します。errRx
という正規表現 (^/\* *ERROR *\"([^\"]*)\" *\*/$
) を使用してコメントをマッチングします。 - エラーの検証: 型チェッカーが報告した実際のエラーメッセージと、収集した期待されるエラーメッセージを比較します。
eliminate
関数は、報告されたエラーが期待されるエラーリストのいずれかにマッチした場合、その期待されるエラーをリストから削除します。 - 複数エラーの処理: 最も重要な変更点として、
check_test.go
は同じソースコード位置で複数のエラーを処理できるようになりました。これは、expectedErrors
関数がmap[token.Pos][]string
を使用して、1つの位置に対して複数の期待されるエラーメッセージ(正規表現)をリストとして保持することで実現されています。eliminate
関数は、報告されたエラーがこのリスト内のいずれかの正規表現にマッチすれば、そのエラーを処理済みと見なします。これにより、開発中に発生する可能性のある「スプリアスエラー」に対応し、テストの柔軟性を高めています。 - テスト実行:
TestCheck
関数は、定義されたテストケース (tests
変数) をループし、各テストケースに対してcheckFiles
関数を呼び出して型チェックとエラー検証を実行します。
- テストファイルの読み込みと解析: テストデータディレクトリ (
-
src/pkg/exp/types/staging/resolver_test.go
: このファイルは、識別子の解決(特に修飾識別子、例:fmt.Println
のfmt
やPrintln
)に関するテストを含んでいます。ResolveQualifiedIdents
関数: この関数は、パッケージのASTを走査し、修飾識別子(例:pkg.Name
)のセレクタ部分(Name
)を、対応するパッケージスコープ内のオブジェクトに解決します。これは、型チェックの前の段階で、名前解決が正しく行われることを保証するために重要です。- テストケース:
sources
変数にGoコードのスニペットが定義されており、これらのコードが正しく解析され、識別子が解決されることをTestResolveQualifiedIdents
関数で検証します。
-
src/pkg/exp/types/staging/types_test.go
: このファイルは、型チェッカーがGo言語の様々な型(基本型、配列、スライス、構造体、ポインタ、関数、インターフェース、マップ、チャネルなど)を正しく表現し、処理できることを検証するためのテストを含んでいます。makePkg
関数: 与えられたソースコードからパッケージを構築し、型チェックを実行するヘルパー関数です。testTypes
変数: 様々なGoの型定義とその文字列表現のペアが定義されており、型チェッカーがこれらの型を正しく解析し、内部的に表現できることをTestTypes
関数で検証します。testExprs
変数: 様々なGoの式とその文字列表現のペアが定義されており、型チェッカーがこれらの式を正しく解析し、内部的に表現できることをTestExprs
関数で検証します。
これらのテストファイルは、Go言語の型チェッカーが正しく機能していることを多角的に検証するための基盤を構築しています。特に check_test.go
の改良は、開発者が型チェッカーのバグを特定し、修正するプロセスを大幅に効率化することを目的としています。
コアとなるコードの変更箇所
このコミットでは、以下の3つの新しいファイルが追加されています。
-
src/pkg/exp/types/staging/check_test.go
: 型チェッカーのテストハーネスの実装。特に、テストファイル内の/* ERROR "rx" */
コメントを解析し、期待されるエラーを検証するロジックが含まれています。同じ位置での複数エラー処理が改善されました。 -
src/pkg/exp/types/staging/resolver_test.go
: 修飾識別子(例:fmt.Println
)の名前解決に関するテスト。パッケージのASTを走査し、セレクタが正しいオブジェクトに解決されることを確認します。 -
src/pkg/exp/types/staging/types_test.go
: Go言語の様々な型(基本型、複合型、関数型など)や式が型チェッカーによって正しく処理されることを検証するテスト。
コアとなるコードの解説
src/pkg/exp/types/staging/check_test.go
の主要な変更点と機能
このファイルは、型チェッカーのテストフレームワークの中核をなします。
-
expectedErrors
関数: この関数は、テストファイル内の/* ERROR "rx" */
コメントをスキャンし、期待されるエラーの正規表現を収集します。var errRx = regexp.MustCompile(`^/\* *ERROR *\"([^\"]*)\" *\*/$`) func expectedErrors(t *testing.T, testname string, files map[string]*ast.File) map[token.Pos][]string { errors := make(map[token.Pos][]string) // ... (ファイル読み込みとスキャンロジック) ... case token.COMMENT: s := errRx.FindStringSubmatch(lit) if len(s) == 2 { list := errors[prev] errors[prev] = append(list, string(s[1])) // 同じ位置に複数のエラーを追加 } // ... return errors }
ここで注目すべきは、
errors
マップの型がmap[token.Pos][]string
である点です。これにより、prev
(エラーが期待されるトークンの位置)をキーとして、複数のエラーメッセージの正規表現をスライス ([]string
) として格納できます。これが、同じ位置で複数のエラーを処理できるようになった主要な変更点です。 -
eliminate
関数: この関数は、型チェッカーが報告した実際のエラー (errors
引数) を受け取り、それがexpected
マップに格納されている期待されるエラーのいずれかにマッチするかどうかを検証します。func eliminate(t *testing.T, expected map[token.Pos][]string, errors error) { // ... for _, error := range errors.(scanner.ErrorList) { pos := getPos(error.Pos.Filename, error.Pos.Offset) list := expected[pos] // その位置で期待されるエラーのリストを取得 index := -1 for i, msg := range list { rx, err := regexp.Compile(msg) if err != nil { /* ... */ } if match := rx.MatchString(error.Msg); match { index = i // マッチするエラーが見つかった break } } if index >= 0 { // マッチしたエラーをリストから削除 n := len(list) - 1 if n > 0 { list[index] = list[n] expected[pos] = list[:n] } else { delete(expected, pos) // リストが空になったらマップからエントリを削除 } } else { t.Errorf("%s: no error expected: %q", error.Pos, error.Msg) } } }
eliminate
関数は、報告されたエラーが期待されるエラーリスト内のいずれかの正規表現にマッチした場合、その期待されるエラーをリストから削除します。これにより、テストの最後にexpected
マップが空であれば、すべての期待されるエラーが報告され、余分なエラーが報告されていないことが確認できます。 -
checkFiles
関数: この関数は、テストケースのGoソースファイルを解析し、識別子解決、型チェックの各段階で発生するエラーをeliminate
関数を使って検証します。func checkFiles(t *testing.T, testname string, testfiles []string) { files, err := parseFiles(t, testname, testfiles) errors := expectedErrors(t, testname, files) // 期待されるエラーを収集 eliminate(t, errors, err) // パーサーからのエラーを検証 pkg, err := ast.NewPackage(fset, files, GcImport, Universe) eliminate(t, errors, err) // 識別子解決からのエラーを検証 var list scanner.ErrorList errh := func(pos token.Pos, msg string) { list.Add(fset.Position(pos), msg) } err = Check(fset, pkg, errh, nil) eliminate(t, errors, list) // 型チェッカーからのエラーを検証 if len(errors) > 0 { t.Errorf("%s: %d errors not reported:", testname, len(errors)) // ... 報告されなかったエラーを出力 ... } }
この関数は、パーサー、識別子解決、型チェッカーの各段階で発生するエラーを順次検証し、最終的にすべての期待されるエラーが報告されたことを確認します。
src/pkg/exp/types/staging/resolver_test.go
の主要な機能
このファイルは、GoのASTにおける修飾識別子の解決をテストします。
ResolveQualifiedIdents
関数: この関数は、ast.SelectorExpr
(例:pkg.Name
)を走査し、セレクタのSel
部分が正しいast.Object
に解決されていることを確認します。これは、型チェックの前にシンボル解決が正しく行われることを保証するために重要です。
src/pkg/exp/types/staging/types_test.go
の主要な機能
このファイルは、型チェッカーがGoの様々な型や式を正しく処理できることを検証します。
-
TestTypes
関数:testTypes
に定義されたGoの型定義文字列を解析し、型チェッカーがそれらを正しく内部表現に変換できることを確認します。例えば、"[]int"
が正しくスライス型として認識されるかなどをテストします。 -
TestExprs
関数:testExprs
に定義されたGoの式文字列を解析し、型チェッカーがそれらを正しくASTとして構築し、型情報を付与できることを確認します。例えば、"x + y * 10"
のような複雑な式が正しく解析されるかなどをテストします。
これらの新しいテストファイルは、exp/types/staging
パッケージの堅牢性を高め、Go言語の型チェッカーの継続的な開発と改善をサポートするための重要な基盤となります。
関連リンク
- GitHub上のコミットページ: https://github.com/golang/go/commit/328f0e7f2efd9ad96b4c09119a18e622e74b3802
- Gerrit Code Review (元の変更リスト): https://golang.org/cl/6584057
参考にした情報源リンク
- Go言語の公式ドキュメント(
go/token
,go/scanner
,go/parser
,go/ast
パッケージに関する情報) - Go言語の
exp
パッケージに関する一般的な情報 - 型チェッカー、AST、コンパイラの基本概念に関する一般的なプログラミング知識