[インデックス 16386] ファイルの概要
このコミットは、Go言語の標準ライブラリ内のgo/ast
パッケージとgo/doc
パッケージに関連するビルドの問題を修正するものです。具体的には、抽象構文木(AST)における関数型(ast.FuncType
)の表現と、それを利用するドキュメント生成ツールgo/doc
の挙動が改善されています。
変更されたファイルは以下の通りです。
src/pkg/go/ast/ast.go
: ASTの定義、特にFuncType
のPos()
メソッドのロバスト性に関する修正。src/pkg/go/doc/example.go
:go/doc
パッケージ内の例(example)コードの処理に関する修正。ast.FuncType
の生成方法が変更されています。src/pkg/go/doc/example_test.go
:go/doc
パッケージのテストケース。変数名がより一般的なものに変更され、可読性が向上しています。
コミット
e3a72b05f8d7ee2fa235e5592ca85b747f1bb310
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e3a72b05f8d7ee2fa235e5592ca85b747f1bb310
元コミット内容
go/doc: fix build
1) go/doc:
- create correct ast.FuncType
- use more commonly used variable names in a test case
2) make ast.FuncType.Pos robust in case of incorrect ASTs
R=golang-dev
CC=golang-dev
https://golang.org/cl/9651044
変更の背景
このコミットは、Go言語のツールチェインにおける2つの主要な問題に対処しています。
go/doc
パッケージのビルド問題:go/doc
はGoのソースコードからドキュメントを生成するためのパッケージです。このパッケージが、特定の条件下でast.FuncType
を正しく扱えず、ビルドエラーや予期せぬ挙動を引き起こしていた可能性があります。特に、ast.FuncType
のParams
フィールドがnil
である場合に問題が発生していたと考えられます。- 不正なASTに対する
ast.FuncType.Pos()
のロバスト性向上: Goコンパイラやツールは、ソースコードを解析して抽象構文木(AST)を構築します。しかし、何らかの理由でASTが不完全または不正な状態になることがあります。ast.FuncType
のPos()
メソッドは、その型がソースコードのどこに位置するかを示す情報(token.Pos
)を返しますが、不正なASTの場合にパニックを起こすなど、ロバスト性に欠けていたようです。コミットメッセージにある「issue 3870」がこの問題に関連している可能性が高いです。
これらの問題は、Goのドキュメント生成プロセスや、ASTを扱う他のツールに影響を与える可能性があったため、修正が必要とされました。
前提知識の解説
抽象構文木 (Abstract Syntax Tree, AST)
ASTは、プログラミング言語のソースコードの抽象的な構文構造を木構造で表現したものです。コンパイラやインタプリタは、ソースコードを直接処理するのではなく、まずASTに変換してから、そのASTを基にコード生成や最適化、静的解析などを行います。Go言語では、go/ast
パッケージがASTの定義と操作を提供します。
go/ast
パッケージ
Go言語のgo/ast
パッケージは、Goプログラムのソースコードを表現するためのASTノードの型定義を提供します。例えば、関数宣言はast.FuncDecl
、変数宣言はast.GenDecl
、式はast.Expr
などで表現されます。
ast.FuncType
: 関数の型を表すASTノードです。関数の引数(パラメータ)と戻り値の型情報を含みます。Params
フィールドは引数のリスト(*ast.FieldList
)を、Results
フィールドは戻り値のリスト(*ast.FieldList
)を保持します。ast.FieldList
: パラメータや戻り値のリストを表します。各要素はast.Field
です。token.Pos
: ソースコード内の位置(行番号、列番号など)を表す型です。go/token
パッケージで定義されます。Pos()
メソッド: 多くのASTノード型が持つメソッドで、そのノードがソースコード内で始まる位置(token.Pos
)を返します。
go/doc
パッケージ
go/doc
パッケージは、Goのソースコードからパッケージ、関数、型、変数などのドキュメントを抽出・生成するためのツールです。go doc
コマンドやgodoc
ツールがこのパッケージを利用しています。このパッケージは、ソースコードを解析してASTを構築し、そのASTからドキュメントコメントや宣言情報を読み取ります。
token.Pos.IsValid()
token.Pos
型にはIsValid()
メソッドがあります。これは、その位置情報が有効であるかどうかをチェックします。通常、token.NoPos
(無効な位置)でない場合にtrue
を返します。
技術的詳細
このコミットは、主に以下の2つの技術的な変更を含んでいます。
-
ast.FuncType.Pos()
の修正 (src/pkg/go/ast/ast.go
):- 変更前:
func (x *FuncType) Pos() token.Pos { if x.Func.IsValid() { return x.Func } return x.Params.Pos() // interface method declarations have no "func" keyword }
- 変更後:
func (x *FuncType) Pos() token.Pos { if x.Func.IsValid() || x.Params == nil { // see issue 3870 return x.Func } return x.Params.Pos() // interface method declarations have no "func" keyword }
この変更は、
FuncType
の開始位置を返すPos()
メソッドのロジックを改善しています。以前はx.Func
(func
キーワードの位置)が有効でない場合、無条件にx.Params.Pos()
を返していました。しかし、x.Params
がnil
である場合(例えば、不正なASTや、FuncType
が不完全に構築された場合)、x.Params.Pos()
を呼び出すとパニックを引き起こす可能性がありました。 新しいロジックでは、x.Func.IsValid()
がtrue
であるか、またはx.Params
がnil
である場合にx.Func
の位置を返します。これにより、x.Params
がnil
であっても安全にPos()
を呼び出せるようになり、不正なASTに対するロバスト性が向上しました。コメントで「see issue 3870」とあるように、この修正は特定のバグ報告に対応するものです。 - 変更前:
-
ast.FuncType
の生成修正 (src/pkg/go/doc/example.go
):- 変更前:
funcDecl := &ast.FuncDecl{ Name: ast.NewIdent("main"), Type: &ast.FuncType{}, Body: body, }
- 変更後:
funcDecl := &ast.FuncDecl{ Name: ast.NewIdent("main"), Type: &ast.FuncType{Params: &ast.FieldList{}}, // FuncType.Params must be non-nil Body: body, }
go/doc
パッケージ内で、main
関数を合成する際にast.FuncType
を生成しています。以前はType: &ast.FuncType{}
としていましたが、これはParams
フィールドがnil
のままになります。ast.FuncType
のParams
フィールドは、たとえ引数がなくてもnil
であってはならず、空のast.FieldList
であるべきです。この修正により、Params: &ast.FieldList{}
と明示的に初期化することで、go/doc
が生成するASTがより正確になり、ast.FuncType.Pos()
のようなメソッドが安全に動作するようになります。 - 変更前:
-
テストケースの変数名変更 (
src/pkg/go/doc/example_test.go
):fs
をfset
に、b
をbuf
に変更しています。これらはそれぞれtoken.FileSet
とbytes.Buffer
のインスタンスを表す変数です。Goコミュニティでは、FileSet
のインスタンスにはfset
、bytes.Buffer
のインスタンスにはbuf
という変数名が慣習的に使われることが多いため、コードの可読性と一貫性を向上させるための変更です。機能的な変更はありません。
コアとなるコードの変更箇所
diff --git a/src/pkg/go/ast/ast.go b/src/pkg/go/ast/ast.go
index e8599184a6..f26ff6b1af 100644
--- a/src/pkg/go/ast/ast.go
+++ b/src/pkg/go/ast/ast.go
@@ -439,7 +439,7 @@ func (x *KeyValueExpr) Pos() token.Pos { return x.Key.Pos() }
func (x *ArrayType) Pos() token.Pos { return x.Lbrack }
func (x *StructType) Pos() token.Pos { return x.Struct }
func (x *FuncType) Pos() token.Pos {\n-\tif x.Func.IsValid() {\n+\tif x.Func.IsValid() || x.Params == nil { // see issue 3870\n \t\treturn x.Func
\t}\
\treturn x.Params.Pos() // interface method declarations have no "func" keyword
diff --git a/src/pkg/go/doc/example.go b/src/pkg/go/doc/example.go
index 2761083c7e..2358ed3890 100644
--- a/src/pkg/go/doc/example.go
+++ b/src/pkg/go/doc/example.go
@@ -265,7 +265,7 @@ func playExample(file *ast.File, body *ast.BlockStmt) *ast.File {\n \t// Synthesize main function.\n \tfuncDecl := &ast.FuncDecl{\n \t\tName: ast.NewIdent(\"main\"),\n-\t\tType: &ast.FuncType{},\n+\t\tType: &ast.FuncType{Params: &ast.FieldList{}}, // FuncType.Params must be non-nil\n \t\tBody: body,\n \t}\
コアとなるコードの解説
src/pkg/go/ast/ast.go
の変更
FuncType
構造体のPos()
メソッドの変更は、ASTの健全性とロバスト性を高める上で非常に重要です。
- 変更前:
if x.Func.IsValid() { return x.Func }
これは、func
キーワードが存在し、その位置が有効であれば、その位置を返すというロジックでした。しかし、func
キーワードがない場合(例えば、インターフェースのメソッド宣言など)や、何らかの理由でx.Func
が無効な場合、次の行のreturn x.Params.Pos()
が実行されます。 - 変更後:
if x.Func.IsValid() || x.Params == nil { return x.Func }
ここに|| x.Params == nil
という条件が追加されました。これは、x.Func
が有効でない場合でも、もしx.Params
(関数の引数リスト)がnil
であれば、x.Func
の位置を返すようにします。 なぜこれが重要かというと、x.Params
がnil
の場合にx.Params.Pos()
を呼び出すと、nil
ポインタ参照によるパニックが発生する可能性があるためです。FuncType
が不完全に構築されたり、不正なASTが渡されたりするシナリオでは、x.Params
がnil
になることがあり得ます。この修正により、そのような不正な状態でも安全にPos()
メソッドが呼び出せるようになり、Goツールチェイン全体の安定性が向上します。コメントにある「issue 3870」は、この特定のバグ報告を指しています。
src/pkg/go/doc/example.go
の変更
playExample
関数内でmain
関数を合成する際のast.FuncType
の初期化方法が変更されました。
- 変更前:
Type: &ast.FuncType{}
この初期化では、ast.FuncType
のParams
フィールドはGoのゼロ値であるnil
になります。 - 変更後:
Type: &ast.FuncType{Params: &ast.FieldList{}}
この変更により、FuncType
のParams
フィールドが明示的に空のast.FieldList
インスタンスで初期化されます。GoのASTの設計では、引数がない場合でもParams
フィールドはnil
ではなく、空のFieldList
であるべきという慣習があります。これにより、go/doc
が生成するASTがより仕様に準拠したものとなり、ast.FuncType.Pos()
のようなメソッドがParams
フィールドがnil
であることによる問題を回避できるようになります。これは、go/ast/ast.go
でのPos()
メソッドの修正と連携して、より堅牢なシステムを構築します。
src/pkg/go/doc/example_test.go
の変更
このファイルでは、テストコード内の変数名が変更されました。
fs
->fset
:token.FileSet
のインスタンスを表す変数名として、fset
がより一般的で分かりやすいです。b
->buf
:bytes.Buffer
のインスタンスを表す変数名として、buf
がより一般的で分かりやすいです。
これらの変更はコードの機能には影響しませんが、Goコミュニティのコーディング規約や慣習に合わせることで、コードの可読性と保守性を向上させます。
関連リンク
- Go issue 3870: このコミットの
ast.FuncType.Pos()
の修正に関連するバグ報告です。go/printer
がFuncType
のParams
フィールドがnil
の場合にクラッシュするという問題が報告されています。- https://github.com/golang/go/issues/3870 (検索結果から推測されるリンク)
- Go CL 9651044: このコミットに対応するGoのコードレビューシステム(Gerrit)のチェンジリストです。
参考にした情報源リンク
- Go言語の公式ドキュメント:
go/ast
パッケージ、go/doc
パッケージ - Go言語のソースコード
- Go言語のIssueトラッカー (GitHub)
- Go言語のコードレビューシステム (Gerrit)