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

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

このコミットは、Goコンパイラ(cmd/gc)において、不正な空白文字(whitespace characters)に対するエラー報告を改善するものです。具体的には、Goのソースコード内で許可されていない制御文字や不可視文字が使用された場合に、コンパイラが適切にエラーを出すように修正されています。

コミット

commit 8f3b703323df5831d13a2492019ebe932559ae4f
Author: Robert Griesemer <gri@golang.org>
Date:   Mon Nov 19 09:09:04 2012 -0800

    cmd/gc: complain about invalid whitespace chars
    
    Fixes #4405.
    
    R=rsc, bradfitz
    CC=golang-dev
    https://golang.org/cl/6855060
---
 src/cmd/gc/lex.c            |  8 ++------
 test/fixedbugs/issue4405.go | 15 +++++++++++++++
 2 files changed, 17 insertions(+), 6 deletions(-)

diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c
index 1031320a01..ad8bdebf03 100644
--- a/src/cmd/gc/lex.c
+++ b/src/cmd/gc/lex.c
@@ -99,7 +99,7 @@ yy_isdigit(int c)\n static int\n yy_isspace(int c)\n {\n-\treturn c >= 0 && c <= 0xFF && isspace(c);\n+\treturn c == \' \' || c == \'\\t\' || c == \'\\n\' || c == \'\\r\';\n }\n \n static int\n@@ -790,12 +790,8 @@ static int\n isfrog(int c)\n {\n \t// complain about possibly invisible control characters\n-\tif(c < 0)\n-\t\treturn 1;\n \tif(c < \' \') {\n-\t\tif(c == \'\\n\' || c== \'\\r\' || c == \'\\t\')\t// good white space\n-\t\t\treturn 0;\n-\t\treturn 1;\n+\t\treturn !yy_isspace(c);\t// exclude good white space\n \t}\n \tif(0x7f <= c && c <= 0xa0)\t// DEL, unicode block including unbreakable space.\n \t\treturn 1;\ndiff --git a/test/fixedbugs/issue4405.go b/test/fixedbugs/issue4405.go
new file mode 100644
index 0000000000..c0d8085598
--- /dev/null
+++ b/test/fixedbugs/issue4405.go
@@ -0,0 +1,15 @@
+// errorcheck
+\n+// Copyright 2012 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+\n+package p
+\n+const (\n+\t_ = iota\n+\t_\007 // ERROR \"illegal character\"\n+\t_\010  // ERROR \"illegal character\"\n+\t_\013  // ERROR \"illegal character\"\n+\t_\014  // ERROR \"illegal character\"\n+)\n

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

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

元コミット内容

このコミットは、Goコンパイラの字句解析器(lexer)が、ソースコード内の不正な空白文字を検出してエラーを報告するように変更します。具体的には、src/cmd/gc/lex.c内のyy_isspace関数とisfrog関数のロジックが修正され、Go言語の仕様で許可されていない制御文字や不可視文字が「不正な文字」として扱われるようになります。また、この変更を検証するための新しいテストケースtest/fixedbugs/issue4405.goが追加されています。

変更の背景

Go言語のソースコードは、特定の文字セットと空白文字のルールに従う必要があります。しかし、エディタや環境によっては、Goの仕様で許可されていない不可視の制御文字(例: ベル文字 \007、バックスペース \010、垂直タブ \013、フォームフィード \014 など)が誤って挿入されることがあります。これらの文字は通常、画面に表示されないため、プログラマが気づきにくく、コンパイルエラーや予期せぬ動作の原因となる可能性がありました。

このコミットは、Goコンパイラがこれらの不正な文字を早期に検出し、明確なエラーメッセージを出すことで、開発者が問題を特定しやすくすることを目的としています。コミットメッセージにある「Fixes #4405」は、この問題がGoのバグトラッカーで報告されていたことを示唆しています。

前提知識の解説

Goコンパイラ (cmd/gc)

cmd/gcは、Go言語の公式コンパイラです。Goのソースコードを機械語に変換する役割を担っています。コンパイルプロセスは、主に以下の段階に分けられます。

  1. 字句解析 (Lexical Analysis): ソースコードをトークン(意味を持つ最小単位、例: キーワード、識別子、演算子)のストリームに変換します。この段階で、不正な文字や構文エラーが検出されることがあります。
  2. 構文解析 (Syntactic Analysis): トークンのストリームを解析し、プログラムの構造を抽象構文木(AST)として構築します。
  3. 意味解析 (Semantic Analysis): 型チェック、変数解決など、プログラムの意味的な正当性を検証します。
  4. 中間コード生成 (Intermediate Code Generation): ASTを中間表現に変換します。
  5. 最適化 (Optimization): 中間コードを最適化し、実行効率を高めます。
  6. コード生成 (Code Generation): 最適化された中間コードをターゲットアーキテクチャの機械語に変換します。

このコミットは、字句解析の段階、特にソースコードの文字を処理する部分に焦点を当てています。

字句解析器 (Lexer) と lex.c

lex.cは、Goコンパイラの字句解析器の一部を実装しているC言語のファイルです。このファイルには、ソースコードから文字を読み込み、それらが有効なGoのトークンの一部であるかどうかを判断するための関数が含まれています。

  • yy_isspace(int c): この関数は、与えられた文字cがGo言語の仕様で「空白文字」として認識されるかどうかを判定します。従来の定義はC標準ライブラリのisspace関数に依存していましたが、Goのコンパイラはより厳密な空白文字の定義を必要とします。
  • isfrog(int c): この関数は、Goのソースコード内で「不正な文字」または「問題のある文字」を検出するために使用されます。通常、不可視の制御文字や、Goの仕様で許可されていないUnicode文字などが対象となります。この関数が1を返すと、その文字は不正であると判断され、コンパイルエラーが報告されます。

制御文字と不可視文字

コンピュータのテキストエンコーディングには、表示可能な文字(例: 'a', 'B', '1', '!')の他に、特定の動作を制御するための「制御文字」が含まれています。例えば、改行 (\n)、タブ (\t)、キャリッジリターン (\r) などは一般的な制御文字で、テキストのレイアウトを整形するために使用されます。

しかし、ベル文字 (\007、端末を鳴らす)、バックスペース (\010、カーソルを戻す)、垂直タブ (\013)、フォームフィード (\014、新しいページを開始する) など、Goのソースコードでは通常使用されない、あるいは意図しない動作を引き起こす可能性のある制御文字も存在します。これらの文字はエディタによっては表示されないため、「不可視文字」とも呼ばれます。

Go言語のコンパイラは、ソースコードの明確性と移植性を保つため、これらの不可視で意図しない動作を引き起こす可能性のある文字を許可しない方針を取っています。

技術的詳細

このコミットの技術的変更は、src/cmd/gc/lex.c内の2つの関数に集中しています。

  1. yy_isspace関数の変更:

    • 変更前: return c >= 0 && c <= 0xFF && isspace(c);
      • この行は、文字cが0から255の範囲にあり、かつC標準ライブラリのisspace関数によって空白文字と判定される場合にtrueを返していました。isspaceはロケールに依存する可能性があり、Goコンパイラが期待する厳密な空白文字の定義とは異なる挙動を示す可能性がありました。
    • 変更後: return c == ' ' || c == '\\t' || c == '\\n' || c == '\\r';
      • この変更により、yy_isspace関数は、Go言語の字句解析器が「有効な空白文字」として認識すべき文字(スペース、タブ、改行、キャリッジリターン)のみを明示的にチェックするようになりました。これにより、コンパイラの空白文字の解釈がより厳密かつ予測可能になります。
  2. isfrog関数の変更:

    • 変更前:
      if(c < 0)
          return 1;
      if(c < ' ') {
          if(c == '\n' || c== '\r' || c == '\t')    // good white space
              return 0;
          return 1;
      }
      
      • このロジックは、文字が負の値である場合(通常はEOFなど)や、表示可能な文字(' '以上)より小さい制御文字である場合に1を返していました。ただし、\n, \r, \tといった「良い空白文字」は除外されていました。この除外ロジックが冗長であり、またyy_isspaceの定義と整合性が取れていませんでした。
    • 変更後:
      if(c < ' ') {
          return !yy_isspace(c);    // exclude good white space
      }
      
      • 変更後のisfrog関数は、まず文字がASCIIの表示可能文字(スペース以上)より小さいかどうかをチェックします。もし小さければ、それは制御文字である可能性が高いです。
      • 次に、!yy_isspace(c)を呼び出すことで、新しく定義されたyy_isspace関数が「良い空白文字」と判断しない文字(つまり、Goのソースコードでは不正な制御文字)である場合にtrueを返します。これにより、isfrogはGoの仕様に準拠しない制御文字を正確に「不正な文字」として識別できるようになりました。
      • if(c < 0)のチェックは削除されました。これは、文字がint型で表現される場合、負の値は通常EOFなどの特殊なケースを意味し、字句解析の通常の文字ストリームでは発生しないか、他の場所で処理されるためと考えられます。
      • if(0x7f <= c && c <= 0xa0)の行は変更されていません。これはDEL文字(0x7f)や、Unicodeの特定のブロック(例えば、ノーブレークスペースなど)に含まれる不可視文字を引き続き「不正な文字」として扱うことを意味します。

テストケース (test/fixedbugs/issue4405.go)

このコミットでは、新しいテストファイルtest/fixedbugs/issue4405.goが追加されています。このテストは、Goコンパイラが不正な制御文字に対して正しくエラーを報告するかどうかを検証します。

// errorcheck

// Copyright 2012 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package p

const (
	_ = iota
	_\007 // ERROR "illegal character"
	_\010  // ERROR "illegal character"
	_\013  // ERROR "illegal character"
	_\014  // ERROR "illegal character"
)
  • // errorcheck: このコメントは、Goのテストフレームワークに対して、このファイルがコンパイルエラーを期待するテストであることを示します。
  • _\007, _\010, _\013, _\014: これらはそれぞれ、ベル文字 (ASCII 7)、バックスペース (ASCII 8)、垂直タブ (ASCII 11)、フォームフィード (ASCII 12) を表すエスケープシーケンスです。
  • // ERROR "illegal character": 各行のコメントは、その行で「illegal character」というエラーメッセージがコンパイラから出力されることを期待していることを示します。

このテストケースは、isfrog関数がこれらの特定の制御文字を「不正な文字」として正しく識別し、コンパイラがそれに対してエラーを出すことを保証します。

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

  • src/cmd/gc/lex.c:
    • yy_isspace関数の実装が変更されました。
    • isfrog関数のロジックが変更されました。
  • test/fixedbugs/issue4405.go:
    • 不正な制御文字を含む新しいテストファイルが追加されました。

コアとなるコードの解説

このコミットの核心は、Goコンパイラがソースコード内の空白文字と制御文字をどのように扱うかをより厳密に定義し、Go言語の仕様に準拠しない文字に対して明確なエラーを出すようにした点にあります。

yy_isspaceの変更は、Goコンパイラが認識する「空白」の定義を、C標準ライブラリのisspaceに依存するのではなく、Go言語の字句解析器が必要とする最小限かつ明確なセット(スペース、タブ、改行、キャリッジリターン)に限定したものです。これにより、コンパイラの挙動がより予測可能になり、異なる環境でのコンパイル結果の一貫性が向上します。

isfrogの変更は、この新しいyy_isspaceの定義を利用して、Goのソースコードで許可されていない制御文字を効率的かつ正確に検出するようにしました。以前のロジックは、if(c < 0)のような冗長なチェックや、yy_isspaceの定義と整合性の取れていない「良い空白文字」の除外ロジックを含んでいました。新しいロジックは、!yy_isspace(c)という簡潔な表現で、Goの仕様に合致しない制御文字をすべて「不正な文字」としてマークします。

追加されたテストケースは、これらの変更が意図した通りに機能し、Goのソースコードに誤って挿入された不可視の制御文字がコンパイル時に適切に検出されることを保証します。これは、Go言語の堅牢性と開発者体験の向上に貢献する重要な改善です。

関連リンク

参考にした情報源リンク

(注: 外部Web検索で「Go issue 4405」を検索しましたが、Goコンパイラに関連する直接的な情報は見つかりませんでした。これは、Goのバグトラッカーの古いエントリであるか、または検索インデックスにない内部的な問題番号である可能性があります。また、golang.org/cl/6855060の検索結果も、このコミットの内容とは異なる情報を示していました。そのため、解説は主にコミットメッセージとコードの変更内容に基づいて作成されています。)