[インデックス 13813] ファイルの概要
このコミットは、Go言語のgo/build
パッケージにおけるImport
関数の挙動を修正し、空の文字列がインポートパスとして渡された場合にエラーを返すように変更したものです。これにより、不正なインポートパスによる予期せぬ動作を防ぎ、堅牢性を向上させています。
コミット
commit ec9967ff11851facad48bb0c11639e52a77f79b9
Author: Francisco Souza <franciscossouza@gmail.com>
Date: Thu Sep 13 10:25:35 2012 -0400
go/build: reject empty strings in Import
Fixes #3889.
R=rsc, adg
CC=golang-dev
https://golang.org/cl/6499102
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ec9967ff11851facad48bb0c11639e52a77f79b9
元コミット内容
go/build
パッケージのImport
関数において、空の文字列がインポートパスとして渡された場合に、それを拒否するように変更しました。これにより、Issue #3889で報告された問題が修正されます。
変更の背景
この変更は、Go言語のIssue #3889「go/build
: Import("")
should return an error」に対応するものです。以前のgo/build.Import
関数は、空の文字列をインポートパスとして受け取った場合、エラーを返さずに*Package
構造体を返していました。これは、通常、有効なパッケージパスではないため、予期せぬ動作や後続の処理でのエラーを引き起こす可能性がありました。
例えば、ツールがユーザーからの入力を処理する際に、誤って空の文字列がImport
関数に渡されると、その後のビルドプロセスが混乱したり、意味のないパッケージ情報が生成されたりする可能性がありました。このコミットは、このような不正な入力に対して早期にエラーを返すことで、ツールの堅牢性と信頼性を向上させることを目的としています。
前提知識の解説
Go言語のgo/build
パッケージ
go/build
パッケージは、Go言語のソースコードを解析し、パッケージの依存関係やビルドに関する情報を提供する標準ライブラリです。go build
コマンドやgo get
コマンドなど、Goのツールチェインの多くの部分で内部的に利用されています。
このパッケージの主な機能は以下の通りです。
- パッケージの解決: 指定されたインポートパスに対応するパッケージのソースディレクトリを特定します。
- パッケージ情報の取得: パッケージ内のGoソースファイル、C/C++/Assemblyソースファイル、テストファイルなどの情報を収集します。
- ビルドタグの処理: ビルドタグ(例:
// +build linux,amd64
)に基づいて、どのファイルをビルドに含めるかを決定します。 - コンテキストの管理: ビルド環境(GOROOT, GOPATH, OS, Archなど)を管理し、それに基づいてパッケージを解決します。
go/build.Import
関数
go/build.Import
関数は、go/build
パッケージの中心的な関数の一つです。この関数は、指定されたインポートパスに基づいてGoパッケージを検索し、そのパッケージに関する詳細情報を含む*Package
構造体を返します。
関数のシグネチャは以下のようになっています(コミット当時のバージョンに近いもの):
func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Package, error)
path
: 検索するパッケージのインポートパス(例:"fmt"
,"github.com/user/repo/pkg"
)。srcDir
:path
が相対パスの場合の基準となるディレクトリ。通常は現在の作業ディレクトリや、インポート元のファイルのディレクトリが指定されます。mode
: インポートのモードを指定するフラグ(例:FindOnly
,ImportComment
,AllowBinary
など)。FindOnly
はパッケージの検索のみを行い、詳細な解析は行いません。
この関数は、成功した場合は*Package
とnil
エラーを返し、パッケージが見つからない場合やエラーが発生した場合はnil
とエラーを返します。
Go言語のエラーハンドリング
Go言語では、エラーは戻り値として明示的に扱われます。関数がエラーを返す可能性がある場合、通常は最後の戻り値としてerror
型の値を返します。呼び出し元は、このerror
値がnil
であるかどうかをチェックすることで、処理が成功したか失敗したかを判断します。
fmt.Errorf
関数は、フォーマットされた文字列から新しいerror
値を生成するために使用されます。これは、エラーメッセージに動的な情報を含める場合に便利です。
技術的詳細
このコミットの技術的な変更点は、go/build.Import
関数の内部に、インポートパスが空文字列である場合のチェックを追加したことです。
変更前のImport
関数は、path
引数が空文字列であっても、特にエラーを返さずに処理を進めていました。これにより、*Package
構造体のImportPath
フィールドが空文字列のままのオブジェクトが生成され、後続の処理で問題を引き起こす可能性がありました。
変更後のコードでは、Import
関数の冒頭でpath == ""
という条件チェックが追加されています。
if path == "" {
return p, fmt.Errorf("import %q: invalid import path", path)
}
このチェックにより、path
が空文字列である場合、fmt.Errorf
を使用して「import "": invalid import path
」というエラーメッセージを含むerror
オブジェクトが生成され、即座にそのエラーが返されるようになりました。この際、p
(*Package
型のポインタ)は、ImportPath
が空文字列で初期化された状態のものが返されます。これは、エラーが発生した場合でも、可能な限り部分的な情報を提供しようとするGoの慣習に沿ったものです。
また、この変更には対応するテストケースTestEmptyImport
がsrc/pkg/go/build/build_test.go
に追加されています。このテストは、Import
関数に空文字列を渡した場合に、期待通りエラーが返されること、そして返された*Package
オブジェクトのImportPath
が空文字列であることを検証しています。
func TestEmptyImport(t *testing.T) {
p, err := Import("", Default.GOROOT, FindOnly)
if err == nil {
t.Fatal(`Import("") returned nil error.`)
}
if p == nil {
t.Fatal(`Import("") returned nil package.`)
}
if p.ImportPath != "" {
t.Fatalf("ImportPath=%q, want %q.", p.ImportPath, "")
}
}
このテストは以下の点を保証します。
Import("")
がnil
ではないエラーを返すこと。Import("")
がnil
ではない*Package
ポインタを返すこと(エラー時でも部分的な情報が返されることを確認)。- 返された
*Package
のImportPath
が空文字列であること。
これにより、この変更が正しく機能し、将来のリグレッションを防ぐための自動化された検証が提供されます。
コアとなるコードの変更箇所
src/pkg/go/build/build.go
--- a/src/pkg/go/build/build.go
+++ b/src/pkg/go/build/build.go
@@ -351,6 +351,9 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa
p := &Package{
ImportPath: path,
}
+ if path == "" {
+ return p, fmt.Errorf("import %q: invalid import path", path)
+ }
var pkga string
var pkgerr error
src/pkg/go/build/build_test.go
--- a/src/pkg/go/build/build_test.go
+++ b/src/pkg/go/build/build_test.go
@@ -61,6 +61,19 @@ func TestDotSlashImport(t *testing.T) {\n }\n }\n \n+func TestEmptyImport(t *testing.T) {\n+\tp, err := Import(\"\", Default.GOROOT, FindOnly)\n+\tif err == nil {\n+\t\tt.Fatal(`Import(\"\") returned nil error.`)\n+\t}\n+\tif p == nil {\n+\t\tt.Fatal(`Import(\"\") returned nil package.`)\n+\t}\n+\tif p.ImportPath != \"\" {\n+\t\tt.Fatalf(\"ImportPath=%q, want %q.\", p.ImportPath, \"\")\n+\t}\n+}\n+\n func TestLocalDirectory(t *testing.T) {\n cwd, err := os.Getwd()\n if err != nil {\n```
## コアとなるコードの解説
### `src/pkg/go/build/build.go` の変更
`Import`関数の内部で、`Package`構造体を初期化した直後に、`path`変数が空文字列であるかどうかのチェックが追加されました。
`if path == ""`という条件が真(つまり、インポートパスが空文字列)の場合、`fmt.Errorf`を使ってエラーメッセージ「`import "": invalid import path`」を生成し、そのエラーと初期化された`Package`構造体`p`を返して関数の実行を終了します。これにより、不正なインポートパスが早期に検出され、処理が中断されるようになります。
### `src/pkg/go/build/build_test.go` の変更
`TestEmptyImport`という新しいテスト関数が追加されました。
このテストは、`Import`関数に空文字列`""`をインポートパスとして渡し、`Default.GOROOT`と`FindOnly`モードを使用します。
テストの目的は以下の3点です。
1. `err == nil`の場合、つまりエラーが返されなかった場合に`t.Fatal`を呼び出してテストを失敗させます。これは、空文字列のインポートパスに対してエラーが返されることを期待しているためです。
2. `p == nil`の場合、つまり`*Package`ポインタが`nil`であった場合に`t.Fatal`を呼び出してテストを失敗させます。これは、エラー時でも`*Package`構造体自体は(たとえ不完全でも)返されることを期待しているためです。
3. `p.ImportPath != ""`の場合、つまり返された`*Package`の`ImportPath`フィールドが空文字列でなかった場合に`t.Fatalf`を呼び出してテストを失敗させます。これは、空文字列がインポートパスとして渡された場合、`ImportPath`フィールドも空文字列のままであることを確認するためです。
これらの変更により、`go/build.Import`関数はより堅牢になり、不正な入力に対して明確なエラーを返すようになりました。
## 関連リンク
* Go Issue #3889: [https://github.com/golang/go/issues/3889](https://github.com/golang/go/issues/3889)
* Go CL 6499102: [https://golang.org/cl/6499102](https://golang.org/cl/6499102)
## 参考にした情報源リンク
* Go言語公式ドキュメント: `go/build`パッケージ ([https://pkg.go.dev/go/build](https://pkg.go.dev/go/build))
* Go言語のエラーハンドリングに関する一般的な情報
* Go言語のテストに関する一般的な情報
* GitHubのコミットページ ([https://github.com/golang/go/commit/ec9967ff11851facad48bb0c11639e52a77f79b9](https://github.com/golang/go/commit/ec9967ff11851facad48bb0c11639e52a77f79b9))
* Go言語のIssueトラッカー ([https://github.com/golang/go/issues](https://github.com/golang/go/issues))
* Go Code Review ([https://go.dev/doc/contribute#code_reviews](https://go.dev/doc/contribute#code_reviews))
* Go CL (Change List) ([https://go.dev/doc/contribute#change_lists](https://go.dev/doc/contribute#change_lists))