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

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

このコミットは、Goコンパイラ(cmd/gc)におけるエラーメッセージの改善を目的としています。具体的には、構文エラーメッセージに表示される?および@シンボルへの参照を削除します。これらのシンボルはインポート時のみに許可されるものであり、一般的な構文エラーメッセージに含めることはユーザーの混乱を招くため、より分かりやすいエラーメッセージを提供するための変更です。

コミット

commit b2e0c34cf32a82cf808bfc54134a2cf1d8042429
Author: Russ Cox <rsc@golang.org>
Date:   Sun Feb 3 01:25:47 2013 -0500

    cmd/gc: remove reference to ? and @ symbols in error message
    
    Those symbols are only allowed during imports;
    the parser may expect them but saying that doesn't help users.
    
    Fixes #3434.
    
    R=ken2
    CC=golang-dev
    https://golang.org/cl/7277045

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

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

元コミット内容

cmd/gc: remove reference to ? and @ symbols in error message

Those symbols are only allowed during imports;
the parser may expect them but saying that doesn't help users.

Fixes #3434.

変更の背景

この変更の背景には、Goコンパイラが生成するエラーメッセージのユーザーフレンドリーさの向上が挙げられます。Go言語のパーサーは、内部的に?@といったシンボルを特定の文脈(特にインポートパス)で期待することがあります。しかし、これらのシンボルは通常のGoコードの構文では使用されません。

問題は、ユーザーが構文エラーを犯した際に、コンパイラが「?または@が予期されます」といったメッセージを出すことがあった点です。これは、パーサーの内部的な期待をそのままユーザーに伝えているだけであり、Go言語の構文規則を知っているユーザーにとっては混乱を招くものでした。なぜなら、Goの通常のコードでこれらのシンボルを使うことはないからです。

このコミットは、このような不必要な情報を含むエラーメッセージを修正し、ユーザーがより直感的にエラーの原因を理解できるようにすることを目的としています。具体的には、GitHub Issue #3434で報告された問題に対応しています。

前提知識の解説

  • cmd/gc: Go言語の公式コンパイラの一部であり、Goソースコードをコンパイルして実行可能ファイルを生成する役割を担っています。gcは「Go Compiler」の略です。
  • yyerror: yyerrorは、Yacc(Yet Another Compiler Compiler)やBisonなどのパーサー生成ツールによって生成されたパーサーにおいて、構文エラーが発生した際に呼び出される標準的なエラー報告関数です。コンパイラのフロントエンド(字句解析と構文解析)でエラーが検出された際に、この関数を通じてエラーメッセージが整形され、ユーザーに表示されます。
  • 構文エラー: プログラミング言語の文法規則に違反しているコードによって発生するエラーです。例えば、括弧の閉じ忘れ、セミコロンの欠落、予約語の誤用などが挙げられます。
  • Go言語における?@シンボル: Go言語の通常の構文では、?@といったシンボルは演算子や識別子として直接使用されません。しかし、Goのパーサーは、内部的に特定の文脈、特にインポートパスの解析において、これらのシンボルを処理するロジックを持つことがあります。例えば、バージョン管理システムのリポジトリパスなど、特殊な形式のインポートパスを扱う際に、これらのシンボルが内部的に「期待される」場合があります。このコミットの文脈では、これらのシンボルが通常のコードの構文エラーメッセージに現れることが問題視されています。
  • strstr関数: C言語の標準ライブラリ関数で、ある文字列(haystack)の中に別の文字列(needle)が最初に現れる位置を検索します。見つかった場合は、その部分文字列へのポインタを返し、見つからない場合はNULLを返します。
  • memmove関数: C言語の標準ライブラリ関数で、メモリブロックをコピーします。memcpyと異なり、コピー元とコピー先のメモリ領域が重なっていても正しく動作することが保証されています。このコミットでは、エラーメッセージ文字列の一部を削除するために、文字列の残りの部分を前方に移動させる目的で使用されています。

技術的詳細

このコミットは、src/cmd/gc/subr.cファイル内のyyerror関数を変更しています。yyerror関数は、Goコンパイラが構文エラーを報告する際に使用する関数です。

変更の核心は、エラーメッセージ文字列から特定の不要な部分を削除することです。具体的には、以下の文字列パターンを検索し、見つかった場合にその部分を削除しています。

  1. "{ or {" の修正: 以前のコミットで既に存在していたロジックですが、このコミットで拡張されています。これは、パーサーが{LBRACEという2つの異なるトークンを同じ{シンボルとして扱うため、エラーメッセージが「{または{が予期されます」のようになるのを防ぐためのものです。このコミットでは、この条件式にstrstr(fmt, " or ?")strstr(fmt, " or @")が追加されています。
  2. " or ?" の削除: エラーメッセージに「?が予期されます」という部分が含まれる場合、その「 or ?」の部分を削除します。
  3. " or @" の削除: エラーメッセージに「@が予期されます」という部分が含まれる場合、その「 or @」の部分を削除します。

これらの削除は、strstr関数で該当するパターンを検索し、パターンが見つかった場合にmemmove関数を使用して、不要な部分の後の文字列を前方にずらすことで実現されています。これにより、エラーメッセージから「 or ?」や「 or @」といった、ユーザーにとって意味不明な部分が取り除かれ、より簡潔で分かりやすいメッセージになります。

このアプローチは、エラーメッセージの生成ロジック自体を変更するのではなく、生成されたメッセージ文字列を後処理で修正するというものです。これは、パーサーの文法定義を変更することなく、ユーザーに表示されるエラーメッセージの品質を向上させるための実用的な方法です。

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

src/cmd/gc/subr.cファイルのyyerror関数内が変更されています。

--- a/src/cmd/gc/subr.c
+++ b/src/cmd/gc/subr.c
@@ -151,14 +151,23 @@ yyerror(char *fmt, ...)\n 	\tif(lastsyntax == lexlineno)\n 	\t\treturn;\n 	\tlastsyntax = lexlineno;\n-\t\t\n-\t\tif(strstr(fmt, \"{ or {\")) {\n+\t\t\t\n+\t\tif(strstr(fmt, \"{ or {\") || strstr(fmt, \" or ?\") || strstr(fmt, \" or @\")) {\n \t\t\t// The grammar has { and LBRACE but both show up as {.\n \t\t\t// Rewrite syntax error referring to \"{ or {\" to say just \"{\".\n \t\t\tstrecpy(buf, buf+sizeof buf, fmt);\n \t\t\tp = strstr(buf, \"{ or {\");\n \t\t\tif(p)\n \t\t\t\tmemmove(p+1, p+6, strlen(p+6)+1);\n+\t\t\t\n+\t\t\t// The grammar has ? and @ but only for reading imports.\n+\t\t\t// Silence them in ordinary errors.\n+\t\t\tp = strstr(buf, \" or ?\");\n+\t\t\tif(p)\n+\t\t\t\tmemmove(p, p+5, strlen(p+5)+1);\n+\t\t\tp = strstr(buf, \" or @\");\n+\t\t\tif(p)\n+\t\t\t\tmemmove(p, p+5, strlen(p+5)+1);\n \t\t\tfmt = buf;\n \t\t}\n \t\t\

コアとなるコードの解説

変更されたコードは、yyerror関数内でエラーメッセージを整形する部分です。

元のコードでは、if(strstr(fmt, "{ or {"))という条件で、エラーメッセージに「{または{」という冗長な表現が含まれる場合に、それを「{」に修正するロジックがありました。

このコミットでは、このif文の条件が以下のように拡張されました。

if(strstr(fmt, "{ or {") || strstr(fmt, " or ?") || strstr(fmt, " or @")) {

これにより、エラーメッセージに「{ or {」だけでなく、「 or ?」または「 or @」が含まれる場合も、後続のメッセージ修正ロジックが適用されるようになりました。

そして、既存の「{ or {」の修正ロジックに加えて、以下の新しいコードが追加されました。

			// The grammar has ? and @ but only for reading imports.
			// Silence them in ordinary errors.
			p = strstr(buf, " or ?");
			if(p)
				memmove(p, p+5, strlen(p+5)+1);
			p = strstr(buf, " or @");
			if(p)
				memmove(p, p+5, strlen(p+5)+1);

この追加されたコードブロックは、以下の処理を行います。

  1. p = strstr(buf, " or ?");: 現在のエラーメッセージ文字列bufの中に「 or ?」という部分文字列があるかを検索します。
  2. if(p) memmove(p, p+5, strlen(p+5)+1);: もし「 or ?」が見つかった場合、その部分(5文字)を削除するために、p(「 or ?」の開始位置)から、その5文字分後ろの文字列をpの位置に上書きコピーします。strlen(p+5)+1は、残りの文字列の長さとヌル終端文字を含めてコピーすることを意味します。
  3. 同様に、p = strstr(buf, " or @");if(p) memmove(p, p+5, strlen(p+5)+1);で、「 or @」についても同じ削除処理を行います。

これらの変更により、Goコンパイラが生成する構文エラーメッセージから、ユーザーにとって不必要な「 or ?」や「 or @」といった表現が取り除かれ、より分かりやすく、混乱の少ないエラーメッセージが提供されるようになりました。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (Go Compiler, cmd/gcに関する情報)
  • C言語のstrstrおよびmemmove関数のリファレンス
  • Yacc/Bisonのエラー処理に関する一般的な情報