[インデックス 11443] ファイルの概要
このコミットは、Go言語のドキュメンテーション生成ツールである go/doc
パッケージにおける、埋め込み型(anonymous fields)のメソッド表示に関する挙動の変更と、それに伴う内部ロジックの改善を目的としています。具体的には、エクスポートされた匿名フィールドのメソッドがドキュメンテーションに表示されないようにするための新しいフラグ AllMethods
の導入と、関連するコードの整理が行われています。
コミット
commit b3a5f9e51ba86bafe414e8a7036a090d9da9a660
Author: Robert Griesemer <gri@golang.org>
Date: Fri Jan 27 14:45:47 2012 -0800
go/doc: don't show methods of exported anonymous fields
Added flag AllMethods: if not set (future default), embedded
methods of exported (and thus visible) embedded fields are not
shown in the final package documentation
The actual change for AllMethods is just in sortedFuncs. All
other changes are simplifications of the existing logic (mostly
deletion of code): Because method conflicts due to embedding
must always be detected, remove any premature elimination of
types and methods. Instead collect all named types and all
methods and do the filtering at the end.
Miscellaneous:
- renamed baseType -> namedType
- streamline logic for recording embedded types
- record embedded types via a map (simpler data structures)
AllMethods is set by default; so the output is unchanged and
the tests pass. The next CL will enable the AllMethods flag
and have adjusted tests (and fix issue 2791).
R=rsc
CC=golang-dev
https://golang.org/cl/5572076
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b3a5f9e51ba86bafe414e8a7036a090d9da9a660
元コミット内容
このコミットの元の内容は、Goのドキュメンテーションツール go/doc
において、エクスポートされた匿名フィールドのメソッドをドキュメンテーションに表示しないようにする変更です。この機能は AllMethods
という新しいフラグによって制御され、将来的にはこのフラグがデフォルトで無効になる予定です。
変更の主なポイントは以下の通りです。
AllMethods
フラグの導入: エクスポートされた(したがって可視な)埋め込みフィールドの埋め込みメソッドを最終的なパッケージドキュメンテーションに表示しないようにするためのフラグが追加されました。- ロジックの簡素化: メソッドの競合を常に検出する必要があるため、型やメソッドの時期尚早な排除を削除し、すべての名前付き型とすべてのメソッドを収集してから最後にフィルタリングを行うように変更されました。これにより、既存のロジックが簡素化され、コードの削除が多く行われました。
- 内部データ構造の改善:
baseType
がnamedType
にリネームされました。- 埋め込み型を記録するロジックが合理化されました。
- 埋め込み型がマップ(
map[*namedType]bool
)を介して記録されるようになり、データ構造がよりシンプルになりました。
- 段階的な導入: 現在のところ
AllMethods
フラグはデフォルトで有効になっているため、このコミットによるドキュメンテーションの出力は変更されません。次の変更リスト(CL)でこのフラグが無効にされ、関連するテストが調整され、Issue 2791が修正される予定です。
変更の背景
Go言語のドキュメンテーションツール go/doc
は、Goのソースコードから自動的にドキュメンテーションを生成します。Goの言語仕様には「埋め込み(embedding)」という強力な機能があり、構造体内に匿名フィールドとして別の型を埋め込むことで、その埋め込まれた型のメソッドを外側の構造体が「継承」したかのように振る舞わせることができます。
しかし、この埋め込み機能がドキュメンテーション生成において問題を引き起こすことがありました。特に、エクスポートされた(つまり外部からアクセス可能な)匿名フィールドのメソッドが、そのフィールドが埋め込まれた構造体のドキュメンテーションに表示されることで、ドキュメンテーションが冗長になったり、ユーザーにとって本当に重要な情報が埋もれてしまったりする可能性がありました。
このコミットの背景には、このようなドキュメンテーションの冗長性を減らし、より簡潔で分かりやすいドキュメンテーションを提供したいという意図があります。Issue 2791("go/doc: don't show methods of exported anonymous fields")がこの変更の直接的な動機となっており、ユーザーがドキュメンテーションで本当に見たいのは、その型が直接定義しているメソッドや、非エクスポートの埋め込み型から「昇格」してきたメソッドであり、エクスポートされた埋め込み型が持つメソッドは、その埋め込み型自体のドキュメンテーションを見れば十分である、という考えに基づいています。
また、この変更は単に表示を制御するだけでなく、ドキュメンテーション生成の内部ロジックをより堅牢で効率的なものにするためのリファクタリングも兼ねています。特に、メソッドの競合検出を正確に行うために、型やメソッドのフィルタリングを最終段階で行うように変更することで、ロジックの複雑性を軽減し、将来的な拡張性も考慮されています。
前提知識の解説
このコミットを理解するためには、以下のGo言語およびドキュメンテーション生成に関する前提知識が必要です。
-
Go言語の埋め込み(Embedding):
- Go言語では、構造体の中にフィールド名なしで別の型(構造体、インターフェースなど)を宣言することができます。これを「埋め込み」と呼びます。
- 埋め込まれた型のメソッドは、外側の構造体のメソッドとして「昇格(promoted)」され、外側の構造体のインスタンスから直接呼び出すことができます。
- 例:
type Engine struct { Power int } func (e Engine) Start() { /* ... */ } type Car struct { Engine // Engine型を匿名フィールドとして埋め込み Brand string } // car.Start() のようにEngineのStartメソッドを呼び出せる
- 埋め込みは、コードの再利用やインターフェースの実装を簡潔にするための強力なメカニズムですが、ドキュメンテーションの観点からは、どのメソッドがその型に「固有」のものなのか、どのメソッドが埋め込みによって「昇格」してきたものなのかを区別することが重要になる場合があります。
-
Goのドキュメンテーションツール (
go/doc
パッケージ):- Goには、ソースコードのコメントから自動的にドキュメンテーションを生成する標準ツールがあります。これは
go doc
コマンドやgodoc
サーバーによって利用されます。 go/doc
パッケージは、このドキュメンテーション生成のコアロジックを提供します。AST(抽象構文木)を解析し、パッケージ、型、関数、変数、定数、メソッドなどの情報を抽出し、整形されたドキュメンテーション構造を構築します。- ドキュメンテーションの生成時には、エクスポートされた(大文字で始まる)識別子のみがデフォルトで表示されます。非エクスポートの識別子は通常、ドキュメンテーションには含まれません。
- Goには、ソースコードのコメントから自動的にドキュメンテーションを生成する標準ツールがあります。これは
-
AST (
go/ast
パッケージ):- Goコンパイラは、ソースコードを解析してASTを構築します。
go/doc
パッケージも、このASTを操作してコードの構造やコメントを読み取ります。 ast.FieldList
は構造体やインターフェースのフィールド(またはメソッド)のリストを表します。ast.IsExported(name string)
は、与えられた名前がエクスポートされている(つまり大文字で始まる)かどうかを判定するヘルパー関数です。
- Goコンパイラは、ソースコードを解析してASTを構築します。
-
メソッドセット(Method Set):
- Goの型には「メソッドセット」という概念があります。これは、その型が持つすべてのメソッドの集合です。
- ポインタ型と非ポインタ型ではメソッドセットが異なる場合があります。
- 埋め込みによってメソッドが昇格する場合、そのメソッドは外側の型のメソッドセットに追加されます。
これらの知識が、コミットが解決しようとしている問題(ドキュメンテーションの冗長性)と、その解決策(AllMethods
フラグと内部ロジックの変更)を理解する上で不可欠です。
技術的詳細
このコミットの技術的な詳細は、主に go/doc
パッケージ内のドキュメンテーション生成ロジックの変更に集約されます。
-
AllMethods
フラグの導入と制御:src/pkg/go/doc/doc.go
にAllMethods Mode = 1 << iota
が追加され、Mode
型の新しいビットフラグとして定義されました。New
関数内でmode |= AllMethods
が一時的に設定されています。これは、このコミット時点ではAllMethods
を常に有効にし、既存のドキュメンテーション出力に影響を与えないようにするためのものです。コミットメッセージにあるように、将来のCLでこの行が削除され、AllMethods
がデフォルトで無効になる予定です。sortedTypes
関数とsortedFuncs
関数にallMethods
というブール引数が追加され、このフラグの値に基づいてメソッドのフィルタリングが行われるようになりました。
-
baseType
からnamedType
へのリネームと構造体の変更:src/pkg/go/doc/reader.go
において、ドキュメンテーション生成の内部で型情報を保持するために使用されていたbaseType
構造体がnamedType
にリネームされました。これは、その型が「名前付きの型」であることをより明確に示します。namedType
構造体内の埋め込み型を管理する方法が変更されました。以前は[]embeddedType
スライスで管理されていましたが、map[*namedType]bool
に変更されました。マップのキーは埋め込み型へのポインタ、値はそれがポインタ型として埋め込まれているかどうかを示すブール値です。これにより、埋め込み型の管理がよりシンプルかつ効率的になります。addEmbeddedType
メソッドが削除され、埋め込み型の記録ロジックがrecordAnonymousField
関数に集約されました。
-
埋め込み型とメソッドの処理ロジックの変更:
recordAnonymousField
関数の導入:src/pkg/go/doc/reader.go
に新しくrecordAnonymousField
関数が追加されました。この関数は、匿名フィールドの型を親のnamedType
に記録する役割を担います。この関数は、埋め込み型がエクスポートされているかどうかに関わらず、すべての匿名フィールドの型を記録します。これにより、メソッドの競合検出に必要なすべての情報が収集されるようになります。- フィルタリングの遅延: 以前のロジックでは、
filterFieldList
やreadFunc
の中で、メソッドをドキュメンテーションに含めるかどうかを早期に判断し、不要なメソッドを排除していました。このコミットでは、この「時期尚早な排除」が削除されました。代わりに、すべての名前付き型とすべてのメソッドをまず収集し、その後にsortedFuncs
のような最終的なソート・フィルタリング段階で表示の有無を決定するように変更されました。 isVisible
関数の削除:reader
構造体からisVisible
ヘルパー関数が削除されました。これは、エクスポートの可視性チェックが、より適切な場所(例えばsortedFuncs
内)で行われるようになったことを示しています。collectEmbeddedMethods
の変更:collectEmbeddedMethods
関数がreader
のメソッドとなり、typ.embedded
マップをイテレートするように変更されました。これにより、埋め込みメソッドの収集ロジックが新しいデータ構造に適応しました。
-
sortedFuncs
における最終フィルタリングロジック:src/pkg/go/doc/reader.go
のsortedFuncs
関数が、allMethods
という新しいブール引数を受け取るようになりました。- この関数内で、メソッドをリストに含めるかどうかの条件が
m.Decl != nil && (allMethods || ast.IsExported(removeStar(m.Orig)))
に変更されました。m.Decl != nil
: メソッドが実際に宣言されていることを確認します(競合などで無効になったメソッドを除外)。allMethods
:AllMethods
フラグが有効な場合、すべてのメソッドを含めます。ast.IsExported(removeStar(m.Orig))
:AllMethods
フラグが無効な場合、メソッドの元のレシーバ型名(ポインタを示す*
を取り除いたもの)がエクスポートされている場合にのみ、そのメソッドを含めます。これにより、エクスポートされた匿名フィールドのメソッドがドキュメンテーションから除外されるという目的が達成されます。
removeStar
ヘルパー関数が追加され、レシーバ型名からポインタを示す*
を取り除くために使用されます。
これらの変更により、go/doc
はより柔軟にドキュメンテーションの表示を制御できるようになり、特に埋め込み型から昇格したメソッドの表示に関するユーザーのニーズに応えることが可能になりました。また、内部ロジックもよりクリーンで保守しやすいものになっています。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は以下のファイルに集中しています。
-
src/pkg/go/doc/doc.go
:Mode
型にAllMethods
フラグが追加されました。New
関数内でmode |= AllMethods
が追加され、一時的にAllMethods
がデフォルトで有効になるように設定されています。sortedTypes
およびsortedFuncs
の呼び出しで、新しいallMethods
引数が渡されるようになりました。
-
src/pkg/go/doc/exports.go
:filterFieldList
関数のシグネチャがbase *baseType
からparent *namedType
に変更されました。- 匿名フィールドの処理ロジックが大幅に簡素化され、
r.recordAnonymousField
の呼び出しに置き換えられました。以前の埋め込み型を追加する複雑なロジックが削除されています。 filterType
関数のシグネチャもbase *baseType
からparent *namedType
に変更されました。
-
src/pkg/go/doc/reader.go
:baseType
構造体がnamedType
にリネームされ、その内部構造も変更されました。特に、埋め込み型を管理するembedded
フィールドが[]embeddedType
からmap[*namedType]bool
に変更されました。addEmbeddedType
メソッドがnamedType
から削除されました。isVisible
ヘルパー関数が削除されました。lookupType
関数が*namedType
を返すように変更されました。recordAnonymousField
関数が新しく追加されました。 この関数は、匿名フィールドの型を親のnamedType
に記録する役割を担います。readValue
,readType
,readFunc
関数内で、baseType
の代わりにnamedType
が使用され、匿名フィールドの処理がrecordAnonymousField
を介して行われるようになりました。また、isVisible
のチェックが削除されています。collectEmbeddedMethods
関数がreader
のメソッドとなり、namedType
の新しいembedded
マップ構造に適応するように変更されました。cleanupTypes
関数内で、可視性チェックがr.mode&AllDecls != 0 || ast.IsExported(t.name)
に直接置き換えられました。sortedTypes
関数とsortedFuncs
関数がallMethods
引数を受け取るようになりました。sortedFuncs
関数内で、メソッドのフィルタリングロジックがm.Decl != nil && (allMethods || ast.IsExported(removeStar(m.Orig)))
に変更されました。 これが、エクスポートされた匿名フィールドのメソッドを非表示にする主要なロジックです。removeStar
ヘルパー関数が追加されました。
これらの変更は、go/doc
パッケージの内部構造とドキュメンテーション生成のフローに大きな影響を与えています。
コアとなるコードの解説
このコミットのコアとなるコードの変更は、主に src/pkg/go/doc/reader.go
内の namedType
構造体、recordAnonymousField
関数、そして sortedFuncs
関数に集約されます。
-
namedType
構造体 (src/pkg/go/doc/reader.go
):- 以前の
baseType
からnamedType
へのリネームは、その役割をより明確にするためのものです。この構造体は、Goのパッケージ内で定義される名前付きの型(構造体、インターフェース、基本型など)に関する情報を保持します。 - 最も重要な変更は、埋め込み型を追跡するための
embedded
フィールドが[]embeddedType
からmap[*namedType]bool
に変更された点です。
このマップのキーは埋め込まれたtype namedType struct { // ... isEmbedded bool // true if this type is embedded isStruct bool // true if this type is a struct embedded map[*namedType]bool // true if the embedded type is a pointer // ... }
namedType
へのポインタであり、値は埋め込みがポインタ型 (*T
) で行われたかどうかを示すブール値です。マップを使用することで、埋め込み型の追加や検索がより効率的になり、重複の管理も容易になります。
- 以前の
-
recordAnonymousField
関数 (src/pkg/go/doc/reader.go
):- この新しい関数は、構造体やインターフェースの匿名フィールド(埋め込み型)を
namedType
に記録する役割を担います。 - 以前は
filterFieldList
内で複雑なロジックが展開されていましたが、この関数に集約されました。 - 重要なのは、この関数が匿名フィールドの型がエクスポートされているかどうかに関わらず、すべての匿名フィールドを記録する点です。これは、メソッドの競合検出のためにすべての埋め込みメソッドを考慮する必要があるためです。
ftype.isEmbedded = true
の行は、この型が別の型に埋め込まれていることをマークします。parent.embedded[ftype] = ptr
の行で、親のnamedType
のembedded
マップに、埋め込まれた型とそのポインタ情報が記録されます。
func (r *reader) recordAnonymousField(parent *namedType, fieldType ast.Expr) (fname string) { fname, imp := baseTypeName(fieldType) if parent == nil || imp { return } if ftype := r.lookupType(fname); ftype != nil { ftype.isEmbedded = true // この型が埋め込まれていることをマーク _, ptr := fieldType.(*ast.StarExpr) parent.embedded[ftype] = ptr // 親のマップに埋め込み情報を記録 } return }
- この新しい関数は、構造体やインターフェースの匿名フィールド(埋め込み型)を
-
sortedFuncs
関数 (src/pkg/go/doc/reader.go
):- この関数は、最終的にドキュメンテーションに表示される関数やメソッドのリストを生成し、ソートする役割を担います。
- このコミットの主要な目的である「エクスポートされた匿名フィールドのメソッドを非表示にする」ロジックがここに実装されています。
allMethods
という新しいブール引数が追加されました。これはAllMethods
フラグの状態を反映します。- メソッドをリストに含めるかどうかの条件が以下のようになりました。
if m.Decl != nil && (allMethods || ast.IsExported(removeStar(m.Orig))) { list[i] = m i++ }
m.Decl != nil
: これは、メソッドが有効な宣言を持っていることを確認します。GoのAST処理では、競合するメソッドなどがDecl == nil
となる場合があります。allMethods
: もしAllMethods
フラグが有効(true
)であれば、この条件は常にtrue
となり、すべてのメソッドが含められます。これは、このコミットが既存の出力に影響を与えないようにするための現在のデフォルト挙動です。ast.IsExported(removeStar(m.Orig))
: もしAllMethods
フラグが無効(false
)であれば、この部分が評価されます。m.Orig
: メソッドの元のレシーバ型名(例:T
や*T
)。removeStar(m.Orig)
: レシーバ型名からポインタを示す*
を取り除きます(例:*T
からT
)。ast.IsExported(...)
: その結果の型名がエクスポートされている(大文字で始まる)かどうかをチェックします。
- このロジックにより、
AllMethods
が無効な場合、エクスポートされた匿名フィールドから昇格したメソッド(そのレシーバ型がエクスポートされているため)はドキュメンテーションから除外されます。
これらの変更は、go/doc
がドキュメンテーションの粒度をより細かく制御できるようにするための基盤を築いています。特に、埋め込みによるメソッドの昇格がドキュメンテーションの冗長性を引き起こす問題に対し、ユーザーがその表示を制御できるメカニズムを提供します。
関連リンク
- Go言語のドキュメンテーション: https://go.dev/doc/
go/doc
パッケージのドキュメンテーション: https://pkg.go.dev/go/doc- Go言語の埋め込みに関する公式ブログ記事(例: Effective Go - Embedding): https://go.dev/doc/effective_go#embedding
- Go言語のASTに関するドキュメンテーション: https://pkg.go.dev/go/ast
参考にした情報源リンク
- Go issue 2791: go/doc: don't show methods of exported anonymous fields - https://github.com/golang/go/issues/2791
- Go CL 5572076: go/doc: don't show methods of exported anonymous fields - https://go.googlesource.com/go/+/5572076 (これはGitHubのコミットページと同じ内容ですが、Goの公式ソースリポジトリのリンクです)
- Go言語のソースコード(
src/pkg/go/doc
ディレクトリ) - Go言語のドキュメンテーションに関する一般的な情報源(
go doc
コマンドの挙動など)