[インデックス 17661] ファイルの概要
コミット
commit 5be1821a31f1b88b8b9b9083454143f5aa90790d
Author: Russ Cox <rsc@golang.org>
Date: Fri Sep 20 15:25:43 2013 -0400
cmd/gc: fix imported and not used error for import .
Fixes issues 6420.
R=ken2
CC=golang-dev
https://golang.org/cl/13703044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/5be1821a31f1b88b8b9b9083454143f5aa90790d
元コミット内容
cmd/gc: fix imported and not used error for import .
このコミットは、Goコンパイラ(cmd/gc
)において、import .
(ドットインポート)を使用した場合に発生する「imported and not used」(インポートされたが使用されていない)というエラーが誤って報告される問題を修正するものです。具体的には、Go言語のIssue 6420を解決します。
変更の背景
Go言語では、パッケージをインポートする際に、そのパッケージ内のエクスポートされた識別子を修飾なしで直接参照できるようにする「ドットインポート」(import . "package_name"
)という機能があります。例えば、import . "fmt"
とすると、fmt.Println
ではなくPrintln
と書けるようになります。
しかし、Goコンパイラには、インポートされたパッケージがコード内で実際に使用されていない場合に「imported and not used」というエラーを報告する機能があります。これは、未使用のインポートを排除し、コードのクリーンさを保つための重要な機能です。
このコミットが修正しようとしている問題は、import .
を使用した場合に、コンパイラがそのインポートを正しく「使用済み」と認識せず、実際には使用されているにもかかわらず「imported and not used」エラーを誤って報告してしまうというバグでした。Go言語のIssue 6420で報告されたこの問題は、開発者がドットインポートを意図的に使用しているにもかかわらず、不必要なエラーに直面するという不便さを引き起こしていました。
前提知識の解説
Go言語のパッケージインポート
Go言語では、コードをモジュール化するためにパッケージを使用します。他のパッケージの機能を利用するには、import
キーワードを使ってそのパッケージをインポートする必要があります。
- 通常のインポート:
import "fmt"
のように記述し、パッケージ内の識別子を参照する際にはfmt.Println
のようにパッケージ名を修飾します。 - エイリアスインポート:
import io "fmt"
のように記述し、io.Println
のようにエイリアス名で参照します。 - ブランクインポート:
import _ "image/png"
のように記述し、パッケージの初期化処理のみを実行し、パッケージ内の識別子を直接使用しない場合に用います。 - ドットインポート:
import . "fmt"
のように記述し、インポートしたパッケージ内のエクスポートされた識別子を、パッケージ名を修飾せずに直接参照できるようにします。これは、テストコードや、特定のパッケージの関数を頻繁に使う場合にコードを簡潔にするために使われることがありますが、名前の衝突やコードの可読性低下を招く可能性があるため、一般的には推奨されません。
Goコンパイラの「imported and not used」エラー
Goコンパイラは、インポートされたパッケージがコード内で一度も使用されていない場合に、コンパイルエラーとして「imported and not used」を報告します。これは、不要な依存関係を排除し、ビルド時間を短縮し、コードベースを整理するためのGo言語の設計思想の一部です。このエラーは、開発者が誤ってインポートを追加したり、コードの変更によってインポートが不要になったりした場合に役立ちます。
cmd/gc
cmd/gc
は、Go言語の公式コンパイラの一部であり、Goソースコードを機械語に変換する役割を担っています。このコンパイラは、構文解析、型チェック、最適化、コード生成など、コンパイルプロセスの様々な段階を担当します。今回の修正は、このコンパイラのセマンティック分析(意味解析)部分、特にインポートの使用状況を追跡するロジックに関連しています。
技術的詳細
このコミットの技術的な核心は、Goコンパイラが「ドットインポート」されたパッケージの使用状況をどのように判断するか、という点にあります。
Goコンパイラには、pkgnotused
という関数が存在します。この関数は、インポートされたパッケージが使用されていない場合にエラーを報告する役割を担っています。元の実装では、pkgnotused
関数は、インポートされたパッケージのパスと、そのパッケージがエイリアスとしてインポートされた場合のname
(エイリアス名)を受け取っていました。
問題は、import . "package_name"
のようにドットインポートされた場合、エイリアス名がnil
(またはそれに相当する値)として渡されるべきなのに、元のコンパイラのロジックでは、このname
引数がnil
であるケースが適切に処理されていなかった点にあります。
具体的には、pkgnotused
関数内の以下の条件分岐が問題でした。
if(strcmp(elem, name) == 0)
ここでname
がnil
である場合、strcmp
関数は未定義の動作を引き起こす可能性があり、結果としてドットインポートが正しく「使用済み」と判断されず、誤って「imported and not used」エラーが報告されていました。
このコミットでは、この問題を解決するために、strcmp
の呼び出しの前にname == nil
というチェックを追加しています。
if(name == nil || strcmp(elem, name) == 0)
この変更により、name
がnil
(つまりドットインポートの場合)であれば、strcmp
の評価をスキップして、そのインポートが使用されていると見なすようになります。これにより、ドットインポートが正しく処理され、誤ったエラー報告がなくなります。
また、mkpackage
関数内のpkgnotused
の呼び出しも修正されています。
pkgnotused(s->def->pack->lineno, s->def->pack->pkg->path, s->name); // 修正前
pkgnotused(s->def->pack->lineno, s->def->pack->pkg->path, nil); // 修正後
修正前は、ドットインポートの場合でもs->name
(これはエイリアス名、またはトップレベルの名前)が渡されていましたが、修正後は明示的にnil
が渡されるようになりました。これにより、pkgnotused
関数がドットインポートを正しく識別し、適切なロジックを適用できるようになります。
コアとなるコードの変更箇所
変更は主に src/cmd/gc/lex.c
ファイルにあります。
--- a/src/cmd/gc/lex.c
+++ b/src/cmd/gc/lex.c
@@ -2296,7 +2296,7 @@ pkgnotused(int lineno, Strlit *path, char *name)
elem++;
else
elem = path->s;
- if(strcmp(elem, name) == 0)
+ if(name == nil || strcmp(elem, name) == 0)
yyerrorl(lineno, "imported and not used: \"%Z\"", path);
else
yyerrorl(lineno, "imported and not used: \"%Z\" as %s", path, name);
@@ -2335,7 +2335,7 @@ mkpackage(char* pkgname)
// throw away top-level name left over
// from previous import . "x"
if(s->def->pack != N && !s->def->pack->used && !nsyntaxerrors) {
- pkgnotused(s->def->pack->lineno, s->def->pack->pkg->path, s->name);
+ pkgnotused(s->def->pack->lineno, s->def->pack->pkg->path, nil);
s->def->pack->used = 1;
}
s->def = N;
また、テストケース test/import1.go
も更新されています。
--- a/test/import1.go
+++ b/test/import1.go
@@ -14,5 +14,6 @@ import bufio "os" // ERROR "redeclared|redefinition|incompatible" "imported and
import (
"fmt" // GCCGO_ERROR "previous|not used"
- fmt "math" // ERROR "redeclared|redefinition|incompatible" "imported and not used"
+ fmt "math" // ERROR "redeclared|redefinition|incompatible" "imported and not used: \"x22math\\x22 as fmt"
+ . "math" // ERROR "imported and not used: \"x22math\\x22$"
)
コアとなるコードの解説
src/cmd/gc/lex.c
の変更
-
pkgnotused
関数の変更:- 元のコード:
if(strcmp(elem, name) == 0)
- 変更後:
if(name == nil || strcmp(elem, name) == 0)
- この変更が最も重要です。
pkgnotused
関数は、インポートされたパッケージが使用されているかどうかをチェックし、使用されていない場合にエラーを報告します。name
引数は、インポートにエイリアスが指定されている場合にそのエイリアス名が入ります。ドットインポートの場合、エイリアスは存在しないため、name
はnil
であるべきです。 - 変更前は、
name
がnil
の場合にstrcmp
が呼び出され、不正なメモリアクセスや予期せぬ動作を引き起こす可能性がありました。 - 変更後は、
name == nil
という条件が追加されたことで、name
がnil
であればstrcmp
の評価をスキップし、そのインポートが「使用済み」であると判断されるようになります。これにより、ドットインポートが正しく処理され、誤った「imported and not used」エラーが回避されます。
- 元のコード:
-
mkpackage
関数内のpkgnotused
呼び出しの変更:- 元のコード:
pkgnotused(s->def->pack->lineno, s->def->pack->pkg->path, s->name);
- 変更後:
pkgnotused(s->def->pack->lineno, s->def->pack->pkg->path, nil);
mkpackage
関数は、パッケージの定義を処理する際に呼び出されます。この部分では、以前のドットインポートによって残されたトップレベルの名前を破棄するロジックの一部として、pkgnotused
が呼び出されていました。- 修正前は、
s->name
がpkgnotused
に渡されていましたが、これはドットインポートの場合には適切ではありませんでした。 - 修正後は、明示的に
nil
が渡されるようになりました。これにより、pkgnotused
関数がドットインポートのケースを正しく認識し、上記のname == nil
チェックが有効に機能するようになります。
- 元のコード:
test/import1.go
の変更
- 新しいテストケースとして、
. "math"
のドットインポートが追加されました。 - このテストケースは、このコミットによって修正された問題が実際に解決されたことを検証するために追加されました。修正前であれば、この行で「imported and not used」エラーが報告されていたはずですが、修正後は期待通りにエラーが報告されなくなります(ただし、このテストファイル自体は他のエラーを意図的に含んでいるため、全体としてはエラーになります)。
- 既存の
fmt "math"
のエラーメッセージも、より詳細な情報を含むように更新されています。これは、このコミットの直接的な修正とは異なりますが、関連するエラーメッセージの改善の一環として行われた可能性があります。
これらの変更により、Goコンパイラはドットインポートを正しく処理し、開発者が意図した通りにコードを記述できるようになりました。
関連リンク
- Go Issue 6420: https://github.com/golang/go/issues/6420
- Go CL 13703044: https://go.dev/cl/13703044 (Goのコードレビューシステムへのリンク)
参考にした情報源リンク
- Go言語の公式ドキュメント (パッケージとインポートに関するセクション)
- Go言語のソースコード (特に
src/cmd/gc/lex.c
) - Go言語のIssueトラッカー (Issue 6420の議論)
strcmp
関数のC言語リファレンス- Go言語のコンパイラ設計に関する一般的な知識