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

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

このコミットは、Goコンパイラ(cmd/gc)におけるCスタイルのif文に対するエラーメッセージの改善を目的としています。具体的には、if文の後に{が欠落している場合に、より分かりやすいエラーメッセージを出力するように変更されています。

コミット

commit d00bd1d1f4a39fb81b34150aca89dcc5e45b727e
Author: Rob Pike <r@golang.org>
Date:   Mon Aug 19 11:49:59 2013 +1000

    cmd/gc: better error messages for C-style if statements.
    Given
            if (i == 0)
                    x++
    The old message was
            x.go:6: syntax error: unexpected semicolon or newline before {
    Now we see
            x.go:6: syntax error: missing { after if clause
    
    Fixes #5687
    
    R=golang-dev, adg
    CC=golang-dev
    https://golang.org/cl/12822045

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

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

元コミット内容

cmd/gc: better error messages for C-style if statements.

このコミットは、Goコンパイラ(cmd/gc)がCスタイルのif文、特にif (condition) statement;のような形式で{が省略された場合に生成するエラーメッセージを改善します。

例として、以下のコードが与えられた場合:

if (i == 0)
        x++

古いエラーメッセージは以下の通りでした:

x.go:6: syntax error: unexpected semicolon or newline before {

このコミットにより、新しいエラーメッセージは以下のようになります:

x.go:6: syntax error: missing { after if clause

この変更は、Issue #5687を修正するものです。

変更の背景

Go言語は、C言語のようなプログラミング言語から影響を受けていますが、構文には明確な違いがあります。特に、ifforswitchなどの制御構造では、条件式の後に必ず波括弧{}で囲まれたブロックが続くことが義務付けられています。C言語では、単一のステートメントであれば波括弧を省略できますが、Goではこれは許可されていません。

このコミットが修正する問題は、Goの構文規則に違反してCスタイルのif文(条件式の後に波括弧なしで単一のステートメントが続く形式)を記述した場合に、コンパイラが生成するエラーメッセージが不明瞭であったことです。

元のエラーメッセージ「syntax error: unexpected semicolon or newline before {」は、プログラマにとって何が問題なのかを正確に把握しにくいものでした。特に、Go言語の初心者やC言語の経験者にとっては、なぜセミコロンや改行が問題になるのか理解しづらく、{が欠落していることが直接的に示されていませんでした。

この不明瞭なエラーメッセージは、開発者が構文エラーの原因を特定し、修正するまでの時間を不必要に長くしていました。そのため、より具体的で分かりやすいエラーメッセージを提供することで、開発者の生産性を向上させることがこの変更の背景にあります。Issue #5687は、このエラーメッセージの改善を求めるコミュニティからの要望を反映したものです。

前提知識の解説

このコミットの変更内容を理解するためには、以下の前提知識が役立ちます。

1. Go言語の構文規則(制御構造)

Go言語のifforswitchなどの制御構造は、C言語やJavaなどとは異なり、条件式の後に必ずステートメントブロックを波括弧{}で囲む必要があります。単一のステートメントであっても波括弧の省略はできません。

例:

// 正しいGoのif文
if x > 0 {
    fmt.Println("x is positive")
}

// 誤ったGoのif文(Cスタイル)
// if x > 0
//     fmt.Println("x is positive") // コンパイルエラー

この規則は、コードの可読性を高め、"dangling else"問題のような曖昧さを避けるために導入されています。

2. コンパイラの構文解析(Parsing)

コンパイラは、ソースコードを機械が理解できる形式に変換するソフトウェアです。その過程で、ソースコードが言語の文法規則に準拠しているかをチェックする「構文解析(Parsing)」の段階があります。

  • 字句解析(Lexical Analysis/Scanning): ソースコードをトークン(キーワード、識別子、演算子など)の並びに分解します。
  • 構文解析(Syntactic Analysis/Parsing): トークンの並びが言語の文法規則に従っているかをチェックし、抽象構文木(AST: Abstract Syntax Tree)を構築します。この段階で文法エラーが検出されます。

3. Bison (GNU Bison)

Bisonは、Yacc(Yet Another Compiler Compiler)互換のパーサジェネレータです。文法規則を記述したファイル(通常.yまたは.yy拡張子)を入力として受け取り、C言語などのソースコード(通常y.tab.c)を生成します。この生成されたコードは、入力されたトークンストリームを解析し、文法規則に合致するかどうかを判断します。

Goコンパイラ(cmd/gc)の初期バージョンでは、Go言語の構文解析にBisonが使用されていました。src/cmd/gc/go.yがGo言語の文法規則を定義するBisonの入力ファイルであり、src/cmd/gc/y.tab.cはBisonによって生成されたパーサのC言語ソースコードです。

4. エラーリカバリとエラーメッセージ

構文解析中に文法エラーが検出された場合、パーサはエラーを報告し、可能であれば解析を続行しようとします(エラーリカバリ)。エラーメッセージは、開発者が問題を特定し、修正するために非常に重要です。良いエラーメッセージは、問題の根本原因を正確に指し示し、修正方法を暗示するものです。

このコミットは、Bisonによって生成されたパーサが特定のエラー状況(if文の後の{の欠落)に遭遇した際に、より適切なエラーメッセージを生成するように、Bisonの文法定義やエラー処理ロジックを調整したものです。

技術的詳細

このコミットの技術的詳細は、Goコンパイラの構文解析部分、特にBisonによって生成されたパーサの動作とエラー処理メカニズムに深く関連しています。

1. go.errors ファイルの変更

src/cmd/gc/go.errorsファイルは、Goコンパイラが生成するエラーメッセージの定義を含んでいます。このファイルは、Bisonの文法定義ファイル(go.y)と連携して、特定の構文規則が破られた際に表示されるメッセージをカスタマイズするために使用されます。

変更前は、ifswitchforなどの制御構造で{が欠落した場合に、一律で「unexpected semicolon or newline before {」というメッセージが使用されていました。これは、パーサがif (condition)の後に予期しないトークン(セミコロンや改行)を見つけ、本来期待される{がないと判断した結果です。

このコミットでは、go.errors内の該当するエラーメッセージが、より具体的で分かりやすいものに修正されています。

  • % loadsys package imports LFUNC LNAME '(' ')' '{' LIF if_header ';'
    • 変更前: "unexpected semicolon or newline before {"
    • 変更後: "missing { after if clause"
  • % loadsys package imports LFUNC LNAME '(' ')' '{' LSWITCH if_header ';'
    • 変更前: "unexpected semicolon or newline before {"
    • 変更後: "missing { after switch clause"
  • % loadsys package imports LFUNC LNAME '(' ')' '{' LFOR for_header ';'
    • 変更前: "unexpected semicolon or newline before {"
    • 変更後: "missing { after for clause"
  • % loadsys package imports LFUNC LNAME '(' ')' '{' LFOR ';' LBODY
    • 変更前: "unexpected semicolon or newline before {"
    • 変更後: "missing { after for clause"

これらの変更により、エラーが発生した制御構造の種類(ifswitchfor)と、具体的な問題点({の欠落)が明確に示されるようになりました。

2. y.tab.c および y.tab.h の変更

src/cmd/gc/y.tab.cは、src/cmd/gc/go.yからBisonによって生成されるC言語のパーサ実装ファイルです。src/cmd/gc/y.tab.hは、そのヘッダファイルです。

このコミットにおけるy.tab.cの変更は非常に大規模であり、行数で見ると削除が1266行、追加が662行となっています。これは、Bisonのバージョンアップ(2.5から2.3へのダウングレード、またはBisonの生成コードの変更)に伴うものか、あるいはgo.yの文法定義が大幅に修正された結果として、y.tab.c全体が再生成されたためと考えられます。

コミットメッセージにはBisonのバージョンに関する直接的な言及はありませんが、y.tab.cのヘッダ部分に「/* A Bison parser, made by GNU Bison 2.5. */」から「/* A Bison parser, made by GNU Bison 2.3. */」への変更が見られます。これは、Bisonのバージョンをダウングレードしたか、あるいは生成されたコードの互換性に関する調整が行われたことを示唆しています。

具体的な変更点としては、Bisonが生成するパーサの内部構造、特にエラー処理に関連する部分が影響を受けています。例えば、yysyntax_error関数の実装が大きく変更されています。この関数は、構文エラーが発生した際に詳細なエラーメッセージを構築する役割を担っています。変更前は、エラーメッセージの生成ロジックがより複雑で、予期しないトークンと期待されるトークンのリストを動的に構築しようとしていました。変更後は、よりシンプルで直接的なメッセージ生成ロジックに変わっています。

この変更は、単にエラーメッセージの文字列を変更するだけでなく、パーサがエラーを検出した際の内部的な状態遷移や、エラーメッセージを生成するための情報収集の方法にも影響を与えています。これにより、特定の構文エラーパターンに対して、より的確なエラーメッセージを生成できるようになりました。

3. yerr.h ファイルの変更

src/cmd/gc/yerr.hは、Bisonによって生成されるエラー関連の定義を含むヘッダファイルです。このファイルには、エラーメッセージの書式設定や、エラーリカバリに関連するマクロなどが含まれることがあります。

このコミットでは、yerr.hにもわずかな変更(8行の追加と削除)があります。これは、y.tab.cの変更と連携して、エラー処理の内部的なメカニズムを調整するためのものです。例えば、YYERROR_VERBOSEマクロの定義や、エラーメッセージの出力に関するマクロの調整が行われている可能性があります。

まとめ

このコミットは、Goコンパイラの構文解析におけるエラーメッセージの品質を向上させるための重要な改善です。Bisonによって生成されたパーサの内部動作と、エラーメッセージの定義ファイルを連携させることで、開発者にとってより分かりやすいフィードバックを提供できるようになりました。特に、C言語の経験者が陥りやすいif文の波括弧省略という構文エラーに対して、直接的なガイダンスを与えることで、学習コストの削減と開発効率の向上に貢献しています。

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

このコミットにおけるコアとなるコードの変更箇所は、主に以下のファイルに集中しています。

  1. src/cmd/gc/go.errors:

    • if_headerswitch_clausefor_clauseなどの構文規則に関連するエラーメッセージが、より具体的で分かりやすい表現に修正されています。
    • 例: "unexpected semicolon or newline before {" から "missing { after if clause" へ。
  2. src/cmd/gc/y.tab.c:

    • Bisonによって生成されたパーサのC言語実装ファイル。
    • Bisonのバージョン変更(2.5から2.3への変更コメント)に伴う大規模なコードの再生成が行われています。
    • 特に、yysyntax_error関数の実装が大幅に変更されており、エラーメッセージの生成ロジックが調整されています。
    • YYBISON_VERSIONが "2.5" から "2.3" に変更されています。
    • YYSTYPE unionの定義が変更されています。
    • YYSTACK_ALLOC_MAXIMUMやメモリ割り当てに関するマクロの定義が変更されています。
    • yy_stack_printyy_reduce_printなどのデバッグ出力関連の関数も変更されています。
  3. src/cmd/gc/y.tab.h:

    • y.tab.cに対応するヘッダファイル。
    • トークンタイプ(yytname配列)の定義が一部変更されています。例えば、$@1のようなBisonが生成する内部的なトークン名が@1に変更されています。
  4. src/cmd/gc/yerr.h:

    • エラー処理に関連する定義が含まれるヘッダファイル。
    • YYFAILマクロに関するコメントが削除されています。
    • YY_LOCATION_PRINTマクロの定義が変更されています。

コアとなるコードの解説

src/cmd/gc/go.errors の変更

このファイルは、Goコンパイラが構文エラーを検出した際に表示するメッセージのテンプレートを定義しています。変更の核心は、一般的な「予期しないセミコロンまたは改行」というメッセージを、より具体的な「{が欠落している」というメッセージに置き換えた点です。

--- a/src/cmd/gc/go.errors
+++ b/src/cmd/gc/go.errors
@@ -21,16 +21,16 @@ static struct {
 	"missing import path; require quoted string",
 
 	% loadsys package imports LFUNC LNAME '(' ')' '{' LIF if_header ';'
-	"unexpected semicolon or newline before {",
+	"missing { after if clause",
 
 	% loadsys package imports LFUNC LNAME '(' ')' '{' LSWITCH if_header ';'
-	"unexpected semicolon or newline before {",
+	"missing { after switch clause",
 
 	% loadsys package imports LFUNC LNAME '(' ')' '{' LFOR for_header ';'
-	"unexpected semicolon or newline before {",
+	"missing { after for clause",
 
 	% loadsys package imports LFUNC LNAME '(' ')' '{' LFOR ';' LBODY
-	"unexpected semicolon or newline before {",
+	"missing { after for clause",
 
 	% loadsys package imports LFUNC LNAME '(' ')' ';' '{'
 	"unexpected semicolon or newline before {",
  • % loadsys ... の行は、Bisonの文法規則に対応するエラーメッセージの定義を示しています。
  • LIF if_header ';' は、if文のヘッダ(条件式)の後にセミコロン(または改行)が続くパターンを示しており、これはGoの構文では{が期待される場所です。
  • 同様に、LSWITCHLFORもそれぞれの制御構造に対応しています。

この変更により、コンパイラは構文解析中にこれらの特定のパターンに遭遇した場合、より的確なエラーメッセージを生成するようになります。

src/cmd/gc/y.tab.c の変更

このファイルはBisonによって生成されるため、手動で編集されることは稀です。このコミットでの大規模な変更は、Bisonのバージョン変更(2.5から2.3へのダウングレード)またはgo.yの文法定義の変更が原因で、ファイル全体が再生成された結果です。

特に注目すべきは、yysyntax_error関数の変更です。この関数は、構文エラーが発生した際に、どのトークンが予期され、どのトークンが予期されなかったかを詳細に報告するためのメッセージを構築します。

変更前は、Bison 2.5のyysyntax_errorの実装が使用されており、これはより複雑なロジックで「unexpected X, expecting Y or Z」のような詳細なメッセージを生成しようとします。しかし、Goの特定の構文エラー({の欠落)の場合、この汎用的なメッセージが必ずしも分かりやすいとは限りませんでした。

変更後(Bison 2.3の生成コードに相当する可能性が高い)は、yysyntax_errorのロジックが簡素化され、go.errorsで定義されたより直接的なメッセージを優先的に使用するようになっています。

--- a/src/cmd/gc/y.tab.c
+++ b/src/cmd/gc/y.tab.c
@@ -1,21 +1,24 @@
-/* A Bison parser, made by GNU Bison 2.5.  */
+/* A Bison parser, made by GNU Bison 2.3.  */

このヘッダコメントの変更は、Bisonのバージョンが変更されたことを明確に示しています。Bisonのバージョンによって生成されるコードの構造やエラー処理の挙動が異なるため、この変更はエラーメッセージの改善に直接的な影響を与えています。

また、YYSTYPE共用体の定義や、スタック操作に関連するマクロ(YYSTACK_RELOCATEなど)の変更も、Bisonのバージョンアップ(またはダウングレード)に伴う生成コードの差異を反映しています。これらの変更は、パーサの内部的なメモリ管理やデータ構造の調整を示唆していますが、直接的なエラーメッセージのロジック変更というよりは、基盤となるパーサジェネレータの変更によるものです。

src/cmd/gc/y.tab.h の変更

このファイルは、y.tab.cで定義されるトークンや型などの宣言を含みます。

--- a/src/cmd/gc/y.tab.h
+++ b/src/cmd/gc/y.tab.h
@@ -731,16 +714,16 @@ const char *yytname[] =
   "'/'", "'%'", "'&'", "NotPackage", "NotParen", "'('", "')'",
   "PreferToRightParen", "';'", "'.'", "'$'", "'='", "':'", "'{'", "'}'",
   "'!'", "'~'", "'['", "']'", "'?'", "'@'", "','", "$accept", "file",
-  "package", "loadsys", "$@1", "imports", "import", "import_stmt",
+  "package", "loadsys", "@1", "imports", "import", "import_stmt",
   "import_stmt_list", "import_here", "import_package", "import_safety",
-  "import_there", "$@2", "xdcl", "common_dcl", "lconst", "vardcl",
+  "import_there", "@2", "xdcl", "common_dcl", "lconst", "vardcl",
   "constdcl", "constdcl1", "typedclname", "typedcl", "simple_stmt", "case",
-  "compound_stmt", "$@3", "caseblock", "$@4", "caseblock_list",
-  "loop_body", "$@5", "range_stmt", "for_header", "for_body", "for_stmt",
-  "$@6", "if_header", "if_stmt", "$@7", "$@8", "$@9", "elseif", "$@10",
-  "elseif_list", "else", "switch_stmt", "$@11", "$@12", "select_stmt",
-  "$@13", "expr", "uexpr", "pseudocall", "pexpr_no_paren", "start_complit",
-  "keyval", "bare_complitexpr", "complitexpr", "pexpr", "expr_or_type",
+  "compound_stmt", "@3", "caseblock", "@4", "caseblock_list", "loop_body",
+  "@5", "range_stmt", "for_header", "for_body", "for_stmt", "@6",
+  "if_header", "if_stmt", "@7", "@8", "@9", "elseif", "@10", "elseif_list",
+  "else", "switch_stmt", "@11", "@12", "select_stmt", "@13", "expr",
+  "uexpr", "pseudocall", "pexpr_no_paren", "start_complit", "keyval",
+  "bare_complitexpr", "complitexpr", "pexpr", "expr_or_type",
   "name_or_type", "lbrace", "new_name", "dcl_name", "onew_name", "sym",
   "hidden_importsym", "name", "labelname", "dotdotdot", "ntype",
   "non_expr_type", "non_recvchantype", "convtype", "comptype",
@@ -750,7 +733,7 @@ const char *yytname[] =
   "vardcl_list", "constdcl_list", "typedcl_list", "structdcl_list",
   "interfacedcl_list", "indcl", "arg_type", "arg_type_list", "oarg_type_list_ocomma", "stmt",
-  "non_dcl_stmt", "$@14", "stmt_list", "new_name_list", "dcl_name_list",
+  "non_dcl_stmt", "@14", "stmt_list", "new_name_list", "dcl_name_list",
   "expr_list", "expr_or_type_list", "keyval_list", "braced_keyval_list",
   "osemi", "ocomma", "oexpr", "oexpr_list", "osimple_stmt",
   "ohidden_funarg_list", "ohidden_structdcl_list",

yytname配列は、Bisonが内部的に使用するトークン名の文字列表現を定義しています。$@1のような形式は、Bisonが生成する非終端記号や規則の内部名です。これらが@1のように変更されているのは、Bisonのバージョン間の命名規則の差異によるものです。これは機能的な変更ではなく、生成コードのスタイルに関するものです。

src/cmd/gc/yerr.h の変更

このファイルは、Bisonのエラー処理に関連するマクロや定義を含みます。

--- a/src/cmd/gc/yerr.h
+++ b/src/cmd/gc/yerr.h
@@ -1,8 +1,8 @@
 /* A Bison parser, made by GNU Bison 2.5.  */
 /* This file is part of the GNU Bison distribution.  */
 
-/* Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
-
-   This program is free software: you can redistribute it and/or modify
+/* Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+   Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

この変更も、Bisonのバージョン変更に伴う著作権表示の更新と、それに伴う生成コードの差異を反映しています。特に、YYFAILマクロに関するコメントの削除やYY_LOCATION_PRINTマクロの変更は、Bisonのエラーリカバリ戦略やデバッグ情報の出力方法の進化を示唆しています。

全体として、このコミットは、Goコンパイラのエラーメッセージを改善するために、Bisonの生成コードとエラーメッセージ定義ファイルを連携して修正したものです。これにより、Go言語の構文エラーがより分かりやすく報告されるようになり、開発者のデバッグ体験が向上しました。

関連リンク

  • Go Issue #5687: https://code.google.com/p/go/issues/detail?id=5687 (Google Code Archive)
    • このコミットが修正した元のIssueです。GoのIssueトラッカーは現在GitHubに移行していますが、古いIssueはGoogle Code Archiveに残されています。
  • Gerrit Change-ID 12822045: https://golang.org/cl/12822045
    • このコミットに対応するGoのコードレビューシステム(Gerrit)上の変更リストです。

参考にした情報源リンク

  • Go言語の公式ドキュメント: Go言語の構文規則に関する基本的な情報は、公式ドキュメントで確認できます。
  • GNU Bison マニュアル: Bisonの動作原理や文法定義の記述方法に関する詳細な情報は、GNU Bisonの公式マニュアルで確認できます。
  • Goコンパイラのソースコード: src/cmd/gcディレクトリ内のファイルは、Goコンパイラの内部構造を理解する上で不可欠です。
  • Go言語のIssueトラッカー: 過去のIssueや議論は、特定の変更の背景や目的を理解する上で役立ちます。