[インデックス 13773] ファイルの概要
このコミットは、Go言語の標準ライブラリである go/scanner
パッケージのテストファイル src/pkg/go/scanner/scanner_test.go
に変更を加えています。このファイルは、Goソースコードを字句解析(スキャン)する go/scanner
パッケージが、様々な入力に対して正しく動作するかどうかを検証するための単体テストを含んでいます。
コミット
このコミットは、go/scanner
パッケージにおけるバイトオーダーマーク (BOM) の処理に関するテストを追加するものです。具体的には、ファイルの先頭に存在するBOMが正しく無視されること、そして2つ目以降のBOMが不正な文字として扱われることを検証するテストケースが追加されました。これにより、go/scanner
がBOMを含むUTF-8エンコードされたファイルを適切に処理できることが保証されます。
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2a391f467d25354477013cc28f2d41f8dae39604
元コミット内容
commit 2a391f467d25354477013cc28f2d41f8dae39604
Author: Robert Griesemer <gri@golang.org>
Date: Fri Sep 7 16:28:15 2012 -0700
go/scanner: add missing BOM test
R=r
CC=golang-dev
https://golang.org/cl/6498106
変更の背景
この変更の背景には、テキストファイルのエンコーディング、特にUTF-8におけるバイトオーダーマーク (BOM) の扱いの問題があります。BOMは、ファイルのエンコーディングやバイト順を示すためにファイルの先頭に挿入される特殊なバイト列です。UTF-8ではBOMは必須ではありませんが、一部のWindowsアプリケーション(例: メモ帳)がUTF-8ファイルをBOM付きで保存することがあります。
Go言語のコンパイラやツールチェーンは、Goソースファイルを処理する際に、これらのBOMを正しく扱う必要があります。特に、字句解析を行う go/scanner
パッケージは、ソースコードの先頭にBOMが存在する場合に、それを無視して実際のコードの解析を開始しなければなりません。しかし、BOMがファイルの途中に現れたり、複数のBOMが存在したりする場合には、それらを通常の文字として扱わず、エラーとして検出する必要があります。
このコミットは、go/scanner
がこれらのエッジケースを適切に処理していることを確認するためのテストが不足していたため、そのテストを追加することを目的としています。これにより、BOMの有無にかかわらず、Goソースコードが安定して解析されることが保証されます。
前提知識の解説
バイトオーダーマーク (BOM)
バイトオーダーマーク (BOM) は、Unicodeテキストファイルの先頭に置かれる特別なバイト列で、そのファイルのエンコーディング(UTF-8, UTF-16, UTF-32など)と、UTF-16/UTF-32の場合のバイト順(ビッグエンディアンまたはリトルエンディアン)を示すために使用されます。
- UTF-8におけるBOM: UTF-8のBOMは
EF BB BF
という3バイトのシーケンスです。UTF-8はバイト順の概念を持たないため、BOMは必須ではありません。むしろ、多くのUnix系システムやプログラミング言語のツールチェーンでは、BOM付きUTF-8ファイルは互換性の問題を引き起こす可能性があるため、推奨されません。BOMが存在すると、ファイルの内容の先頭に余分な文字があるかのように扱われ、スクリプトの実行や文字列処理で予期せぬ問題が発生することがあります。 - UTF-16/UTF-32におけるBOM: これらのエンコーディングでは、BOMはバイト順を示すために重要です。例えば、UTF-16のBOMは
FE FF
(ビッグエンディアン) またはFF FE
(リトルエンディアン) です。
Go言語の go/scanner
パッケージ
go/scanner
パッケージは、Go言語のソースコードを字句解析(lexical analysis)するための機能を提供します。字句解析とは、ソースコードの文字列を、キーワード、識別子、演算子、リテラルなどの意味のあるトークン(字句)のシーケンスに変換するプロセスです。このパッケージは、GoコンパイラやGoツール(go fmt
, go vet
など)の基盤となる重要なコンポーネントです。
go/scanner
は、入力されたバイトストリームを読み込み、Go言語の文法規則に従ってトークンを生成します。このプロセスにおいて、ファイルのエンコーディング、特にBOMの有無は、正しくトークンを識別するために重要になります。Go言語のソースコードはUTF-8で記述されることが標準であり、go/scanner
はUTF-8エンコーディングを前提としています。
技術的詳細
このコミットは、go/scanner
がBOMをどのように扱うべきかという仕様をテストによって明確にしています。
-
先頭のBOMの無視: GoのソースファイルがBOM付きUTF-8でエンコードされている場合、
go/scanner
はファイルの先頭にあるBOM (U+FEFF
) を無視し、実際のGoコードの解析を開始する必要があります。これは、BOMがソースコードの一部ではなく、エンコーディング情報であるためです。この動作は、"\ufeff#;"
というテストケースによって検証されます。この文字列は、BOMの後にGoのコメント開始を示す#
とセミコロン挿入を示す;
が続くことを想定しており、スキャナーがBOMを無視して#
を正しく認識することを確認します。 -
2つ目以降のBOMの不正文字扱い: ファイルの先頭以外にBOM (
U+FEFF
) が出現した場合、またはファイルの先頭に複数のBOMが連続して出現した場合、go/scanner
はそれらを不正な文字として扱う必要があります。これは、Goのソースコード内でU+FEFF
が有効な文字ではないためです。この動作は、{"\ufeff\ufeff", token.ILLEGAL, 3, "illegal character U+FEFF"}
というテストケースによって検証されます。このテストは、最初のBOMは無視されるが、2番目のBOMはillegal character U+FEFF
というエラーを発生させることを期待しています。token.ILLEGAL
は不正なトークンを意味し、3
はエラーが発生するバイトオフセット(最初のBOMが3バイトなので、その次から数えて3バイト目、つまり2番目のBOMの開始位置)を示しています。
これらのテストケースの追加により、go/scanner
がBOMの有無や位置に関わらず、Goソースコードを堅牢に解析できることが保証されます。これは、異なるエディタや環境で作成されたGoファイルが、Goツールチェーンによって一貫して処理されるために重要です。
コアとなるコードの変更箇所
変更は src/pkg/go/scanner/scanner_test.go
ファイルに対して行われました。
--- a/src/pkg/go/scanner/scanner_test.go
+++ b/src/pkg/go/scanner/scanner_test.go
@@ -346,6 +346,7 @@ var lines = []string{
// # indicates a semicolon present in the source
// $ indicates an automatically inserted semicolon
"",
+ "\ufeff#;", // first BOM is ignored
"#;",
"foo$\n",
"123$\n",
@@ -694,6 +695,7 @@ var errors = []struct {
{"0X", token.INT, 0, "illegal hexadecimal number"},
{"\"abc\x00def\"", token.STRING, 4, "illegal character NUL"},
{"\"abc\x80def\"", token.STRING, 4, "illegal UTF-8 encoding"},
+ {"\ufeff\ufeff", token.ILLEGAL, 3, "illegal character U+FEFF"}, // only first BOM is ignored
}
func TestScanErrors(t *testing.T) {
コアとなるコードの解説
このコミットでは、scanner_test.go
内の2つのテストデータ構造に新しいエントリが追加されています。
-
lines
配列への追加:"\ufeff#;", // first BOM is ignored
lines
配列は、go/scanner
が正しく字句解析できると期待されるGoコードのスニペットを含んでいます。ここに追加された"\ufeff#;"
は、UTF-8 BOM (\ufeff
はUnicodeのU+FEFF文字を表すGoの文字列リテラル表現) の後にコメント開始文字#
とセミコロン挿入を示す;
が続く文字列です。 このテストケースは、go/scanner
がファイルの先頭にあるBOMを完全に無視し、その後の#;
を通常のGoコードとして正しく解析できることを検証します。もしBOMが無視されなければ、スキャナーは予期せぬ文字としてエラーを報告するか、誤ったトークンを生成する可能性があります。 -
errors
配列への追加:{"\ufeff\ufeff", token.ILLEGAL, 3, "illegal character U+FEFF"}, // only first BOM is ignored
errors
配列は、go/scanner
が字句解析中にエラーを報告すると期待される不正なGoコードのスニペットと、期待されるエラー情報(トークンタイプ、オフセット、エラーメッセージ)を含んでいます。ここに追加された{"\ufeff\ufeff", token.ILLEGAL, 3, "illegal character U+FEFF"}
は、2つの連続するUTF-8 BOMを含む文字列です。 このテストケースは、go/scanner
が最初のBOMは無視するものの、2番目のBOMは不正な文字 (U+FEFF
) として検出し、指定されたオフセット (3
) でillegal character U+FEFF
というエラーメッセージを生成することを検証します。これは、go/scanner
がBOMを一度だけ、かつファイルの先頭でのみ特別扱いし、それ以外のBOMの出現はエラーとして扱うという厳密なルールを適用していることを示しています。
これらのテストの追加により、go/scanner
のBOM処理ロジックが、意図した通りに堅牢かつ正確であることを保証するための重要な検証ポイントが確立されました。
関連リンク
- Go言語の
go/scanner
パッケージのドキュメント: https://pkg.go.dev/go/scanner - Go言語の
go/token
パッケージのドキュメント (トークン定義): https://pkg.go.dev/go/token - Go言語のソースコードエンコーディングに関する議論 (Go 1の仕様): https://go.dev/doc/go1#encoding
参考にした情報源リンク
- バイトオーダーマーク (BOM) について:
- Go言語のコードレビューシステム (Gerrit) の変更リスト: https://golang.org/cl/6498106 (これはコミットメッセージに記載されているリンクであり、元の変更提案の詳細が含まれています)
- Go言語のソースコードのエンコーディングに関する一般的な情報源。