[インデックス 11379] ファイルの概要
このコミットは、Go言語の標準ライブラリであるgo/doc
パッケージの内部実装を大幅に書き換えるものです。具体的には、以下のファイルが変更されています。
src/pkg/go/doc/doc.go
:go/doc
パッケージの主要なデータ構造と、ドキュメント生成のエントリポイントであるNew
関数の定義が含まれています。このファイルでは、新しい内部構造に合わせてType
やMethod
の定義が変更され、New
関数のロジックが刷新されています。src/pkg/go/doc/exports.go
: ASTからエクスポートされていない(非公開の)宣言をフィルタリングするロジックが含まれています。このコミットでは、フィルタリング処理がより洗練され、新しい内部構造(reader
型)と連携するように変更されています。src/pkg/go/doc/reader.go
: ドキュメント生成の主要なロジックが実装されているファイルです。このコミットの変更の大部分がこのファイルに集中しており、docReader
型がreader
型に置き換えられ、メソッドセットの計算、型の収集、ソートなどの新しいフェーズが導入されています。src/pkg/go/doc/testdata/e.0.golden
: 新しいテストデータファイル。src/pkg/go/doc/testdata/e.1.golden
: 新しいテストデータファイル。src/pkg/go/doc/testdata/e.go
: 新しいテストケースのGoソースコード。埋め込み型とメソッドの競合に関するテストが含まれています。
コミット
このコミットは、go/doc
パッケージの内部をクリーンに書き直すものです。実装は以下の4つのフェーズに分割されています。
- 必要に応じて、入力ASTのエクスポートフィルタリング(
exports.go
)。 - フィルタリングされたASTの読み込み(
reader.go: type reader
)。 - メソッドセットの計算(
reader.go
)。 - 最終的なドキュメントのソートと作成(
reader.go
)。
古い実装とは対照的に、プレゼンテーションデータ(Names
, Docs
, Decls
など)は、対応するASTノードを読み込むとすぐに作成されます。また、すべての型は(埋め込み型であるかどうかにかかわらず)統一された方法で収集されます。
AST全体の処理が完了すると、すべてのメソッドと型が収集され、各型のメソッドセットが計算されます(フェーズ3)。
最終的なドキュメントを生成するために、メソッドセットと値マップがソートされます。
APIの変更はなく、既存のテストスイートは変更なしでパスします。
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/6d68be46dd37985d57eeec7d5191a6b9a719afa8
元コミット内容
commit 6d68be46dd37985d57eeec7d5191a6b9a719afa8
Author: Robert Griesemer <gri@golang.org>
Date: Wed Jan 25 09:53:26 2012 -0800
go/doc: clean rewrite of go/doc internals
The implementation is divided into 4 phases:
1) export filtering of an incoming AST if necessary (exports.go)
2) reading of a possibly filtered AST (reader.go: type reader)
3) method set computation (reader.go)
4) sorting and creation of final documentation (reader.go)
In contrast to the old implementation, the presentation data
(Names, Docs, Decls, etc.) are created immediately upon reading
the respective AST node. Also, all types are collected (embedded
or not) in a uniform way.
Once the entire AST has been processed, all methods and types
have been collected and the method sets for each type can be
computed (phase 3).
To produce the final documentation, the method sets and value
maps are sorted.
There are no API changes. Passes the existing test suite unchanged.
R=rsc, rogpeppe
CC=golang-dev
https://golang.org/cl/5554044
変更の背景
このコミットが行われた背景には、Go言語のドキュメンテーション生成ツールであるgo/doc
パッケージの内部構造の複雑さと、特にメソッドセットの正確な計算における課題があったと考えられます。
Go言語は、構造体の埋め込み(embedding)によって、ある型が別の型のメソッドを「継承」するような振る舞いを実現します。この埋め込みは多段階になることもあり、また、同じ名前のメソッドが複数の埋め込み元から提供される場合に、Go言語の仕様に則った競合解決ルール(より浅いレベルのメソッドが優先される、同じレベルで競合する場合はメソッドが存在しないとみなされるなど)を正確に適用する必要があります。
従来のgo/doc
の実装は、これらの複雑なルールを扱う上で、コードが読みにくく、メンテナンスが困難になっていた可能性があります。コミットメッセージにある「clean rewrite of go/doc internals」という表現は、まさにその内部的な複雑さを解消し、より明確で効率的な構造に再構築することを目指したものであることを示唆しています。
特に、以下の点が改善の動機となったと推測されます。
- メソッドセット計算の正確性と効率性: 埋め込み型を含む複雑な型構造におけるメソッドセットの計算は、Go言語のドキュメンテーションの正確性を保証する上で非常に重要です。古い実装では、この計算が非効率であったり、特定のコーナーケースで誤りがあったりした可能性があります。
- コードのモジュール化と可読性: ドキュメント生成のプロセスを明確な4つのフェーズに分割することで、各フェーズの責任が明確になり、コードの可読性とメンテナンス性が向上します。
- 統一された型収集: 埋め込み型を含むすべての型を統一的に収集するアプローチは、ドキュメント生成のロジックを簡素化し、将来的な機能拡張を容易にします。
- 即時的なデータ生成: ASTノードの読み込みと同時にプレゼンテーションデータを生成するアプローチは、中間状態の管理を簡素化し、メモリ使用量や処理速度の改善に寄与する可能性があります。
このコミットは、外部APIを変更せずに内部実装を改善する「リファクタリング」であり、既存のテストがパスするという事実は、この変更が機能的な後退を伴わない、堅牢な改善であることを示しています。
前提知識の解説
このコミットの技術的詳細を理解するためには、以下のGo言語に関する前提知識が必要です。
1. Go言語のgo/doc
パッケージ
go/doc
パッケージは、Goのソースコードからドキュメンテーションを生成するためのツールです。go doc
コマンドやgodoc
ツール(Go 1.11以降はgo doc
に統合)の基盤となっています。このパッケージは、Goのソースコードを解析して抽象構文木(AST)を構築し、そのASTからパッケージ、型、関数、変数、定数、メソッドなどのドキュメンテーションコメントを抽出し、構造化された形式で提供します。
2. GoのAST (Abstract Syntax Tree)
Goコンパイラは、ソースコードを解析する際に、そのコードの構造を表現する抽象構文木(AST)を生成します。go/ast
パッケージは、このASTをプログラム的に操作するための型と関数を提供します。go/doc
パッケージは、このASTを走査し、必要な情報を抽出してドキュメンテーションを構築します。例えば、ast.File
は単一のGoソースファイルを表し、ast.GenDecl
はvar
, const
, type
宣言を、ast.FuncDecl
は関数やメソッド宣言を表します。
3. Goの識別子のエクスポートルール
Go言語では、識別子(変数名、関数名、型名など)の最初の文字が大文字である場合、その識別子はパッケージ外にエクスポート(公開)されます。小文字で始まる場合は、パッケージ内でのみ利用可能な非公開(unexported)な識別子となります。go/doc
は通常、エクスポートされた識別子のみをドキュメント化しますが、特定のモード(AllDecls
)では非公開の宣言もドキュメント化できます。このコミットのexports.go
ファイルは、このエクスポートルールに基づいてASTをフィルタリングする役割を担っています。
4. Goのメソッドセットと構造体埋め込み(Embedding)
Go言語の型には「メソッドセット」という概念があります。これは、その型が持つすべてのメソッドの集合です。Goのメソッドセットのルールは、特に構造体の埋め込み(embedding)とポインタ型で複雑になります。
- 構造体埋め込み: Goでは、構造体の中に匿名フィールドとして別の構造体やインターフェースを埋め込むことができます。これにより、埋め込まれた型のメソッドが、埋め込み元の型のメソッドセットに「昇格」します。
type Inner struct { Name string } func (i Inner) Greet() string { return "Hello, " + i.Name } type Outer struct { Inner // Innerを埋め込み } // Outer型の変数oは、o.Greet()を呼び出すことができる
- メソッドの競合解決: 複数の埋め込み元から同じ名前のメソッドが提供される場合、Goには明確な競合解決ルールがあります。
- 浅いレベルの優先: より浅いレベル(直接埋め込まれているなど)で宣言されたメソッドが、より深いレベルで埋め込まれた同じ名前のメソッドよりも優先されます。
- 同じレベルでの競合: 同じレベルで複数の埋め込み元から同じ名前のメソッドが提供される場合、そのメソッドはメソッドセットに含まれません(競合により「消滅」します)。これは、どのメソッドを呼び出すべきか曖昧になるためです。
type A struct{} func (A) M() { fmt.Println("A.M") } type B struct{} func (B) M() { fmt.Println("B.M") } type C struct { A // 埋め込み B // 埋め込み } // C型の変数cは、c.M()を直接呼び出すことはできない(A.MとB.Mが競合するため) // ただし、c.A.M()やc.B.M()のように明示的にアクセスすることは可能
- ポインタレシーバと値レシーバ:
T
型のメソッドセットには、レシーバがT
であるすべてのメソッドが含まれます。*T
型のメソッドセットには、レシーバがT
であるすべてのメソッドと、レシーバが*T
であるすべてのメソッドが含まれます。- つまり、ポインタレシーバのメソッドは値レシーバのメソッドセットには含まれませんが、値レシーバのメソッドはポインタレシーバのメソッドセットに含まれます。
- 埋め込みの場合、ポインタ型として埋め込まれた構造体(例:
*Inner
)のメソッドは、そのポインタ型が持つメソッドセットのルールに従って昇格します。
これらのルールを正確に適用し、ドキュメンテーションに反映させることが、go/doc
パッケージの重要な役割であり、このコミットの主要な焦点の一つとなっています。
技術的詳細
このコミットは、go/doc
パッケージの内部処理を、より構造化され、効率的で、正確なものにするための抜本的な変更を導入しています。主要な技術的変更点は以下の通りです。
1. 4フェーズの処理フロー
コミットメッセージに明記されているように、ドキュメント生成プロセスは以下の4つの明確なフェーズに分割されました。
- フェーズ1: エクスポートフィルタリング (
exports.go
):- 入力されたASTから、エクスポートされていない(非公開の)宣言やフィールドをフィルタリングし、公開されるべき要素のみを残します。
filterIdentList
,filterFieldList
,filterParamList
,filterType
,filterSpec
,filterDecl
,fileExports
といった関数が、ASTノードを走査し、Goのエクスポートルールに基づいて不要な部分を削除または変更します。- 特に
filterFieldList
は、匿名フィールド(埋め込み型)が非公開であっても、そのメソッドが公開される可能性があるため、それらをbaseType
のembedded
リストに追加するロジックを含んでいます。
- フェーズ2: ASTの読み込みと初期データ収集 (
reader.go
):reader
型が導入され、パッケージのASTを読み込み、パッケージレベルのドキュメント、インポートパス、定数、変数、型、関数などの基本的な情報を収集します。reader.readPackage
関数が、ファイル名をソートして順にreader.readFile
を呼び出し、各ファイルのASTを処理します。reader.readFile
は、パッケージコメント、宣言(GenDecl
、FuncDecl
)、そしてBUG(...)
コメントを収集します。reader.readValue
は、const
やvar
宣言を処理し、必要に応じて特定の型に関連付けます。reader.readType
は、type
宣言を処理し、baseType
構造体を作成・更新します。この際、埋め込みフィールドも識別し、baseType.embedded
に追加します。reader.readFunc
は、関数やメソッド宣言を処理し、レシーバの有無に応じてパッケージレベルの関数または特定の型のメソッドとして登録します。- このフェーズでは、プレゼンテーションデータ(
Value
,Func
,Method
など)がASTノードから直接生成され、関連するASTノードのDoc
フィールドはnil
に設定され、重複して処理されないようにします。
- フェーズ3: メソッドセットの計算 (
reader.go
):reader.computeMethodSets
関数がこのフェーズの主要な役割を担います。- Goのメソッドセットのルール(特に埋め込みと競合解決)に従って、各型の最終的なメソッドセットを計算します。
methodSet
型(map[string]*Method
)が導入され、メソッドの追加と競合解決ロジック(methodSet.add
)をカプセル化します。collectEmbeddedMethods
関数は再帰的に呼び出され、埋め込み階層を深く探索し、埋め込み型から昇格するメソッドを収集します。この際、ポインタ埋め込みの挙動や、より浅いレベルのメソッドが優先されるルールが適用されます。customizeRecv
関数は、埋め込みによってレシーバの型が変わるメソッドのAST表現を調整します。
- フェーズ4: クリーンアップと最終的なドキュメントのソート (
reader.go
):reader.cleanupTypes
関数は、宣言が見つからない型(例えば、プリデクレアされた型や、ASTが不完全なために宣言が欠落している埋め込み型)に関連付けられた値、関数、メソッドをパッケージレベルに移動させ、情報が失われないようにします。また、非公開の型でドキュメント化の対象外となるものを削除します。- 最終的に、
sortedKeys
,sortedValues
,sortedTypes
,sortedFuncs
,sortedMethods
といったヘルパー関数が導入され、ドキュメントの表示順序を決定するために、収集されたデータが名前や宣言順に基づいてソートされます。
2. 主要なデータ構造の刷新
reader
型(旧docReader
):- パッケージ全体のドキュメント生成プロセスを管理する中心的な構造体です。
mode
,doc
,filenames
,bugs
,imports
,values
,types
,funcs
といったフィールドを持ち、ドキュメント生成に必要なすべての状態を保持します。readPackage
,readFile
,readValue
,readType
,readFunc
,computeMethodSets
,cleanupTypes
などの主要な処理メソッドを持ちます。
baseType
型(旧typeInfo
):- 個々の型(
struct
,interface
など)に関する情報を保持する構造体です。 doc
,name
,decl
,values
,funcs
,methods
,isEmbedded
,isStruct
,embedded
といったフィールドを持ちます。- 特に
funcs
とmethods
は、新しいmethodSet
型を使用しており、メソッドセットの計算ロジックがこの型に集約されています。 addEmbeddedType
メソッドは、埋め込み型を追跡するために使用されます。
- 個々の型(
methodSet
型:map[string]*Method
として定義され、特定の型に属するメソッドの集合を表します。set
メソッドは、新しいメソッドを追加する際に、既存のメソッドとの競合(特にドキュメントの有無)を考慮します。add
メソッドは、埋め込みによって昇格するメソッドを追加する際に、Goのメソッド競合解決ルール(レベルの優先、同じレベルでの競合による消滅)を適用します。sortedFuncs
とsortedMethods
は、メソッドセットをソートされたリストとして返すためのヘルパーです。
3. メソッドセット計算の改善
このコミットの最も重要な改善点の一つは、Goの複雑なメソッドセットルール、特に構造体埋め込みにおけるメソッドの昇格と競合解決を正確に処理する能力の向上です。
collectEmbeddedMethods
関数は、埋め込み階層を再帰的に探索し、各レベルで昇格するメソッドを収集します。methodSet.add
メソッドは、メソッドのLevel
フィールド(埋め込みの深さ)を利用して、より浅いレベルのメソッドを優先し、同じレベルでの競合を適切に処理します。これにより、go/doc
が生成するドキュメントがGo言語のセマンティクスと完全に一致するようになります。customizeRecv
関数は、埋め込みによってレシーバの型が変更される場合に、そのメソッドのAST表現を動的に調整し、ドキュメントに正確なレシーバ型が表示されるようにします。
4. その他の改善
go/token
パッケージの利用:doc.go
でsort
パッケージの代わりにgo/token
がインポートされています。これは、トークン(token.CONST
,token.VAR
など)を使用して宣言の種類を識別するためです。- テストの追加:
testdata/e.go
とその.golden
ファイルは、埋め込み型とメソッドの競合に関する具体的なテストケースを提供し、新しい実装がこれらの複雑なシナリオを正しく処理できることを保証します。 - API互換性の維持: 内部実装の大幅な変更にもかかわらず、外部に公開されている
go/doc
パッケージのAPIは変更されていません。これは、このリファクタリングが既存のGoツールチェーンやアプリケーションに影響を与えないことを意味します。
これらの変更により、go/doc
パッケージはより堅牢で、正確で、メンテナンスしやすいコードベースとなり、Go言語の進化する機能(特に型システムとメソッドセットのルール)に追従できるようになりました。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は、主に以下のファイルと関数/構造体に集約されています。
-
src/pkg/go/doc/doc.go
:Type
構造体からmethods
とembedded
フィールドが削除され、Methods
フィールドが[]*Method
型に変更されました。これは、メソッドセットの計算ロジックがreader.go
の新しいmethodSet
型に集約されたことを反映しています。New
関数の実装が大幅に書き換えられました。--- a/src/pkg/go/doc/doc.go +++ b/src/pkg/go/doc/doc.go @@ -35,11 +35,12 @@ type Value struct { order int } +// Method is the documentation for a method declaration. type Method struct { *Func // TODO(gri) The following fields are not set at the moment. Origin *Type // original receiver base type - Level int // embedding level; 0 means Func is not embedded + Level int // embedding level; 0 means Method is not embedded } // Type is the documentation for type declaration. @@ -54,9 +55,7 @@ type Type struct { Funcs []*Func // sorted list of functions returning this type Methods []*Method // sorted list of methods (including embedded ones) of this type - methods []*Func // top-level methods only - embedded methodSet // embedded methods only - order int + order int } // Func is the documentation for a func declaration. @@ -77,27 +76,22 @@ const ( AllDecls Mode = 1 << iota ) -// New computes the package documentation for the given package. -func New(pkg *ast.Package, importpath string, mode Mode) *Package { - var r docReader - r.init(pkg.Name, mode) - filenames := make([]string, len(pkg.Files)) - // sort package files before reading them so that the - // result is the same on different machines (32/64bit) - i := 0 - for filename := range pkg.Files { - filenames[i] = filename - i++ - } - sort.Strings(filenames) - - // process files in sorted order - for _, filename := range filenames { - f := pkg.Files[filename] - if mode&AllDecls == 0 { - r.fileExports(f) - } - r.addFile(f) - } - return r.newDoc(importpath, filenames) +// New computes the package documentation for the given package AST. +func New(pkg *ast.Package, importPath string, mode Mode) *Package { + var r reader + r.readPackage(pkg, mode) + r.computeMethodSets() + r.cleanupTypes() + return &Package{ + Doc: r.doc, + Name: pkg.Name, + ImportPath: importPath, + Imports: sortedKeys(r.imports), + Filenames: r.filenames, + Bugs: r.bugs, + Consts: sortedValues(r.values, token.CONST), + tTypes: sortedTypes(r.types), + Vars: sortedValues(r.values, token.VAR), + Funcs: r.funcs.sortedFuncs(), + } }
-
src/pkg/go/doc/exports.go
:docReader
レシーバがreader
レシーバに変更されました。filterFieldList
関数が、匿名フィールドの処理ロジックを改善し、baseType
に埋め込み型を追加するように変更されました。fileExports
関数が、ASTから非公開の宣言を削除するロジックを簡素化しました。
-
src/pkg/go/doc/reader.go
: このファイルは最も多くの変更があり、新しい内部構造の核心です。methodSet
型と関連メソッド: メソッドセットの計算と競合解決を扱う新しいマップ型が導入されました。type methodSet map[string]*Method func (mset methodSet) set(f *ast.FuncDecl) { ... } func (mset methodSet) add(m *Method) { ... } func (mset methodSet) sortedFuncs() []*Func { ... } func (mset methodSet) sortedMethods() []*Method { ... }
baseType
型: 型情報を保持する新しい構造体で、旧typeInfo
を置き換えます。type baseType struct { doc string // doc comment for type name string // local type name (excluding package qualifier) decl *ast.GenDecl // nil if declaration hasn't been seen yet // associated declarations values []*Value // consts and vars funcs methodSet methods methodSet isEmbedded bool // true if this type is embedded isStruct bool // true if this type is a struct embedded []embeddedType // list of embedded types }
reader
型: ドキュメント生成の主要なロジックをカプセル化する新しい構造体で、旧docReader
を置き換えます。type reader struct { mode Mode // package properties doc string // package documentation, if any filenames []string bugs []string // declarations imports map[string]int values []*Value // consts and vars types map[string]*baseType funcs methodSet }
- 主要な処理関数:
func (r *reader) readPackage(pkg *ast.Package, mode Mode)
: パッケージ全体の読み込みと処理をオーケストレーションします。func (r *reader) readFile(src *ast.File)
: 個々のソースファイルを読み込み、宣言を処理します。func (r *reader) readValue(decl *ast.GenDecl)
: 定数/変数宣言を処理します。func (r *reader) readType(decl *ast.GenDecl, spec *ast.TypeSpec)
: 型宣言を処理し、baseType
を作成します。func (r *reader) readFunc(fun *ast.FuncDecl)
: 関数/メソッド宣言を処理します。func (r *reader) computeMethodSets()
: 各型のメソッドセットを計算する中心的な関数です。func collectEmbeddedMethods(mset methodSet, typ *baseType, recvTypeName string, embeddedIsPtr bool, level int)
: 埋め込み型からメソッドを再帰的に収集します。func customizeRecv(m *Method, recvTypeName string, embeddedIsPtr bool, level int) *Method
: 埋め込みによってレシーバが変わるメソッドを調整します。func (r *reader) cleanupTypes()
: 宣言のない型や非公開の型をクリーンアップし、関連する宣言をパッケージレベルに移動します。
- ソートヘルパー:
sortBy
,sortedKeys
,sortedValues
,sortedTypes
など、最終的なドキュメントのソートに使用される汎用的なソート関数が追加されました。
これらの変更は、go/doc
の内部が、よりモジュール化され、Goの型システムとメソッドセットの複雑なルールを正確に処理できるように再設計されたことを示しています。
コアとなるコードの解説
src/pkg/go/doc/doc.go
の変更
Type
構造体の変更:methods
とembedded
フィールドが削除されたのは、これらの情報がreader.go
内の新しいbaseType
構造体とmethodSet
型によって一元的に管理されるようになったためです。これにより、Type
構造体は最終的なドキュメント表示に必要な情報のみを持つようになり、関心の分離が図られています。Methods []*Method
は、最終的にソートされたメソッドのリストを保持します。
New
関数の刷新:- 以前は
docReader
のinit
、fileExports
、addFile
、newDoc
といったメソッドを順に呼び出していましたが、新しい実装ではreader
型のインスタンスを作成し、r.readPackage(pkg, mode)
、r.computeMethodSets()
、r.cleanupTypes()
という3つの主要なフェーズを順に呼び出すようになりました。 - この変更は、ドキュメント生成プロセスが明確な段階に分割され、各段階が特定の責任を持つように再設計されたことを示しています。
r.readPackage
はASTの読み込みと初期データ収集を担当し、r.computeMethodSets
はメソッドセットの複雑な計算を、r.cleanupTypes
は最終的なデータ整理を行います。- 最終的な
Package
構造体の生成も、reader
が保持するデータから直接行われるようになり、よりシンプルになりました。
- 以前は
src/pkg/go/doc/exports.go
の変更
docReader
からreader
へのレシーバ変更:- これは、旧来の
docReader
型が新しいreader
型に置き換えられたことに伴う、コードベース全体にわたる変更の一部です。これにより、フィルタリングロジックが新しいreader
のコンテキストで動作するようになります。
- これは、旧来の
filterFieldList
の改善:- この関数は、構造体やインターフェースのフィールドリストから、エクスポートされていないフィールドをフィルタリングします。
- 特に重要なのは、匿名フィールド(埋め込み型)の処理です。たとえ匿名フィールド自体が非公開であっても、その型が公開されたメソッドを持っている場合、そのメソッドは埋め込み元の型に昇格して公開される可能性があります。この関数は、そのような埋め込み型を
baseType
のembedded
リストに適切に追加し、後続のメソッドセット計算フェーズで考慮されるようにします。
fileExports
の簡素化:- この関数は、Goソースファイル全体のASTから、エクスポートされていない宣言を削除します。
- 以前は戻り値でエクスポートされた宣言があるかどうかを返していましたが、新しい実装では単にASTをインプレースで変更するだけになり、その後の処理でエクスポートされた宣言の有無が判断されるようになりました。
src/pkg/go/doc/reader.go
の変更
このファイルは、このコミットの心臓部であり、Goのドキュメンテーション生成ロジックの大部分が再実装されています。
methodSet
型:- Goのメソッドセットの概念を直接モデル化したものです。
map[string]*Method
として、メソッド名をキーにMethod
構造体(メソッドのドキュメント情報)を保持します。 set(f *ast.FuncDecl)
:ast.FuncDecl
からMethod
を作成し、マップに追加します。同じ名前のメソッドが既に存在し、かつドキュメントを持っている場合は、新しいメソッドは無視されます。これは、複数のファイルに同じ名前の関数/メソッドが定義されている場合に、ドキュメントを持つ方を優先するためのGoの慣習を反映しています。add(m *Method)
: 埋め込みによって昇格するメソッドをメソッドセットに追加する際に使用されます。このメソッドは、Goのメソッド競合解決ルールを厳密に適用します。- もし追加しようとしているメソッド
m
が、既存のメソッドよりも「浅いレベル」(m.Level < old.Level
)にある場合、m
が優先されます。 - もし同じレベル(
m.Level == old.Level
)で同じ名前のメソッドが既に存在する場合、それは競合とみなされ、mset[m.Name]
がFunc == nil
のMethod
で上書きされます。これにより、競合するメソッドは最終的なドキュメントから除外されます。
- もし追加しようとしているメソッド
sortedFuncs()
/sortedMethods()
: マップ内のメソッドをソートされたリストとして返します。競合によりFunc == nil
となったエントリは除外されます。
- Goのメソッドセットの概念を直接モデル化したものです。
baseType
型:- Goの各型(構造体、インターフェースなど)に関する詳細な情報を保持します。
funcs
とmethods
フィールドがmethodSet
型になったことで、型に関連する関数とメソッドの管理がより構造化されました。embedded []embeddedType
フィールドは、その型が埋め込んでいる匿名フィールドの型を追跡します。これは、埋め込みによるメソッドの昇格を正確に計算するために不可欠です。
reader
型:- パッケージ全体のドキュメント生成の状態を管理します。
readPackage(pkg *ast.Package, mode Mode)
:- パッケージ内のファイルをソートし、各ファイルに対して
fileExports
(エクスポートフィルタリング)とreadFile
(AST読み込みと初期データ収集)を実行します。 - この関数が、ドキュメント生成の最初の2つのフェーズを統合しています。
- パッケージ内のファイルをソートし、各ファイルに対して
computeMethodSets()
:- すべての型が収集された後、この関数が呼び出され、各型に対して最終的なメソッドセットを計算します。
collectEmbeddedMethods
を呼び出すことで、埋め込み階層を再帰的に探索し、昇格するメソッドを収集します。
collectEmbeddedMethods(mset methodSet, typ *baseType, recvTypeName string, embeddedIsPtr bool, level int)
:- 再帰的に埋め込み型を走査し、その型が持つメソッドを
mset
に追加します。 embeddedIsPtr
引数は、ポインタ埋め込みのセマンティクス(ポインタとして埋め込まれた場合、その後の埋め込みもポインタとして扱われる)を正確に反映します。level
引数は、メソッドの埋め込み深さを追跡し、競合解決ルール(より浅いレベルが優先)に利用されます。customizeRecv
を呼び出すことで、昇格したメソッドのレシーバ型を、埋め込み元の型に合わせて調整します。
- 再帰的に埋め込み型を走査し、その型が持つメソッドを
cleanupTypes()
:computeMethodSets
の後に実行され、最終的なドキュメント表示のためにデータを整理します。- 宣言がない型(例:プリデクレアされた型や、ASTが不完全なために宣言が欠落している埋め込み型)に関連付けられた値、関数、メソッドをパッケージレベルに移動させ、情報が失われないようにします。
- 非公開で、かつ埋め込みによっても公開されない型は、最終的なドキュメントから削除されます。
- ソートヘルパー関数:
sortBy
,sortedKeys
,sortedValues
,sortedTypes
などの関数は、Goのsort
パッケージを汎用的に利用するためのラッパーです。- これにより、最終的なドキュメントの要素(インポート、定数、変数、型、関数、メソッド)が、名前や宣言順に基づいて一貫した順序で表示されることが保証されます。
これらの変更により、go/doc
はGo言語の複雑な型システムとメソッドセットのルールをより正確に、かつ効率的に処理できるようになり、生成されるドキュメントの品質と信頼性が向上しました。
関連リンク
- Go言語公式ドキュメント: https://go.dev/
go/doc
パッケージのドキュメント: https://pkg.go.dev/go/docgo/ast
パッケージのドキュメント: https://pkg.go.dev/go/ast- Go言語仕様 - メソッドセット: https://go.dev/ref/spec#Method_sets
- Go言語仕様 - 構造体型(埋め込みについて): https://go.dev/ref/spec#Struct_types
参考にした情報源リンク
- Go言語公式ブログ - Go's Declaration Syntax: https://go.dev/blog/declaration-syntax (Goの宣言構文とエクスポートルールに関する基本的な理解に役立ちます)
- Go言語公式ブログ - The Go Programming Language Specification: https://go.dev/ref/spec (メソッドセットや埋め込みの厳密なルールを確認するために参照しました)
- Goのソースコード(特に
go/doc
パッケージの他のコミット履歴や関連するIssue): このコミットの背景にある課題や設計思想を理解するために、GoのGitHubリポジトリの関連部分を参考にしました。