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

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

このコミットは、Goコンパイラ(gc)において、インポートパスにバックスラッシュ(\)が含まれている場合に、より明確なエラーメッセージを出力するように改善するものです。また、この新しい診断機能が正しく動作することを確認するためのテストケースが追加されています。Go言語のインポートパスはスラッシュ(/)区切りが標準であり、バックスラッシュはファイルシステムパスの区切り文字としてWindows環境などで使用されますが、Goのインポートパスとしては不正です。この変更により、ユーザーが誤ったパス区切り文字を使用した際に、より分かりやすいフィードバックが得られるようになります。

コミット

commit 33f3afa7afac66f91ba281521db26ea932ead5a4
Author: Russ Cox <rsc@golang.org>
Date:   Tue Jan 31 17:29:59 2012 -0500

    gc: diagnose \ in import path
    
    R=ken2
    CC=golang-dev
    https://golang.org/cl/5609044

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

https://github.com/golang/go/commit/33f3afa7afac66f91ba281521db26ea932ead5a4

元コミット内容

このコミットは、Goコンパイラ(gc)の字句解析器(lex.c)に、インポートパスの検証ロジックを追加します。具体的には、インポートパス文字列内にバックスラッシュ(\)が含まれていないかをチェックし、もし含まれていれば「import path contains backslash; use slash」(インポートパスにバックスラッシュが含まれています。スラッシュを使用してください)というエラーメッセージを出力してコンパイルを中断します。

また、この新しいエラー診断機能を検証するために、test/import5.goという新しいテストファイルが追加されています。このテストファイルは、意図的にimport "net\http"という不正なインポートパスを含んでおり、コンパイラが期待通りにエラーを報告することを確認します。

変更の背景

Go言語の設計思想の一つに、クロスプラットフォームでの一貫性があります。ファイルパスの区切り文字は、Unix系システムではスラッシュ(/)、Windows系システムではバックスラッシュ(\)が一般的です。しかし、Goのパッケージインポートパスは、どのオペレーティングシステム上でも常にスラッシュ(/)を使用するように定められています。これは、Goのソースコードが異なる環境間で移植される際に、インポートパスの記述方法によって問題が発生するのを防ぐためです。

このコミットが作成された2012年当時、Go言語はまだ比較的新しく、多くの開発者がGoの慣習や仕様に慣れていない時期でした。特にWindows環境で開発を行っていたユーザーは、ファイルパスの習慣からインポートパスにもバックスラッシュを使用してしまいがちでした。しかし、当時のGoコンパイラは、このような不正なインポートパスに対して、必ずしも明確なエラーメッセージを出力していませんでした。その結果、ユーザーはなぜコンパイルエラーが発生するのか、あるいはパッケージが見つからないのかを理解するのに苦労する可能性がありました。

この変更の背景には、ユーザーエクスペリエンスの向上と、Go言語のインポートパスに関する正しい慣習を開発者に促すという目的があります。明確なエラーメッセージを提供することで、ユーザーは問題の原因を迅速に特定し、正しい修正を行うことができるようになります。

前提知識の解説

Go言語のパッケージとインポートパス

Go言語では、コードは「パッケージ」という単位で整理されます。パッケージは、関連する機能や型、変数をまとめたもので、他のパッケージから利用することができます。他のパッケージの機能を利用するには、importキーワードを使ってそのパッケージをインポートする必要があります。

インポートパスは、Goのソースコードがどこにあるかをコンパイラに伝えるための文字列です。Goの標準ライブラリのパッケージ(例: fmt, net/http)は、その名前がそのままインポートパスになります。サードパーティのパッケージや自身のプロジェクト内のパッケージは、通常、バージョン管理システムのリポジトリパス(例: github.com/user/repo/package)や、GOPATH環境変数で指定されたワークスペース内の相対パスとして解決されます。

重要な点として、Goのインポートパスは、オペレーティングシステムに関わらず、常にスラッシュ(/)を区切り文字として使用します。 これは、Goのビルドシステムがインポートパスを解決する際の標準的な動作であり、クロスプラットフォーム互換性を保証するために不可欠です。

Goコンパイラ(gc

gcは、Go言語の公式コンパイラです。Goのソースコードを機械語に変換する役割を担っています。コンパイルプロセスは、字句解析、構文解析、意味解析、中間コード生成、最適化、コード生成など、複数のフェーズに分かれています。

  • 字句解析(Lexical Analysis): ソースコードをトークン(キーワード、識別子、演算子など)のストリームに分解するフェーズです。このコミットで変更されるlex.cは、この字句解析に関連する処理、特に文字列リテラルやパスの解釈に関わっていると考えられます。
  • エラー診断: コンパイラは、ソースコードに文法エラーや意味エラーがある場合に、それを検出し、ユーザーにエラーメッセージを報告します。このエラーメッセージは、開発者が問題を理解し、修正するために非常に重要です。

C言語のstrchr関数

strchrはC標準ライブラリの関数で、文字列(char *)の中から特定の文字が最初に現れる位置を検索します。 char *strchr(const char *str, int c); strで指定された文字列内で、cで指定された文字(int型だがcharとして扱われる)を検索し、最初に見つかった位置へのポインタを返します。見つからなかった場合はNULLを返します。

このコミットでは、strchr(f->u.sval->s, '\\')という形で使用されており、インポートパス文字列f->u.sval->sの中にバックスラッシュ文字'\\'が含まれているかをチェックしています。

技術的詳細

このコミットの技術的な核心は、Goコンパイラの字句解析フェーズにおけるインポートパスの検証強化にあります。

  1. src/cmd/gc/lex.cの変更:

    • このファイルは、Goコンパイラのフロントエンドの一部であり、ソースコードの字句解析(トークン化)や、インポートパスのような文字列の処理を担当しています。
    • importfile関数は、import文で指定されたパスを処理する役割を担っています。この関数内で、インポートパス文字列が有効であるかどうかの追加チェックが行われるようになりました。
    • 追加されたコードは、strchr関数を使用して、インポートパス文字列(f->u.sval->s)にバックスラッシュ文字('\\')が含まれているかを効率的に検出します。
    • もしバックスラッシュが検出された場合、yyerror関数(コンパイラのエラー報告メカニズム)を呼び出して、具体的なエラーメッセージ「import path contains backslash; use slash」を出力します。
    • その後、errorexit()を呼び出してコンパイルプロセスを即座に終了させます。これにより、不正なインポートパスが後続のコンパイルフェーズで予期せぬ問題を引き起こすのを防ぎ、ユーザーに早期にエラーを通知します。
  2. test/import5.goの追加:

    • Goプロジェクトでは、コンパイラの挙動や言語仕様の変更を検証するために、広範なテストスイートが用意されています。testディレクトリ内のファイルは、Goのテストフレームワークとは異なる、コンパイラ自身のテストに使われることが多いです。
    • // errchk $G -e $D/$F.goという行は、このファイルがコンパイラのエラーチェックテストであることを示しています。$GはGoコンパイラへのパス、-eはエラーが期待されることを示し、$D/$F.goは現在のテストファイルのパスを指します。
    • import net\http``という行は、意図的に不正なインポートパスを含んでいます。これは、src/cmd/gc/lex.cで追加された診断ロジックがこのバックスラッシュを検出し、エラーを報告することを期待しています。
    • // ERROR "backslash"というコメントは、テストハーネスに対して、コンパイラの出力に「backslash」という文字列が含まれるエラーメッセージが出力されることを期待していることを伝えます。これにより、診断メッセージの内容まで含めて正確にテストされます。

この変更は、Goコンパイラの堅牢性を高め、ユーザーがGoのインポートパスの規則をより簡単に理解できるようにするための、小さなしかし重要な改善です。

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

src/cmd/gc/lex.c

--- a/src/cmd/gc/lex.c
+++ b/src/cmd/gc/lex.c
@@ -574,6 +574,11 @@ importfile(Val *f, int line)
 		yyerror("import path contains NUL");
 		errorexit();
 	}
+	
+	if(strchr(f->u.sval->s, '\\')) {
+		yyerror("import path contains backslash; use slash");
+		errorexit();
+	}
 
 	// The package name main is no longer reserved,
 	// but we reserve the import path "main" to identify

test/import5.go

--- /dev/null
+++ b/test/import5.go
@@ -0,0 +1,11 @@
+// errchk $G -e $D/$F.go
+
+// Copyright 2009 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.
+
+// import paths are slash-separated; reject backslash
+
+package main
+
+import `net\http`  // ERROR "backslash"

コアとなるコードの解説

src/cmd/gc/lex.cの変更点

importfile関数は、Goのimport文で指定されたパスを処理するGoコンパイラの内部関数です。 追加されたコードブロックは以下の通りです。

if(strchr(f->u.sval->s, '\\')) {
    yyerror("import path contains backslash; use slash");
    errorexit();
}
  • f->u.sval->s: これは、現在処理されているインポートパスの文字列データへのポインタです。fVal型のポインタで、u.svalは共用体(union)内の文字列値、sはその文字列データ自体を指します。
  • strchr(f->u.sval->s, '\\'): C標準ライブラリのstrchr関数を呼び出しています。これは、f->u.sval->sで示される文字列の中に、バックスラッシュ文字'\\'が最初に現れる位置を検索します。もしバックスラッシュが見つかれば、その位置へのポインタを返します。見つからなければNULLを返します。
  • if(...): strchrNULL以外の値を返した場合(つまり、バックスラッシュが見つかった場合)に、ifブロック内のコードが実行されます。
  • yyerror("import path contains backslash; use slash");: yyerrorは、Goコンパイラがエラーメッセージを出力するために使用する内部関数です。この行は、ユーザーに対して「インポートパスにバックスラッシュが含まれています。スラッシュを使用してください」という明確なエラーメッセージを表示します。
  • errorexit();: この関数は、コンパイルプロセスを直ちに終了させます。これにより、不正なインポートパスが原因で後続のコンパイルフェーズでさらに複雑なエラーが発生するのを防ぎ、ユーザーに問題の早期解決を促します。

この変更により、Goコンパイラは、インポートパスの誤用に対してよりユーザーフレンドリーな診断を提供するようになりました。

test/import5.goの解説

このファイルは、Goコンパイラのテストスイートの一部として機能します。

// errchk $G -e $D/$F.go
  • この行は、Goのテストハーネスに対する指示です。errchkは、このテストがコンパイルエラーをチェックするためのものであることを示します。
  • $G: Goコンパイラの実行可能ファイルへのパスを表す変数です。
  • -e: コンパイル時にエラーが発生することを期待していることを示します。
  • $D/$F.go: 現在のテストファイルの絶対パスを表す変数です。 この行全体で、「Goコンパイラでこのファイルをコンパイルし、エラーが発生することを期待する」というテストの意図を定義しています。
// import paths are slash-separated; reject backslash
  • このコメントは、このテストの目的を簡潔に説明しています。Goのインポートパスはスラッシュ区切りであり、バックスラッシュは拒否されるべきであるという原則を述べています。
package main

import `net\http`  // ERROR "backslash"
  • package main: このファイルが実行可能なプログラムのエントリポイントであることを示します。
  • import net\http``: この行がテストの核心です。標準ライブラリのnet/httpパッケージをインポートしようとしていますが、意図的にパス区切り文字としてバックスラッシュ\を使用しています。これはGoのインポートパスの規則に違反しています。
  • // ERROR "backslash": このコメントは、テストハーネスに対して、コンパイラがこの行でエラーを報告し、そのエラーメッセージに「backslash」という文字列が含まれることを期待していることを伝えます。これにより、コンパイラが正しいエラーメッセージを出力しているかどうかも検証されます。

このテストファイルは、src/cmd/gc/lex.cで実装された新しい診断ロジックが、期待通りに不正なインポートパスを検出し、適切なエラーメッセージを出力することを確認するためのものです。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント(特にパッケージとインポートに関するセクション)
  • Go言語のソースコード(src/cmd/gc/lex.cおよびtest/ディレクトリ内の関連テストファイル)
  • C言語のstrchr関数のドキュメント
  • Go言語のコミット履歴と関連するコードレビュー(https://golang.org/cl/5609044
  • Go言語のコミュニティフォーラムやメーリングリストでの議論(インポートパスに関する一般的な質問や問題提起)
  • Go言語のビルドシステムに関する一般的な知識
  • オペレーティングシステムにおけるファイルパスの区切り文字に関する一般的な知識