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

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

このコミットは、Go言語のパーサーにおけるエラーメッセージの改善に関するものです。具体的には、条件式が期待される場所で単純なステートメントが見つかった場合に表示されるエラーメッセージに、より具体的なヒントを追加しています。

コミット

commit 8a089c07ec13c2c9d3f721f2236624f6284b7877
Author: Robert Griesemer <gri@golang.org>
Date:   Thu Jan 9 14:51:23 2014 -0800

    go/parser: slightly improved error message by adding hint
    
    It's difficult to make this much better w/o much
    more effort. This is a rare case and probably not
    worth it.
    
    Fixes #6052.
    
    R=golang-codereviews, bradfitz, adonovan
    CC=golang-codereviews
    https://golang.org/cl/49740045

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

https://github.com/golang/go/commit/8a089c07ec13c2c9d3f721f2236624f6284b7877

元コミット内容

go/parser: slightly improved error message by adding hint

このコミットは、Goパーサーのエラーメッセージをわずかに改善し、ヒントを追加するものです。コミットメッセージには、「これ以上改善するのは多大な労力を要し、稀なケースであるため、おそらくそれだけの価値はない」と述べられています。また、Fixes #6052 とあり、Issue 6052を修正するものであることが示されています。

変更の背景

この変更の背景には、Go言語のパーサーが特定の構文エラーに遭遇した際、ユーザーに提供するエラーメッセージの分かりにくさがありました。特に、条件式が期待される場所(if文やfor文の条件部など)で、誤って単純なステートメント(例えば、複合リテラルを括弧で囲まずに記述した場合など)が記述された場合に、パーサーは「expected condition, found simple statement」(条件式が期待されるが、単純なステートメントが見つかった)というエラーを出力していました。

このエラーメッセージは、構文的には正しいものの、なぜそのエラーが発生したのか、どのように修正すればよいのかが初心者には分かりにくいという問題がありました。Issue #6052("go/parser: improve error message for if { ... }")では、この問題が具体的に指摘されており、よりユーザーフレンドリーなエラーメッセージが求められていました。

コミットメッセージにある「It's difficult to make this much better w/o much more effort. This is a rare case and probably not worth it.」という記述は、この特定のエラーケースが比較的稀であり、かつ、パーサーの複雑なロジックを大幅に変更せずにエラーメッセージを劇的に改善することは難しいという開発者の判断を示しています。そのため、このコミットでは、最小限の労力で最大限の効果を得るために、既存のエラーメッセージにヒントを追加するというアプローチが取られました。

前提知識の解説

Go言語のパーサー

Go言語のコンパイラは、ソースコードを機械が理解できる形式に変換する過程で、いくつかの段階を踏みます。その最初の重要な段階の一つが「パース(構文解析)」です。パーサーは、字句解析器(lexer/scanner)によって生成されたトークンストリームを受け取り、Go言語の文法規則に従って、それらが正しい構造を持っているかを確認し、抽象構文木(AST: Abstract Syntax Tree)を構築します。

ASTは、プログラムの構造を木構造で表現したもので、コンパイラの後の段階(型チェック、最適化、コード生成など)で利用されます。パーサーは、文法エラーを検出した場合、そのエラーの位置と内容をユーザーに報告する役割も担っています。

抽象構文木 (AST)

ASTは、ソースコードの構造を抽象的に表現したツリーデータ構造です。各ノードは、プログラムの構成要素(変数宣言、関数呼び出し、式、ステートメントなど)を表します。Go言語のgo/astパッケージは、このASTの構造を定義しています。

go/parserパッケージ

Go標準ライブラリのgo/parserパッケージは、Go言語のソースコードをパースし、ASTを生成するための機能を提供します。このパッケージは、Goコンパイラ自体だけでなく、Goのツール(go fmtgo vetなど)やIDE、静的解析ツールなど、Goコードをプログラム的に解析する必要がある多くの場面で利用されます。

ast.Stmtast.Expr

Go言語のASTにおいて、ast.Stmt(Statement)はプログラムの実行可能な単位(例: 変数宣言、代入、if文、for文など)を表し、ast.Expr(Expression)は値を生成するコードの断片(例: リテラル、変数参照、関数呼び出し、算術演算など)を表します。

if文やfor文の条件部など、特定の文脈ではast.Expr(条件式)が期待されますが、誤ってast.Stmt(単純なステートメント)が記述されることがあります。このコミットが修正しようとしているのは、まさにこのような状況で発生するエラーです。

複合リテラル (Composite Literals)

Go言語の複合リテラルは、構造体、配列、スライス、マップなどの複合型を初期化するための構文です。例えば、struct{x int}{x: 1}のように記述します。重要なのは、複合リテラルはそれ自体が式(ast.Expr)であるということです。しかし、if文の条件部などで、複合リテラルを直接記述しようとすると、パーサーがそれを単純なステートメントと誤解釈する場合があります。これは、複合リテラルが波括弧{}を使用するため、コードブロックと見分けがつきにくいという構文上の曖昧さに起因することがあります。この場合、複合リテラルを括弧()で囲むことで、パーサーにそれが式であることを明示する必要があります(例: if (struct{x int}{x: 1}).x == 1 { ... })。

技術的詳細

このコミットは、src/pkg/go/parser/parser.goファイル内のmakeExprというメソッドの変更を含んでいます。

makeExprメソッドは、ast.Stmt(ステートメント)を受け取り、それをast.Expr(式)に変換しようとするパーサー内部のヘルパー関数です。この関数は、例えばif文の条件部のように、式が期待されるが、パーサーが最初にステートメントとして認識してしまった場合に呼び出されます。

元のコードでは、入力されたsast.ExprStmt(式ステートメント、つまり単一の式からなるステートメント)であるかどうかをチェックし、もしそうであればその式を返していました。しかし、sast.ExprStmtではない場合、つまり、式が期待される場所で、パーサーが式ではない別の種類の単純なステートメント(例えば、複合リテラルを括弧なしで記述した結果、それがブロックと誤解釈された場合など)を見つけた場合、エラーを報告していました。

変更前のエラーメッセージは以下の通りでした。 p.error(s.Pos(), "expected condition, found simple statement")

このメッセージは、何が問題なのかを正確に伝えていますが、なぜその「単純なステートメント」が条件として受け入れられないのか、そしてどのように修正すればよいのかについてのヒントがありませんでした。

このコミットでは、エラーメッセージに以下のヒントが追加されました。 p.error(s.Pos(), "expected condition, found simple statement (missing parentheses around composite literal?)")

この変更により、エラーメッセージは「(missing parentheses around composite literal?)」(複合リテラルの周りに括弧がありませんか?)という具体的な示唆を含むようになりました。これは、Issue #6052で報告された典型的な誤用パターン(複合リテラルを条件式として直接使用しようとするケース)を直接的にターゲットにしています。

このヒントは、パーサーが特定の構文パターンを検出したときにのみ表示されるわけではなく、makeExprast.ExprStmt以外のast.Stmtを受け取った場合に常に表示されます。これは、コミットメッセージにある「It's difficult to make this much better w/o much more effort. This is a rare case and probably not worth it.」という記述と一致しています。つまり、パーサーのロジックを複雑にすることなく、最も一般的な誤解釈のケースに対する有用なヒントを汎用的に追加したということです。

この変更は、Go言語のパーサーがよりユーザーフレンドリーになるための小さな一歩であり、特にGo言語の学習者が遭遇しやすい特定の構文エラーに対する理解を深めるのに役立ちます。

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

変更はsrc/pkg/go/parser/parser.goファイル内の1箇所のみです。

--- a/src/pkg/go/parser/parser.go
+++ b/src/pkg/go/parser/parser.go
@@ -1752,7 +1752,7 @@ func (p *parser) makeExpr(s ast.Stmt) ast.Expr {
 	if es, isExpr := s.(*ast.ExprStmt); isExpr {
 		return p.checkExpr(es.X)
 	}
-	p.error(s.Pos(), "expected condition, found simple statement")
+	p.error(s.Pos(), "expected condition, found simple statement (missing parentheses around composite literal?)")
 	return &ast.BadExpr{From: s.Pos(), To: s.End()}
 }

コアとなるコードの解説

parser.gomakeExpr関数は、Go言語のパーサーが構文解析中に呼び出す内部関数です。

func (p *parser) makeExpr(s ast.Stmt) ast.Expr {
	// sがast.ExprStmt(式ステートメント)であれば、その中の式を返す
	if es, isExpr := s.(*ast.ExprStmt); isExpr {
		return p.checkExpr(es.X)
	}
	// sがast.ExprStmtではない場合、エラーを報告する
	// 変更前: p.error(s.Pos(), "expected condition, found simple statement")
	// 変更後: p.error(s.Pos(), "expected condition, found simple statement (missing parentheses around composite literal?)")
	p.error(s.Pos(), "expected condition, found simple statement (missing parentheses around composite literal?)")
	// エラーが発生したため、不正な式を表すast.BadExprを返す
	return &ast.BadExpr{From: s.Pos(), To: s.End()}
}

この関数は、if文やfor文の条件部など、式が期待される文脈で呼び出されます。もし、渡されたs(ステートメント)が実際に式ステートメント(ast.ExprStmt)であれば、その中の式(es.X)を返します。これは、例えばif x > 0 { ... }のような場合に、x > 0という式が正しく抽出されることを意味します。

しかし、もしsが式ステートメントではない場合、それはパーサーが構文エラーを検出したことを意味します。この場合、パーサーはp.errorメソッドを呼び出してエラーメッセージを生成します。このコミットの変更は、このエラーメッセージの文字列を修正し、より具体的なヒントを追加することにあります。

追加されたヒント「(missing parentheses around composite literal?)」は、特にGo言語の初心者や、複合リテラルの構文に慣れていない開発者が陥りやすい間違い(複合リテラルを括弧で囲まずに条件式として使用しようとするケース)を想定しています。これにより、エラーメッセージを見た開発者が、より迅速に問題の原因を特定し、修正できるようになります。

関連リンク

参考にした情報源リンク