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

[インデックス 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 という新しいフラグによって制御され、将来的にはこのフラグがデフォルトで無効になる予定です。

変更の主なポイントは以下の通りです。

  1. AllMethods フラグの導入: エクスポートされた(したがって可視な)埋め込みフィールドの埋め込みメソッドを最終的なパッケージドキュメンテーションに表示しないようにするためのフラグが追加されました。
  2. ロジックの簡素化: メソッドの競合を常に検出する必要があるため、型やメソッドの時期尚早な排除を削除し、すべての名前付き型とすべてのメソッドを収集してから最後にフィルタリングを行うように変更されました。これにより、既存のロジックが簡素化され、コードの削除が多く行われました。
  3. 内部データ構造の改善:
    • baseTypenamedType にリネームされました。
    • 埋め込み型を記録するロジックが合理化されました。
    • 埋め込み型がマップ(map[*namedType]bool)を介して記録されるようになり、データ構造がよりシンプルになりました。
  4. 段階的な導入: 現在のところ AllMethods フラグはデフォルトで有効になっているため、このコミットによるドキュメンテーションの出力は変更されません。次の変更リスト(CL)でこのフラグが無効にされ、関連するテストが調整され、Issue 2791が修正される予定です。

変更の背景

Go言語のドキュメンテーションツール go/doc は、Goのソースコードから自動的にドキュメンテーションを生成します。Goの言語仕様には「埋め込み(embedding)」という強力な機能があり、構造体内に匿名フィールドとして別の型を埋め込むことで、その埋め込まれた型のメソッドを外側の構造体が「継承」したかのように振る舞わせることができます。

しかし、この埋め込み機能がドキュメンテーション生成において問題を引き起こすことがありました。特に、エクスポートされた(つまり外部からアクセス可能な)匿名フィールドのメソッドが、そのフィールドが埋め込まれた構造体のドキュメンテーションに表示されることで、ドキュメンテーションが冗長になったり、ユーザーにとって本当に重要な情報が埋もれてしまったりする可能性がありました。

このコミットの背景には、このようなドキュメンテーションの冗長性を減らし、より簡潔で分かりやすいドキュメンテーションを提供したいという意図があります。Issue 2791("go/doc: don't show methods of exported anonymous fields")がこの変更の直接的な動機となっており、ユーザーがドキュメンテーションで本当に見たいのは、その型が直接定義しているメソッドや、非エクスポートの埋め込み型から「昇格」してきたメソッドであり、エクスポートされた埋め込み型が持つメソッドは、その埋め込み型自体のドキュメンテーションを見れば十分である、という考えに基づいています。

また、この変更は単に表示を制御するだけでなく、ドキュメンテーション生成の内部ロジックをより堅牢で効率的なものにするためのリファクタリングも兼ねています。特に、メソッドの競合検出を正確に行うために、型やメソッドのフィルタリングを最終段階で行うように変更することで、ロジックの複雑性を軽減し、将来的な拡張性も考慮されています。

前提知識の解説

このコミットを理解するためには、以下のGo言語およびドキュメンテーション生成に関する前提知識が必要です。

  1. Go言語の埋め込み(Embedding):

    • Go言語では、構造体の中にフィールド名なしで別の型(構造体、インターフェースなど)を宣言することができます。これを「埋め込み」と呼びます。
    • 埋め込まれた型のメソッドは、外側の構造体のメソッドとして「昇格(promoted)」され、外側の構造体のインスタンスから直接呼び出すことができます。
    • 例:
      type Engine struct {
          Power int
      }
      func (e Engine) Start() { /* ... */ }
      
      type Car struct {
          Engine // Engine型を匿名フィールドとして埋め込み
          Brand string
      }
      // car.Start() のようにEngineのStartメソッドを呼び出せる
      
    • 埋め込みは、コードの再利用やインターフェースの実装を簡潔にするための強力なメカニズムですが、ドキュメンテーションの観点からは、どのメソッドがその型に「固有」のものなのか、どのメソッドが埋め込みによって「昇格」してきたものなのかを区別することが重要になる場合があります。
  2. Goのドキュメンテーションツール (go/doc パッケージ):

    • Goには、ソースコードのコメントから自動的にドキュメンテーションを生成する標準ツールがあります。これは go doc コマンドや godoc サーバーによって利用されます。
    • go/doc パッケージは、このドキュメンテーション生成のコアロジックを提供します。AST(抽象構文木)を解析し、パッケージ、型、関数、変数、定数、メソッドなどの情報を抽出し、整形されたドキュメンテーション構造を構築します。
    • ドキュメンテーションの生成時には、エクスポートされた(大文字で始まる)識別子のみがデフォルトで表示されます。非エクスポートの識別子は通常、ドキュメンテーションには含まれません。
  3. AST (go/ast パッケージ):

    • Goコンパイラは、ソースコードを解析してASTを構築します。go/doc パッケージも、このASTを操作してコードの構造やコメントを読み取ります。
    • ast.FieldList は構造体やインターフェースのフィールド(またはメソッド)のリストを表します。
    • ast.IsExported(name string) は、与えられた名前がエクスポートされている(つまり大文字で始まる)かどうかを判定するヘルパー関数です。
  4. メソッドセット(Method Set):

    • Goの型には「メソッドセット」という概念があります。これは、その型が持つすべてのメソッドの集合です。
    • ポインタ型と非ポインタ型ではメソッドセットが異なる場合があります。
    • 埋め込みによってメソッドが昇格する場合、そのメソッドは外側の型のメソッドセットに追加されます。

これらの知識が、コミットが解決しようとしている問題(ドキュメンテーションの冗長性)と、その解決策(AllMethods フラグと内部ロジックの変更)を理解する上で不可欠です。

技術的詳細

このコミットの技術的な詳細は、主に go/doc パッケージ内のドキュメンテーション生成ロジックの変更に集約されます。

  1. AllMethods フラグの導入と制御:

    • src/pkg/go/doc/doc.goAllMethods Mode = 1 << iota が追加され、Mode 型の新しいビットフラグとして定義されました。
    • New 関数内で mode |= AllMethods が一時的に設定されています。これは、このコミット時点では AllMethods を常に有効にし、既存のドキュメンテーション出力に影響を与えないようにするためのものです。コミットメッセージにあるように、将来のCLでこの行が削除され、AllMethods がデフォルトで無効になる予定です。
    • sortedTypes 関数と sortedFuncs 関数に allMethods というブール引数が追加され、このフラグの値に基づいてメソッドのフィルタリングが行われるようになりました。
  2. baseType から namedType へのリネームと構造体の変更:

    • src/pkg/go/doc/reader.go において、ドキュメンテーション生成の内部で型情報を保持するために使用されていた baseType 構造体が namedType にリネームされました。これは、その型が「名前付きの型」であることをより明確に示します。
    • namedType 構造体内の埋め込み型を管理する方法が変更されました。以前は []embeddedType スライスで管理されていましたが、map[*namedType]bool に変更されました。マップのキーは埋め込み型へのポインタ、値はそれがポインタ型として埋め込まれているかどうかを示すブール値です。これにより、埋め込み型の管理がよりシンプルかつ効率的になります。
    • addEmbeddedType メソッドが削除され、埋め込み型の記録ロジックが recordAnonymousField 関数に集約されました。
  3. 埋め込み型とメソッドの処理ロジックの変更:

    • recordAnonymousField 関数の導入: src/pkg/go/doc/reader.go に新しく recordAnonymousField 関数が追加されました。この関数は、匿名フィールドの型を親の namedType に記録する役割を担います。この関数は、埋め込み型がエクスポートされているかどうかに関わらず、すべての匿名フィールドの型を記録します。これにより、メソッドの競合検出に必要なすべての情報が収集されるようになります。
    • フィルタリングの遅延: 以前のロジックでは、filterFieldListreadFunc の中で、メソッドをドキュメンテーションに含めるかどうかを早期に判断し、不要なメソッドを排除していました。このコミットでは、この「時期尚早な排除」が削除されました。代わりに、すべての名前付き型とすべてのメソッドをまず収集し、その後に sortedFuncs のような最終的なソート・フィルタリング段階で表示の有無を決定するように変更されました。
    • isVisible 関数の削除: reader 構造体から isVisible ヘルパー関数が削除されました。これは、エクスポートの可視性チェックが、より適切な場所(例えば sortedFuncs 内)で行われるようになったことを示しています。
    • collectEmbeddedMethods の変更: collectEmbeddedMethods 関数が reader のメソッドとなり、typ.embedded マップをイテレートするように変更されました。これにより、埋め込みメソッドの収集ロジックが新しいデータ構造に適応しました。
  4. sortedFuncs における最終フィルタリングロジック:

    • src/pkg/go/doc/reader.gosortedFuncs 関数が、allMethods という新しいブール引数を受け取るようになりました。
    • この関数内で、メソッドをリストに含めるかどうかの条件が m.Decl != nil && (allMethods || ast.IsExported(removeStar(m.Orig))) に変更されました。
      • m.Decl != nil: メソッドが実際に宣言されていることを確認します(競合などで無効になったメソッドを除外)。
      • allMethods: AllMethods フラグが有効な場合、すべてのメソッドを含めます。
      • ast.IsExported(removeStar(m.Orig)): AllMethods フラグが無効な場合、メソッドの元のレシーバ型名(ポインタを示す * を取り除いたもの)がエクスポートされている場合にのみ、そのメソッドを含めます。これにより、エクスポートされた匿名フィールドのメソッドがドキュメンテーションから除外されるという目的が達成されます。
    • removeStar ヘルパー関数が追加され、レシーバ型名からポインタを示す * を取り除くために使用されます。

これらの変更により、go/doc はより柔軟にドキュメンテーションの表示を制御できるようになり、特に埋め込み型から昇格したメソッドの表示に関するユーザーのニーズに応えることが可能になりました。また、内部ロジックもよりクリーンで保守しやすいものになっています。

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

このコミットにおけるコアとなるコードの変更箇所は以下のファイルに集中しています。

  1. src/pkg/go/doc/doc.go:

    • Mode 型に AllMethods フラグが追加されました。
    • New 関数内で mode |= AllMethods が追加され、一時的に AllMethods がデフォルトで有効になるように設定されています。
    • sortedTypes および sortedFuncs の呼び出しで、新しい allMethods 引数が渡されるようになりました。
  2. src/pkg/go/doc/exports.go:

    • filterFieldList 関数のシグネチャが base *baseType から parent *namedType に変更されました。
    • 匿名フィールドの処理ロジックが大幅に簡素化され、r.recordAnonymousField の呼び出しに置き換えられました。以前の埋め込み型を追加する複雑なロジックが削除されています。
    • filterType 関数のシグネチャも base *baseType から parent *namedType に変更されました。
  3. 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 関数に集約されます。

  1. 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) で行われたかどうかを示すブール値です。マップを使用することで、埋め込み型の追加や検索がより効率的になり、重複の管理も容易になります。
  2. recordAnonymousField 関数 (src/pkg/go/doc/reader.go):

    • この新しい関数は、構造体やインターフェースの匿名フィールド(埋め込み型)を namedType に記録する役割を担います。
    • 以前は filterFieldList 内で複雑なロジックが展開されていましたが、この関数に集約されました。
    • 重要なのは、この関数が匿名フィールドの型がエクスポートされているかどうかに関わらず、すべての匿名フィールドを記録する点です。これは、メソッドの競合検出のためにすべての埋め込みメソッドを考慮する必要があるためです。
    • ftype.isEmbedded = true の行は、この型が別の型に埋め込まれていることをマークします。
    • parent.embedded[ftype] = ptr の行で、親の namedTypeembedded マップに、埋め込まれた型とそのポインタ情報が記録されます。
    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
    }
    
  3. 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 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 コマンドの挙動など)