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

[インデックス 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をどのように扱うべきかという仕様をテストによって明確にしています。

  1. 先頭のBOMの無視: GoのソースファイルがBOM付きUTF-8でエンコードされている場合、go/scanner はファイルの先頭にあるBOM (U+FEFF) を無視し、実際のGoコードの解析を開始する必要があります。これは、BOMがソースコードの一部ではなく、エンコーディング情報であるためです。この動作は、"\ufeff#;" というテストケースによって検証されます。この文字列は、BOMの後にGoのコメント開始を示す # とセミコロン挿入を示す ; が続くことを想定しており、スキャナーがBOMを無視して # を正しく認識することを確認します。

  2. 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つのテストデータ構造に新しいエントリが追加されています。

  1. lines 配列への追加:

    "\ufeff#;", // first BOM is ignored
    

    lines 配列は、go/scanner が正しく字句解析できると期待されるGoコードのスニペットを含んでいます。ここに追加された "\ufeff#;" は、UTF-8 BOM (\ufeff はUnicodeのU+FEFF文字を表すGoの文字列リテラル表現) の後にコメント開始文字 # とセミコロン挿入を示す ; が続く文字列です。 このテストケースは、go/scanner がファイルの先頭にあるBOMを完全に無視し、その後の #; を通常のGoコードとして正しく解析できることを検証します。もしBOMが無視されなければ、スキャナーは予期せぬ文字としてエラーを報告するか、誤ったトークンを生成する可能性があります。

  2. 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処理ロジックが、意図した通りに堅牢かつ正確であることを保証するための重要な検証ポイントが確立されました。

関連リンク

参考にした情報源リンク