[インデックス 16916] ファイルの概要
このコミットは、Go言語のパーサーライブラリである go/parser
におけるエラー報告の挙動を修正するものです。具体的には、DeclarationErrors
フラグが設定されていない場合に、パッケージ名がアンダースコア _
であることに関するエラー(invalid package name _
)を報告しないように変更しています。これにより、パーサーの柔軟性が向上し、特定のユースケースにおいて不要なエラー報告を抑制できるようになります。
コミット
go/parser: don't report name errors if DeclarationErrors is not set
R=adonovan
CC=golang-dev
https://golang.org/cl/12072043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/3eb9adeeb8261662f87d8cb20f5e1c95a2506228
元コミット内容
go/parser: don't report name errors if DeclarationErrors is not set
R=adonovan
CC=golang-dev
https://golang.org/cl/12072043
変更の背景
Go言語の公式パーサーである go/parser
は、Goソースコードを抽象構文木(AST: Abstract Syntax Tree)に変換する役割を担っています。このパーサーは、構文エラーだけでなく、一部のセマンティックなエラー(宣言に関するエラーなど)も報告する機能を持っています。
Go言語の仕様では、パッケージ名は識別子として扱われますが、特別な意味を持つアンダースコア _
(ブランク識別子)をパッケージ名として使用することは許可されていません。通常、go/parser
はこのような無効なパッケージ名を見つけるとエラーを報告します。
しかし、go/parser
には、パーシングの厳密さを制御するための Mode
フラグが存在します。このフラグの一つに parser.DeclarationErrors
があります。このフラグは、宣言に関するエラー(例えば、重複する宣言や未解決の識別子など)を報告するかどうかを制御します。
このコミット以前は、go/parser
は DeclarationErrors
フラグの状態に関わらず、パッケージ名が _
である場合に常にエラーを報告していました。これは、go/parser
を利用するツールやアプリケーションによっては、宣言エラーの報告を抑制したい場合があるにも関わらず、この特定の「無効なパッケージ名」エラーが常に発生してしまうという問題を引き起こしていました。
この変更の背景には、パーサーの挙動をより柔軟にし、DeclarationErrors
フラグの意図に沿ったエラー報告を行うことで、go/parser
の利用者がより細かくエラー報告を制御できるようにするという目的があります。特に、コードの静的解析ツールなどでは、構文解析は行いつつも、宣言に関する厳密なチェックは後続のフェーズで行いたい場合があり、そのような場合にこのエラー報告が邪魔になることがありました。
前提知識の解説
1. go/parser
パッケージ
go/parser
は、Go言語の標準ライブラリの一部であり、Goソースコードを解析して抽象構文木(AST)を生成するためのパッケージです。ASTは、プログラムの構造を木構造で表現したもので、コンパイラ、リンカ、静的解析ツール、コードフォーマッタなど、Goコードを扱う多くのツールで利用されます。
go/parser
の主な機能は以下の通りです。
ParseFile
: 単一のGoソースファイルを解析し、*ast.File
型のASTを返します。ParseDir
: ディレクトリ内のすべてのGoソースファイルを解析し、パッケージごとのASTを返します。Mode
フラグ: パーシングの挙動を制御するためのビットフラグです。例えば、コメントをASTに含めるか、型解決を行うか、宣言エラーを報告するかなどを指定できます。
2. 抽象構文木 (AST: Abstract Syntax Tree)
ASTは、ソースコードの構文構造を抽象的に表現した木構造のデータ構造です。各ノードはソースコードの構成要素(式、文、宣言など)を表し、その子ノードは構成要素の内部構造を表します。go/parser
が生成するASTは、go/ast
パッケージで定義されている型(例: ast.File
, ast.Decl
, ast.Expr
など)で構成されます。
3. parser.DeclarationErrors
フラグ
go/parser
パッケージの Mode
フラグの一つで、parser.DeclarationErrors
は、パーサーが宣言に関するエラー(例: 未宣言の識別子、重複する宣言、無効な宣言など)を報告するかどうかを制御します。このフラグが設定されている場合、パーサーはこれらのエラーを報告しますが、設定されていない場合は報告を抑制します。これは、構文解析のみを行いたい場合や、より高度なセマンティックチェックを後続のフェーズで行う場合に有用です。
4. Go言語のパッケージ名とブランク識別子 _
Go言語では、すべてのソースファイルは package
宣言で始まる必要があります。パッケージ名は、そのファイルが属するパッケージを識別します。Go言語の仕様では、ブランク識別子 _
をパッケージ名として使用することは許可されていません。ブランク識別子は、値を破棄するために使用される特別な識別子であり、通常の識別子とは異なる扱いを受けます。
例えば、以下のコードは無効なパッケージ名です。
package _
func main() {
// ...
}
go/parser
は、このような無効なパッケージ名を見つけるとエラーを報告するように設計されています。
5. p.mode&DeclarationErrors != 0
これはGo言語におけるビット演算の典型的なパターンです。
p.mode
: パーサーの現在のモード設定(複数のフラグがビットとして格納されている整数値)。DeclarationErrors
:parser.DeclarationErrors
フラグの定数値(これもビットとして定義されている)。&
: ビットAND演算子。p.mode
とDeclarationErrors
の両方のビットが1である場合にのみ、結果の対応するビットが1になります。!= 0
: ビットANDの結果が0でない場合、つまりDeclarationErrors
フラグがp.mode
に含まれている(設定されている)場合に真となります。
この条件は、「パーサーのモードに DeclarationErrors
フラグが設定されているか?」をチェックしています。
技術的詳細
このコミットの技術的な核心は、go/parser
の内部でパッケージ名を解析する parseFile
メソッドにおけるエラー報告の条件変更にあります。
go/parser
は、ソースコードをトークンに分解し、それらのトークンを基に構文規則に従ってASTを構築します。この過程で、構文的に不正な箇所や、パーサーが検出できる範囲のセマンティックな問題(例えば、無効なパッケージ名)を見つけると、エラーを報告します。エラー報告は通常、パーサーの error
メソッドを通じて行われ、これはエラーリストに追加されるか、即座にパニックを引き起こすか(設定による)します。
変更前のコードでは、parseFile
メソッド内でパッケージ名を解析した後、その名前が _
(ブランク識別子)であるかどうかを単純にチェックしていました。もし _
であれば、p.error(p.pos, "invalid package name _")
を呼び出してエラーを報告していました。このチェックは、パーサーの Mode
フラグ、特に DeclarationErrors
の設定とは独立して行われていました。
変更後のコードでは、このエラー報告の条件に p.mode&DeclarationErrors != 0
という条件が追加されました。これにより、エラーが報告されるのは以下の両方の条件が満たされる場合のみとなります。
- 解析されたパッケージ名が
_
である。 - パーサーのモードに
DeclarationErrors
フラグが設定されている。
この変更により、go/parser
を利用する際に DeclarationErrors
フラグを設定しない場合(例えば、parser.ParseFile(fset, filename, src, 0)
のように Mode
を 0
に設定した場合)、パッケージ名が _
であっても invalid package name _
というエラーは報告されなくなります。これは、パーサーの挙動をより一貫性のあるものにし、DeclarationErrors
フラグの意図(宣言に関するエラー報告の制御)を尊重するものです。
この修正は、パーサーの柔軟性を高め、特定のユースケース(例えば、構文解析のみを目的とし、セマンティックなチェックは後続のツールに任せる場合)において、不要なエラー報告を抑制することを可能にします。
コアとなるコードの変更箇所
変更は src/pkg/go/parser/parser.go
ファイルの parseFile
関数内の一箇所です。
--- a/src/pkg/go/parser/parser.go
+++ b/src/pkg/go/parser/parser.go
@@ -2385,7 +2385,7 @@ func (p *parser) parseFile() *ast.File {
// Go spec: The package clause is not a declaration;
// the package name does not appear in any scope.
ident := p.parseIdent()
- if ident.Name == "_" {
+ if ident.Name == "_" && p.mode&DeclarationErrors != 0 {
p.error(p.pos, "invalid package name _")
}
p.expectSemi()
コアとなるコードの解説
変更された行は、parseFile
関数内でパッケージ名を解析し、それがブランク識別子 _
である場合にエラーを報告する条件文です。
変更前:
if ident.Name == "_" {
p.error(p.pos, "invalid package name _")
}
このコードは、ident.Name
(解析されたパッケージ名)が文字列 "_"
と等しい場合、無条件に invalid package name _
というエラーを報告していました。
変更後:
if ident.Name == "_" && p.mode&DeclarationErrors != 0 {
p.error(p.pos, "invalid package name _")
}
変更後では、エラー報告の条件に && p.mode&DeclarationErrors != 0
が追加されています。
ident.Name == "_"
: これは以前と同じく、パッケージ名がブランク識別子であるかどうかのチェックです。p.mode&DeclarationErrors != 0
: これは、パーサーの現在のモード (p.mode
) にDeclarationErrors
フラグが設定されているかどうかをチェックするビット演算です。
この二つの条件が論理AND (&&
) で結合されているため、invalid package name _
エラーが報告されるのは、パッケージ名が _
であり、かつ DeclarationErrors
フラグが有効になっている場合のみとなります。
これにより、go/parser
を利用する際に DeclarationErrors
フラグを意図的に無効にしている場合(例えば、parser.ParseFile
の mode
引数に 0
や parser.ImportsOnly
などを渡した場合)、この特定のパッケージ名に関するエラーは抑制されるようになります。これは、パーサーの挙動をより細かく制御したい開発者にとって有用な変更です。
関連リンク
- Go CL 12072043: https://golang.org/cl/12072043
- Go
go/parser
パッケージドキュメント: https://pkg.go.dev/go/parser - Go
go/ast
パッケージドキュメント: https://pkg.go.dev/go/ast - Go言語仕様 - Package clause: https://go.dev/ref/spec#Package_clause
- Go言語仕様 - Blank identifier: https://go.dev/ref/spec#Blank_identifier
参考にした情報源リンク
- 上記の関連リンクに記載されているGo言語の公式ドキュメントと仕様。
- Go言語のソースコード(
src/pkg/go/parser/parser.go
)。 - Go言語のパーサーに関する一般的な知識。
- Go言語のビット演算に関する知識。