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

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

このコミットは、Go言語のパーサー(go/parserパッケージ)におけるエラーメッセージの改善と、それに関連するテストケースの追加を行っています。具体的には、不正なショート変数宣言(:=)に対するエラーメッセージをより分かりやすく修正し、その変更を検証するためのテストを追加しています。

変更されたファイルは以下の2つです。

  • src/pkg/go/parser/parser.go: パーサーの主要なロジックが含まれるファイルで、エラーメッセージの修正が行われました。
  • src/pkg/go/parser/short_test.go: パーサーのテストケースが含まれるファイルで、新しいエラーメッセージを検証するためのテストが追加されました。

コミット

このコミットは、Go言語のパーサーが不正な宣言を検出した際のエラーメッセージを改善することを目的としています。特に、ショート変数宣言(:=)の左辺に識別子ではないものが来た場合に表示されるエラーメッセージを、「identifier」から「identifier on left side of :=」へと具体化しています。これにより、開発者がエラーの原因をより迅速に特定できるようになります。

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

https://github.com/golang/go/commit/82accf44bffcb4834c6ec794596fc8b335f99c2b

元コミット内容

commit 82accf44bffcb4834c6ec794596fc8b335f99c2b
Author: Robert Griesemer <gri@golang.org>
Date:   Wed Jan 9 11:31:57 2013 -0800

    go/parser: better error message for declaration error
    
    Fixes #4616.
    
    R=rsc
    CC=golang-dev
    https://golang.org/cl/7069049

変更の背景

この変更は、Go言語のIssue #4616「go/parser: better error message for declaration error」に対応するものです。

元のパーサーでは、ショート変数宣言(:=)の左辺に識別子ではない式が来た場合、例えば t[0] := 0 のようなコードに対して、単に「expected identifier」(識別子が必要です)というエラーメッセージを出力していました。しかし、このメッセージだけでは、なぜ識別子が必要なのか、具体的にどの部分が問題なのかが不明瞭でした。特に、:= 演算子の文脈を考慮すると、左辺には新しい変数を宣言するための識別子が必要であるという情報が欠けていました。

この曖昧さを解消し、開発者がより直感的にエラーの原因を理解できるようにするため、エラーメッセージを「expected identifier on left side of :=」(:= の左辺に識別子が必要です)という、より具体的で文脈に即した表現に改善する必要がありました。この変更により、コンパイルエラーに遭遇した開発者は、問題の箇所と修正方法をより容易に特定できるようになります。

前提知識の解説

Go言語のパーサー (go/parser)

go/parser パッケージは、Go言語のソースコードを解析し、抽象構文木(AST: Abstract Syntax Tree)を生成するための標準ライブラリです。ASTは、ソースコードの構造を木構造で表現したもので、コンパイラやリンター、コード分析ツールなどがコードの意味を理解するために利用します。

パーサーは、字句解析器(lexer)が生成したトークン列を受け取り、Go言語の文法規則に従って構文解析を行います。この過程で文法エラーが検出された場合、パーサーはエラーメッセージを生成し、開発者に問題箇所を通知します。

抽象構文木 (AST: Abstract Syntax Tree)

ASTは、プログラミング言語のソースコードの抽象的な構文構造を、木構造で表現したものです。各ノードはソースコードの構成要素(変数宣言、関数呼び出し、演算子など)を表し、その子ノードはさらに詳細な構成要素を表します。ASTは、コンパイラのセマンティック解析、最適化、コード生成の段階で利用されるほか、IDEのコード補完、リファクタリングツール、静的解析ツールなど、様々な開発ツールで活用されます。

ショート変数宣言 (:=)

Go言語には、変数を宣言し、同時に初期値を代入するためのショート変数宣言(:=)という構文があります。これは、var name type = value のような通常の変数宣言を簡潔に記述するためのものです。

例:

package main

func main() {
    // ショート変数宣言
    message := "Hello, Go!" // messageという新しい変数を宣言し、"Hello, Go!"で初期化
    println(message)
}

ショート変数宣言の重要なルールとして、:= の左辺には少なくとも1つの新しい変数が含まれている必要があります。また、左辺に現れるのは変数名(識別子)でなければなりません。例えば、t[0] := 0 のように、既に宣言されている配列の要素や、その他の式を左辺に置くことはできません。これは、:= が「宣言と代入」を同時に行う演算子であるためです。

Go言語におけるエラーハンドリングとパーサーのエラー報告

Go言語では、エラーは通常、関数の戻り値として明示的に返されます。パーサーのようなツールでは、構文解析中にエラーが発生した場合、そのエラーを適切に報告する必要があります。go/parser パッケージでは、parser 構造体がエラーを記録するためのメソッド(例: errorerrorExpected)を持っています。これらのメソッドは、エラーが発生した位置(ファイル名、行番号、列番号)と、エラーの内容を示すメッセージを記録します。

p.errorExpected(pos, msg) は、パーサーが特定の構文要素を期待していたにもかかわらず、別のものが現れた場合に呼び出されるメソッドです。pos はエラーが発生したソースコード上の位置を、msg は期待していたものを示すメッセージを渡します。このコミットでは、この msg の内容をより具体的に変更することで、エラーメッセージの質を向上させています。

技術的詳細

このコミットの技術的な核心は、go/parser パッケージ内の shortVarDecl 関数におけるエラー報告ロジックの変更です。

shortVarDecl 関数は、Go言語のショート変数宣言(:=)を解析する役割を担っています。この関数は、:= の左辺に現れる式を評価し、それが有効な識別子であるかどうかを検証します。

変更前のコードでは、:= の左辺に識別子ではない式(例えば、t[0] のようなインデックス付きの式)が来た場合、以下の行でエラーを報告していました。

p.errorExpected(x.Pos(), "identifier")

ここで x.Pos() はエラーが発生した位置を示し、"identifier" はパーサーが「識別子」を期待していたことを示すメッセージです。

このコミットでは、このエラーメッセージをより具体的にするために、"identifier""identifier on left side of :=" に変更しました。

p.errorExpected(x.Pos(), "identifier on left side of :=")

この変更により、例えば t[0] := 0 という不正なコードに対して、パーサーは以下のようなエラーメッセージを出力するようになります。

expected identifier on left side of :=

これは、単に「識別子が必要です」と表示されるよりも、:= 演算子の文脈において左辺に識別子が必要であるという具体的な情報を提供するため、開発者にとってエラーの原因を特定しやすくなります。

また、この変更を検証するために、src/pkg/go/parser/short_test.go に新しいテストケースが追加されました。

`package p; func f() { var t []int; t /* ERROR "expected identifier on left side of :=" */ [0] := 0 };`,

このテストケースは、t[0] := 0 という不正なショート変数宣言を含むコードスニペットをパーサーに渡し、期待されるエラーメッセージが「expected identifier on left side of :=」であることを検証します。これにより、変更が正しく機能し、意図したエラーメッセージが出力されることが保証されます。

この修正は、Go言語のコンパイラやツールチェインが生成するエラーメッセージの品質を向上させるための、細かではあるが重要な改善の一例です。より明確なエラーメッセージは、開発者のデバッグ体験を大幅に向上させ、開発効率を高めます。

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

diff --git a/src/pkg/go/parser/parser.go b/src/pkg/go/parser/parser.go
index cf94e00653..959af38720 100644
--- a/src/pkg/go/parser/parser.go
+++ b/src/pkg/go/parser/parser.go
@@ -149,7 +149,7 @@ func (p *parser) shortVarDecl(decl *ast.AssignStmt, list []ast.Expr) {
 				}
 			}
 		} else {
-			p.errorExpected(x.Pos(), "identifier")
+			p.errorExpected(x.Pos(), "identifier on left side of :=")
 		}
 	}
 	if n == 0 && p.mode&DeclarationErrors != 0 {
diff --git a/src/pkg/go/parser/short_test.go b/src/pkg/go/parser/short_test.go
index daba853088..c62f7e0506 100644
--- a/src/pkg/go/parser/short_test.go
+++ b/src/pkg/go/parser/short_test.go
@@ -70,6 +70,7 @@ var invalids = []string{
 	`package p; func f() { select { case _ <- chan /* ERROR "expected expression" */ int: } };`,\
 	`package p; func f() { _ = (<-<- /* ERROR "expected 'chan'" */ chan int)(nil) };`,\
 	`package p; func f() { _ = (<-chan<-chan<-chan<-chan<-chan<- /* ERROR "expected channel type" */ int)(nil) };`,\
+\t`package p; func f() { var t []int; t /* ERROR "expected identifier on left side of :=" */ [0] := 0 };`,\
 }\
 \
 func TestInvalid(t *testing.T) {

コアとなるコードの解説

src/pkg/go/parser/parser.go の変更

このファイルでは、parser 構造体の shortVarDecl メソッドが変更されています。

func (p *parser) shortVarDecl(decl *ast.AssignStmt, list []ast.Expr): このメソッドは、ショート変数宣言(:=)を解析する際に呼び出されます。decl は抽象構文木における代入文のノード、list は代入の左辺に現れる式のリストです。

変更が行われたのは、else ブロック内の p.errorExpected の呼び出しです。 元のコード:

p.errorExpected(x.Pos(), "identifier")

変更後のコード:

p.errorExpected(x.Pos(), "identifier on left side of :=")

ここで x は、:= の左辺に現れた式を表します。もし x が識別子ではない場合(例えば、t[0] のようなインデックス付きの式や、関数呼び出しなど)、この else ブロックが実行されます。 p.errorExpected は、パーサーが特定の構文要素を期待していたにもかかわらず、別のものが現れた場合にエラーを報告するための内部メソッドです。 この変更により、エラーメッセージが「identifier」(識別子)から「identifier on left side of :=」(:= の左辺に識別子)へと具体化され、エラーの文脈が明確になります。

src/pkg/go/parser/short_test.go の変更

このファイルでは、invalids という文字列スライスの最後に新しいテストケースが追加されています。

`package p; func f() { var t []int; t /* ERROR "expected identifier on left side of :=" */ [0] := 0 };`,

この文字列は、不正なGoコードスニペットを表しています。

  • package p; func f() { ... }: 標準的なGoパッケージと関数定義。
  • var t []int;: スライス t を宣言。
  • t /* ERROR "expected identifier on left side of :=" */ [0] := 0: ここがテスト対象の不正なコードです。t[0] は配列の要素であり、識別子ではありません。/* ERROR "..." */ は、Goのテストフレームワークがこの位置で特定のエラーメッセージが出力されることを期待していることを示す特別なコメントです。このコメントにより、パーサーが t[0] := 0 に対して「expected identifier on left side of :=」というエラーメッセージを正確に出力するかどうかが検証されます。

このテストケースの追加により、parser.go で行われたエラーメッセージの変更が、期待通りに機能していることが自動的に検証されるようになります。

関連リンク

参考にした情報源リンク