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

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

このコミットは、Go言語のパーサー(go/parserパッケージ)における不正なインポートパスのテストケースを拡張するものです。具体的には、src/pkg/go/parser/parser_test.goファイル内のimportsマップが更新され、より多様な不正なインポートパスのシナリオが追加されています。これにより、Goパーサーが様々な形式の不正なインポートパスを正しく識別し、エラーを報告できることを保証します。

コミット

commit 3a6a1f9e7ecbbb33e7d20be621e1b41ce750b30d
Author: Russ Cox <rsc@golang.org>
Date:   Thu Feb 23 14:44:53 2012 -0500

    go/parser: expand test cases for bad import
    
    R=gri
    CC=golang-dev
    https://golang.org/cl/5697047

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

https://github.com/golang/go/commit/3a6a1f9e7ecbbb33e7d20be621e1b41ce750b30d

元コミット内容

このコミットの元の内容は、Go言語のパーサーテストにおいて、不正なインポートパスに対するテストケースを拡張することです。具体的には、go/parserパッケージのテストファイルであるparser_test.go内のimportsマップに、より多くの不正なインポートパスの例を追加し、それらが正しくエラーとして扱われることを検証しています。

変更の背景

Go言語のパーサーは、ソースコードを解析し、構文木(AST: Abstract Syntax Tree)を生成する役割を担っています。このプロセスにおいて、インポートパスの構文解析は非常に重要です。Goのインポートパスは特定の規則に従う必要があり、例えば、ヌルバイト(\x00)や制御文字、特定の記号などを含めることはできません。

このコミットが行われた背景には、Goパーサーがこれらの不正なインポートパスを確実に検出し、適切なエラーを返すことを保証する必要があったと考えられます。特に、Go言語には二種類の文字列リテラル(ダブルクォートで囲まれた解釈済み文字列リテラルと、バッククォートで囲まれた生文字列リテラル)が存在するため、それぞれの形式で不正な文字が指定された場合に、パーサーがどのように振る舞うかを網羅的にテストすることが求められました。

初期のテストケースではカバーしきれていなかったエッジケースや、将来的な言語仕様の変更に備えるため、より堅牢なテストスイートを構築することが目的であったと推測されます。

前提知識の解説

Go言語のインポートパス

Go言語では、他のパッケージの機能を利用するためにimport宣言を使用します。インポートパスは、通常、パッケージのソースコードが配置されているディレクトリのパスに対応します。例えば、import "fmt"は標準ライブラリのfmtパッケージをインポートします。インポートパスには、特定の文字セットのみが許可されており、ヌルバイト、制御文字、スペース、特定の記号などは使用できません。これは、ファイルシステムパスとしての有効性や、クロスプラットフォームでの互換性を考慮した設計です。

Go言語の文字列リテラル

Go言語には、以下の2種類の文字列リテラルがあります。

  1. 解釈済み文字列リテラル (Interpreted String Literals):

    • ダブルクォート(")で囲まれます。
    • バックスラッシュエスケープシーケンス(例: \n\t\x00\uXXXX\UXXXXXXXX)が解釈されます。
    • 例: "Hello\nWorld"
  2. 生文字列リテラル (Raw String Literals):

    • バッククォート(`)で囲まれます。
    • バックスラッシュエスケープシーケンスは解釈されず、文字通りに扱われます。
    • 改行を含めることができます。
    • 例: `Hello\nWorld` は、\nを文字通りに含む文字列になります。

インポートパスは文字列リテラルとして記述されるため、これらの文字列リテラルの特性がパーサーの挙動に影響を与えます。特に、生文字列リテラルはエスケープシーケンスを解釈しないため、ヌルバイトなどの不正な文字を直接埋め込むことが可能であり、パーサーがこれを適切に処理できるかどうかのテストが重要になります。

go/parserパッケージ

go/parserパッケージは、Go言語のソースコードを解析し、抽象構文木(AST)を構築するためのGo標準ライブラリの一部です。このパッケージは、Goコンパイラ、go vetgofmtなどのGoツールチェーンの基盤となっています。ParseFile関数は、指定されたGoソースファイルを解析し、そのASTを返します。解析中に構文エラーが検出された場合、エラーが返されます。

技術的詳細

このコミットの技術的詳細は、go/parserパッケージのテストスイートの強化にあります。

変更の中心は、src/pkg/go/parser/parser_test.goファイル内のimportsというmap[string]bool型の変数です。このマップは、キーとしてインポートパスの文字列、値としてそのパスが有効(true)か無効(false)かを示すブール値を持っています。

元のimportsマップには、基本的な有効/無効なインポートパスの例が含まれていましたが、このコミットでは特に以下の種類のテストケースが追加されています。

  1. 生文字列リテラル ( ) の導入:

    • 以前はダブルクォート(")で囲まれた文字列リテラルのみがテストされていましたが、このコミットではバッククォート(`)で囲まれた生文字列リテラルも追加されました。
    • 例: `a` (有効なインポートパスの生文字列リテラル)
    • 例: `\x00` (ヌルバイトを含む不正なインポートパスの生文字列リテラル)
  2. 不正な文字の網羅的なテスト:

    • ヌルバイト (\x00)、DEL文字 (\x7f)、スペース、感嘆符 (!)、バックスラッシュ (\)、非ASCII文字(\x80\x80\xFFFD)など、インポートパスとして不正な文字を含むケースが追加されました。
    • これらの不正な文字は、エスケープシーケンス(例: "\x00")と、生文字列リテラル内で直接表現される形式(例: `\x00`)の両方でテストされています。これにより、パーサーが文字列リテラルの種類に関わらず、インポートパスの妥当性を正しく検証できることを確認しています。
  3. fmt.Sprintfのフォーマット変更:

    • テストコード内でインポート宣言を生成するfmt.Sprintfのフォーマット文字列が、"package p; import %q"から"package p; import %s"に変更されました。
    • これは、importsマップのキーが、インポートパスそのものではなく、Goのソースコード内で記述される文字列リテラル(ダブルクォートやバッククォートを含む)になったためです。%qは文字列をGoのクォートされた文字列リテラルとしてフォーマットしますが、%sは文字列をそのまま挿入します。新しいテストケースでは、マップのキー自体が既にクォートされた形式になっているため、%sが適切です。

これらの変更により、Goパーサーがインポートパスの構文規則をより厳密に、かつ多様な文字列リテラルの形式で検証できるようになったことが示されています。

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

変更はsrc/pkg/go/parser/parser_test.goファイルに集中しています。

--- a/src/pkg/go/parser/parser_test.go
+++ b/src/pkg/go/parser/parser_test.go
@@ -207,24 +207,40 @@ func TestVarScope(t *testing.T) {
 }
 
 var imports = map[string]bool{
-	"a":        true,
-	"a/b":      true,
-	"a.b":      true,
-	"m\\x61th":  true,
-	"greek/αβ": true,
-	"":         false,
-	"\\x00":     false,
-	"\\x7f":     false,
-	"a!":       false,
-	"a b":      false,
-	`a\b`:      false,
-	"`a`":      false,
-	"\\x80\\x80": false,
+	`"a"`:        true,
+	"`a`":        true,
+	`"a/b"`:      true,
+	`"a.b"`:      true,
+	`"m\x61th"`:  true,
+	`"greek/αβ"`: true,
+	`""`:         false,
+
+	// Each of these pairs tests both `` vs "" strings
+	// and also use of invalid characters spelled out as
+	// escape sequences and written directly.
+	// For example `"\x00"` tests import "\x00"
+	// while "`\x00`" tests import `<actual-NUL-byte>`.
+	`"\x00"`:     false,
+	"`\x00`":     false,
+	`"\x7f"`:     false,
+	"`\x7f`":     false,
+	`"a!"`:       false,
+	"`a!`":       false,
+	`"a b"`:      false,
+	"`a b`":      false,
+	`"a\\b"`:     false,
+	"`a\\b`":     false,
+	`"\"`a`\""`:    false,
+	"`\"a\"`":    false,
+	`"\x80\x80"`: false,
+	"`\x80\x80`": false,
+	`"\xFFFD"`:   false,
+	"`\xFFFD`":   false,
 }
 
 func TestImports(t *testing.T) {
 	for path, isValid := range imports {
-\t\tsrc := fmt.Sprintf(\"package p; import %q\", path)\n+\t\tsrc := fmt.Sprintf(\"package p; import %s\", path)\
 		_, err := ParseFile(fset, \"\", src, 0)
 		switch {
 		case err != nil && isValid:

コアとなるコードの解説

上記の差分が示すように、importsマップの定義が大幅に拡張されています。

変更前: importsマップのキーは、インポートパスの「内容」そのものでした。例えば、"a"import "a"をテストし、"\\x00"import "\x00"をテストしていました。fmt.Sprintf%qフォーマット動詞が、これらの文字列をGoの文字列リテラルとして適切にクォートしていました。

変更後: importsマップのキーは、Goのソースコード内で記述される「文字列リテラルそのもの」になりました。

  • "a""a"に、"a/b""a/b"に、といった形で、既存の有効なパスもダブルクォートで囲まれた形式に変更されました。
  • 新たにバッククォートで囲まれた生文字列リテラル(例: `a``\x00`)が追加されました。
  • 不正なインポートパスのテストケースが大幅に拡充され、ヌルバイト、DEL文字、スペース、感嘆符、バックスラッシュ、非ASCII文字など、様々な不正な文字が、ダブルクォートとバッククォートの両方のリテラル形式で網羅的にテストされています。
    • 特に注目すべきは、"\x00"`\x00`のように、同じ不正な文字をエスケープシーケンスと生文字として表現するペアが追加されている点です。これにより、パーサーが文字列リテラルの種類に関わらず、インポートパスの妥当性チェックを正確に行えるかを確認しています。
    • コメントで示されているように、"\x00"import "\x00"をテストし、`\x00`import <actual-NUL-byte>(実際のヌルバイト文字を含むインポートパス)をテストします。

また、TestImports関数内のfmt.Sprintfの呼び出しが、%qから%sに変更されています。

  • src := fmt.Sprintf("package p; import %s", path)
    • これは、importsマップのキーが既にGoの文字列リテラル形式(ダブルクォートやバッククォートを含む)になっているため、%sを使ってその文字列をそのまま挿入すればよい、という変更です。これにより、生成されるソースコードはpackage p; import "a"package p; import a``のようになります。

この変更は、Goパーサーの堅牢性を高め、より多様な(特に不正な)インポートパスのシナリオに対して、正確なエラー検出と報告ができるようにするための重要なステップです。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語のソースコード(特にgo/parserパッケージ)
  • Go言語の仕様書
  • GitHubのコミット履歴
  • Gerrit Code Review (golang.org/cl)