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

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

このコミットは、Go言語の抽象構文木(AST)を扱う go/ast パッケージ内の FuncType ノードに関する修正とドキュメントの改善を目的としています。具体的には、FuncType.Pos() メソッドの実装が修正され、FuncType.Params および ChanType.Arrow フィールドのドキュメントがより正確になるように更新されています。

go/ast パッケージは、Goプログラムのソースコードを解析して抽象構文木を構築するための型と関数を提供します。ASTは、コンパイラ、リンター、コードフォーマッター、静的解析ツールなど、Goコードをプログラム的に操作する多くのツールにとって不可欠な中間表現です。

このコミットで変更されたファイルは以下の通りです。

  • src/pkg/go/ast/ast.go: go/ast パッケージの主要な型定義が含まれています。FuncType 構造体とその Pos() メソッド、ChanType 構造体などが定義されています。
  • src/pkg/go/ast/filter.go: ASTのフィルタリングや操作に関連するユーティリティ関数が含まれています。このファイルでは token.NoPos の利用箇所が修正されています。

コミット

go/ast: FuncType.Pos() の実装と FuncType.Params のドキュメントを修正

adonovan氏の指摘による。

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

https://github.com/golang/go/commit/75b62e367bfe0b4935fb08f5941f3a9a99932dae

元コミット内容

commit 75b62e367bfe0b4935fb08f5941f3a9a99932dae
Author: Robert Griesemer <gri@golang.org>
Date:   Wed May 22 13:36:43 2013 -0700

    go/ast: fix FuncType.Pos() impl. and FuncType.Params documentation
    
    As pointed out by adonovan.
    
    R=golang-dev, adonovan
    CC=golang-dev
    https://golang.org/cl/9662045

変更の背景

このコミットの背景には、Go言語のAST(抽象構文木)を扱う go/ast パッケージにおける FuncType ノードの Pos() メソッドの不正確な振る舞いと、関連するドキュメントの誤りがありました。

go/ast パッケージでは、ソースコード内の各要素(識別子、キーワード、式など)が token.Pos 型で表される位置情報を持っています。これは、エラー報告、コードのハイライト、リファクタリングツールなど、ソースコードの正確な位置を特定する必要がある場合に非常に重要です。

FuncType は関数型を表すASTノードであり、その Pos() メソッドは、その関数型がソースコード内で始まる位置を返すことが期待されます。しかし、元の実装では、FuncType.Func フィールド(func キーワードの位置)を無条件に返していました。

問題は、インターフェースのメソッド宣言のように、func キーワードが存在しない関数型も存在するという点です。このような場合、FuncType.Functoken.NoPos(無効な位置)となるため、FuncType.Pos() が常に token.NoPos を返してしまうというバグがありました。これは、インターフェースメソッドのASTを正確に処理しようとするツールにとって問題となります。

また、FuncType.Params フィールドのドキュメントが「(incoming) parameters; or nil」となっていましたが、実際にはパラメータがない場合でも FieldListnil ではなく、空の FieldList が設定されるべきでした。同様に、ChanType.Arrow のドキュメントも token.NoPos の意味合いを明確にする必要がありました。

これらの問題は、Go言語のツール開発者であるadonovan氏によって指摘され、そのフィードバックに基づいてこの修正がコミットされました。目的は、go/ast パッケージの正確性と堅牢性を向上させ、ASTを扱うツールがより信頼性の高い位置情報を取得できるようにすることです。

前提知識の解説

抽象構文木 (Abstract Syntax Tree, AST)

ASTは、プログラミング言語のソースコードの抽象的な構文構造を木構造で表現したものです。コンパイラやインタープリタがソースコードを解析する際の中間表現として広く利用されます。ASTは、ソースコードの具体的な構文(括弧やセミコロンなど)を抽象化し、プログラムの論理的な構造に焦点を当てます。

Go言語では、go/ast パッケージがGoプログラムのASTを構築するための型と関数を提供します。例えば、関数宣言は ast.FuncDecl、変数宣言は ast.GenDecl、式は ast.Expr などのノードで表現されます。

go/ast パッケージ

go/ast パッケージは、Go言語のソースコードを解析してASTを生成し、そのASTを操作するための基本的な構成要素を提供します。主要な型には以下のようなものがあります。

  • Node: AST内のすべてのノードが実装するインターフェース。
  • Expr: 式を表すノード。
  • Stmt: 文を表すノード。
  • Decl: 宣言を表すノード。
  • File: 単一のGoソースファイル全体のASTを表すノード。
  • Package: 複数のファイルからなるパッケージのASTを表すノード。

token.Postoken.NoPos

go/token パッケージは、ソースコード内の位置情報を表す Pos 型と、無効な位置を表す NoPos 定義を提供します。

  • token.Pos: ソースコード内の特定の文字位置を整数で表します。これは通常、ファイル内のオフセットとして扱われます。
  • token.NoPos: token.Pos のゼロ値であり、有効な位置情報がないことを示します。例えば、ASTノードがソースコードの特定のキーワードに対応しない場合や、合成されたノードで位置情報が不要な場合に使用されます。IsValid() メソッドで token.Pos が有効かどうかをチェックできます。

FuncType 構造体

go/ast パッケージの FuncType 構造体は、関数型(例: func(int, string) (bool, error))をASTで表現するためのノードです。

type FuncType struct {
	Func    token.Pos  // position of "func" keyword
	Params  *FieldList // (incoming) parameters; or nil
	Results *FieldList // (outgoing) results; or nil
}
  • Func: func キーワードの開始位置。インターフェースメソッド宣言のように func キーワードがない場合は token.NoPos になります。
  • Params: 関数の引数を表す FieldList へのポインタ。
  • Results: 関数の戻り値を表す FieldList へのポインタ。

FieldList 構造体

FieldList は、関数パラメータや構造体フィールドのリストを表すために使用されます。

type FieldList struct {
	Opening token.Pos // position of opening parenthesis '(' or '{'
	List    []*Field  // field list; or nil
	Closing token.Pos // position of closing parenthesis ')' or '}'
}
  • Opening: 開き括弧(( または {)の位置。
  • List: Field のスライス。各 Field は単一のパラメータまたはフィールドを表します。
  • Closing: 閉じ括弧() または })の位置。

FieldList.Pos() メソッドは、Opening フィールドが有効であればその位置を返し、そうでなければ List 内の最初の Field の位置を返します。

インターフェースメソッド宣言

Go言語のインターフェースは、メソッドのシグネチャの集合を定義します。インターフェース内で宣言されるメソッドは、func キーワードを持ちません。例えば:

type MyInterface interface {
    MyMethod(int) string
}

この MyMethod の型は func(int) string ですが、ソースコード上には func キーワードが存在しません。ASTでは、このようなメソッドも FuncType ノードとして表現されますが、その Func フィールドは token.NoPos となります。

技術的詳細

このコミットは、主に以下の2つの技術的な側面に焦点を当てています。

  1. FuncType.Pos() メソッドの修正: 元の FuncType.Pos() メソッドは、単に x.Func を返していました。これは、通常の関数宣言(例: func foo() {})では問題ありませんが、インターフェースのメソッド宣言(例: type I interface { M() })のように func キーワードが存在しない場合、x.Functoken.NoPos となります。その結果、FuncType.Pos()token.NoPos を返してしまい、インターフェースメソッドのASTノードの正確な開始位置を取得できないという問題がありました。

    修正後の実装では、まず x.Func.IsValid() をチェックします。

    • x.Func が有効な位置(つまり func キーワードが存在する)であれば、その位置を返します。
    • x.Func が無効な位置(token.NoPos)であれば、それはインターフェースメソッド宣言であると判断し、代わりに x.Params.Pos() を返します。x.Params は関数のパラメータリストを表す FieldList であり、その Pos() メソッドはパラメータリストの開始位置(通常は開き括弧 ( の位置)を返します。これにより、func キーワードがない場合でも、関数型の開始位置を正確に特定できるようになります。
  2. ドキュメントの更新:

    • FuncType.Func フィールドのコメントが「position of "func" keyword」から「position of "func" keyword (token.NoPos if there is no "func")」に変更されました。これにより、func キーワードが存在しない場合の token.NoPos の意味合いが明確になりました。
    • FuncType.Params フィールドのコメントが「(incoming) parameters; or nil」から「(incoming) parameters; non-nil」に変更されました。これは、パラメータがない場合でも FieldListnil ではなく、空の FieldList が設定されるというASTの内部的な不変条件を反映しています。これにより、ASTを扱うツールが Paramsnil である可能性を考慮する必要がなくなり、コードの堅牢性が向上します。
    • ChanType.Arrow フィールドのコメントも同様に「position of "<-" (noPos if there is no "<-")」から「position of "<-" (token.NoPos if there is no "<-")」に変更され、token.NoPos の使用が明確化されました。
  3. noPos 変数の削除と token.NoPos の直接使用: 以前は var noPos token.Pos というグローバル変数が定義され、token.NoPos の代わりに noPos が使用されていました。このコミットでは、この noPos 変数が削除され、代わりに token.NoPos 定数が直接使用されるようになりました。これは、コードの明確性を高め、冗長性を排除するためのクリーンアップです。token.NoPos は定数であるため、グローバル変数として保持する必要はありません。

これらの変更により、go/ast パッケージはより正確なAST表現を提供し、Go言語の静的解析ツールやコード生成ツールがより信頼性の高い情報を利用できるようになります。特に、インターフェースメソッドの解析における正確性が向上しました。

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

diff --git a/src/pkg/go/ast/ast.go b/src/pkg/go/ast/ast.go
index bf533d1d24..e8599184a6 100644
--- a/src/pkg/go/ast/ast.go
+++ b/src/pkg/go/ast/ast.go
@@ -385,8 +385,8 @@ type (
 
 	// A FuncType node represents a function type.
 	FuncType struct {
-		Func    token.Pos  // position of "func" keyword
-		Params  *FieldList // (incoming) parameters; or nil
+		Func    token.Pos  // position of "func" keyword (token.NoPos if there is no "func")
+		Params  *FieldList // (incoming) parameters; non-nil
 		Results *FieldList // (outgoing) results; or nil
 	}
 
@@ -407,7 +407,7 @@ type (
 	// A ChanType node represents a channel type.
 	ChanType struct {
 		Begin token.Pos // position of "chan" keyword or "<-" (whichever comes first)
-		Arrow token.Pos // position of "<-" (noPos if there is no "<-")
+		Arrow token.Pos // position of "<-" (token.NoPos if there is no "<-")
 		Dir   ChanDir   // channel direction
 		Value Expr      // value type
 	}
@@ -438,10 +438,15 @@ func (x *BinaryExpr) Pos() token.Pos     { return x.X.Pos() }\n func (x *KeyValueExpr) Pos() token.Pos   { return x.Key.Pos() }\n func (x *ArrayType) Pos() token.Pos      { return x.Lbrack }\n func (x *StructType) Pos() token.Pos     { return x.Struct }\n-func (x *FuncType) Pos() token.Pos       { return x.Func }\n-func (x *InterfaceType) Pos() token.Pos  { return x.Interface }\n-func (x *MapType) Pos() token.Pos        { return x.Map }\n-func (x *ChanType) Pos() token.Pos       { return x.Begin }\n+func (x *FuncType) Pos() token.Pos {\n+	if x.Func.IsValid() {\n+		return x.Func\n+	}\n+	return x.Params.Pos() // interface method declarations have no "func" keyword\n+}\n+func (x *InterfaceType) Pos() token.Pos { return x.Interface }\n+func (x *MapType) Pos() token.Pos       { return x.Map }\n+func (x *ChanType) Pos() token.Pos      { return x.Begin }\n 
 func (x *BadExpr) End() token.Pos { return x.To }\n func (x *Ident) End() token.Pos   { return token.Pos(int(x.NamePos) + len(x.Name)) }\n @@ -511,12 +516,10 @@ func (*ChanType) exprNode()      {}\n // ----------------------------------------------------------------------------\n // Convenience functions for Idents\n \n-var noPos token.Pos\n-\n // NewIdent creates a new Ident without position.\n // Useful for ASTs generated by code other than the Go parser.\n //\n-func NewIdent(name string) *Ident { return &Ident{noPos, name, nil} }\n+func NewIdent(name string) *Ident { return &Ident{token.NoPos, name, nil} }\n \n // IsExported returns whether name is an exported Go symbol\n // (i.e., whether it begins with an uppercase letter).\ndiff --git a/src/pkg/go/ast/filter.go b/src/pkg/go/ast/filter.go\nindex 71c9ed7766..fc3eeb4a1d 100644\n--- a/src/pkg/go/ast/filter.go\n+++ b/src/pkg/go/ast/filter.go\n@@ -308,7 +308,7 @@ func nameOf(f *FuncDecl) string {\n // separator is an empty //-style comment that is interspersed between\n // different comment groups when they are concatenated into a single group\n //\n-var separator = &Comment{noPos, \"//\"}\n+var separator = &Comment{token.NoPos, \"//\"}\n \n // MergePackageFiles creates a file AST by merging the ASTs of the\n // files belonging to a package. The mode flags control merging behavior.\n```

## コアとなるコードの解説

### `src/pkg/go/ast/ast.go` の変更点

1.  **`FuncType` 構造体のコメント修正**:
    ```diff
    -		Func    token.Pos  // position of "func" keyword
    -		Params  *FieldList // (incoming) parameters; or nil
    +		Func    token.Pos  // position of "func" keyword (token.NoPos if there is no "func")
    +		Params  *FieldList // (incoming) parameters; non-nil
    ```
    *   `Func` フィールドのコメントに「(token.NoPos if there is no "func")」が追記されました。これにより、`func` キーワードが存在しない(例: インターフェースメソッド)場合に `Func` が `token.NoPos` となることが明示され、ドキュメントの正確性が向上しました。
    *   `Params` フィールドのコメントが「(incoming) parameters; or nil」から「(incoming) parameters; non-nil」に変更されました。これは、パラメータがない場合でも `Params` フィールドが `nil` になることはなく、空の `FieldList` を指すポインタが設定されるというASTの不変条件を明確にしています。これにより、ASTを処理するコードが `nil` チェックを不要に実行するのを防ぎ、より堅牢になります。

2.  **`ChanType` 構造体のコメント修正**:
    ```diff
    -		Arrow token.Pos // position of "<-" (noPos if there is no "<-")
    +		Arrow token.Pos // position of "<-" (token.NoPos if there is no "<-")
    ```
    *   `Arrow` フィールドのコメントが「(noPos if there is no "<-")」から「(token.NoPos if there is no "<-")」に変更されました。これは、`noPos` 変数が削除され、`token.NoPos` 定数が直接使用されるようになったことに伴う修正であり、一貫性を保つためのものです。

3.  **`FuncType.Pos()` メソッドの実装修正**:
    ```diff
    -func (x *FuncType) Pos() token.Pos       { return x.Func }
    +func (x *FuncType) Pos() token.Pos {
    +	if x.Func.IsValid() {
    +		return x.Func
    +	}
    +	return x.Params.Pos() // interface method declarations have no "func" keyword
    +}
    ```
    *   この変更がこのコミットの最も重要な部分です。
    *   元の実装は単に `x.Func` を返していました。
    *   新しい実装では、まず `x.Func.IsValid()` をチェックします。
        *   `x.Func` が有効な位置(`func` キーワードが存在する)であれば、その位置を返します。
        *   `x.Func` が無効な位置(`token.NoPos`)であれば、それはインターフェースメソッド宣言のように `func` キーワードが存在しないケースであると判断し、代わりに `x.Params.Pos()` を返します。`x.Params.Pos()` はパラメータリストの開始位置(通常は開き括弧 `(` の位置)を返すため、`func` キーワードがない場合でも関数型の正確な開始位置を提供できます。コメント「interface method declarations have no "func" keyword」がこのロジックの意図を明確にしています。

4.  **`noPos` 変数の削除と `NewIdent` 関数の修正**:
    ```diff
    -var noPos token.Pos
    -
    // NewIdent creates a new Ident without position.
    // Useful for ASTs generated by code other than the Go parser.
    //
    -func NewIdent(name string) *Ident { return &Ident{noPos, name, nil} }
    +func NewIdent(name string) *Ident { return &Ident{token.NoPos, name, nil} }
    ```
    *   グローバル変数 `noPos` が削除されました。
    *   `NewIdent` 関数内で `noPos` を使用していた箇所が `token.NoPos` に直接置き換えられました。これにより、コードの冗長性がなくなり、`token.NoPos` の意図がより明確になります。

### `src/pkg/go/ast/filter.go` の変更点

1.  **`separator` 変数の修正**:
    ```diff
    -var separator = &Comment{noPos, "//"}
    +var separator = &Comment{token.NoPos, "//"}
    ```
    *   `src/pkg/go/ast/ast.go` と同様に、`noPos` 変数の削除に伴い、`separator` 変数の初期化で `noPos` を使用していた箇所が `token.NoPos` に直接置き換えられました。

これらの変更は、`go/ast` パッケージの内部的な整合性と正確性を高め、特にASTノードの位置情報に関する信頼性を向上させるものです。これにより、Go言語のツールエコシステム全体が恩恵を受けます。

## 関連リンク

*   Go言語の `go/ast` パッケージに関する公式ドキュメント: [https://pkg.go.dev/go/ast](https://pkg.go.dev/go/ast)
*   Go言語の `go/token` パッケージに関する公式ドキュメント: [https://pkg.go.dev/go/token](https://pkg.go.dev/go/token)

## 参考にした情報源リンク

*   GitHubコミットページ: [https://github.com/golang/go/commit/75b62e367bfe0b4935fb08f5941f3a9a99932dae](https://github.com/golang.com/go/commit/75b62e367bfe0b4935fb08f5941f3a9a99932dae)
*   Go CL 9662045: [https://golang.org/cl/9662045](https://golang.org/cl/9662045) (これは古いGoのコードレビューシステムへのリンクであり、現在はGitHubのプルリクエストにリダイレクトされるか、アクセスできない場合があります。)
*   Go言語のASTに関する一般的な情報源 (例: Go AST Explorerなど、ASTの構造を視覚的に理解するのに役立つツール)
*   Go言語のインターフェースとメソッドに関する公式ドキュメント。
*   `token.Pos` と `token.NoPos` の概念に関するGo言語の仕様または関連ドキュメント。
*   Go言語のソースコード解析に関するブログ記事やチュートリアル。