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

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

このコミットは、Go言語のドキュメンテーションツールである go/doc パッケージのテストケースを追加・修正するものです。具体的には、構造体に埋め込まれたフィールドがメソッドの競合を引き起こす場合の go/doc の振る舞いを検証しています。特に、エクスポートされた(可視な)匿名フィールドとエクスポートされていない(不可視な)匿名フィールドの両方を通じて同じ名前のメソッドが埋め込まれる場合に、そのメソッドがドキュメントに表示されるべきではないという挙動をテストしています。

コミット

commit a0d0ed200210012a42d4f6e32b4b52004ca3c46e
Author: Robert Griesemer <gri@golang.org>
Date:   Fri Jan 27 14:45:31 2012 -0800

    go/doc: added test case
    
    Don't show conflicting method embedded via
    a visible and invisible anonymous field.
    
    R=rsc
    CC=golang-dev
    https://golang.org/cl/5564064

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

https://github.com/golang/go/commit/a0d0ed200210012a42d4f6e32b4b52004ca3c46e

元コミット内容

このコミットの目的は、go/doc ツールが、可視(エクスポート済み)および不可視(エクスポートされていない)の匿名フィールドを介して埋め込まれた、競合するメソッドを表示しないようにするためのテストケースを追加することです。これは、Goの言語仕様におけるメソッドの埋め込みと競合解決のルールに go/doc が正しく従っていることを保証するためです。

変更の背景

Go言語の構造体埋め込みは、コードの再利用とインターフェースの実装において強力な機能です。しかし、複数の埋め込みフィールドが同じ名前のメソッドを持つ場合、メソッドの競合が発生する可能性があります。Goの言語仕様では、このような競合を解決するための明確なルールが定められています。

特に重要なのは、メソッドがエクスポートされた型とエクスポートされていない型の両方から同じ名前で埋め込まれる場合です。この場合、Goのコンパイラは曖昧さ(ambiguity)として扱い、そのメソッドは埋め込み元の構造体からは直接アクセスできなくなります。go/doc のようなドキュメンテーションツールは、このような言語のセマンティクスを正確に反映する必要があります。

このコミット以前は、go/doc がこのような特定の競合シナリオ(可視と不可視の匿名フィールドからの競合)を適切に処理していなかったか、あるいはその挙動が十分にテストされていなかった可能性があります。このテストケースの追加は、go/doc がGo言語のメソッド解決ルールに厳密に従い、ユーザーに誤解を招くようなドキュメントを生成しないようにするために行われました。これにより、生成されるドキュメントが実際のコードの振る舞いを正確に反映することが保証されます。

前提知識の解説

Go言語の構造体埋め込み (Struct Embedding)

Go言語では、構造体内に匿名フィールドとして別の構造体やインターフェースを埋め込むことができます。これにより、埋め込まれた型のメソッドやフィールドが、埋め込み元の構造体のメソッドやフィールドであるかのように「昇格(promoted)」されます。これは、継承とは異なり、コンポジション(合成)の一種と見なされます。

例:

type Engine struct {
    power int
}

func (e Engine) Start() { /* ... */ }

type Car struct {
    Engine // Engineを埋め込み
    brand string
}

func main() {
    c := Car{brand: "Toyota"}
    c.Start() // CarはEngineのStartメソッドを「継承」したかのように呼び出せる
}

メソッドセット (Method Sets)

Goの各型は、その型が持つメソッドの集合である「メソッドセット」を持っています。構造体に匿名フィールドが埋め込まれると、埋め込まれた型のメソッドセットが埋め込み元の構造体のメソッドセットに「昇格」されます。

メソッドの競合解決ルール (Method Conflict Resolution Rules)

複数の埋め込みフィールドが同じ名前のメソッドを持つ場合、Goは以下のルールで競合を解決します。

  1. 直接定義されたメソッドの優先: 埋め込み元の構造体自身に定義されたメソッドは、埋め込まれたフィールドから昇格されたメソッドよりも優先されます。
  2. 浅いレベルの優先: 複数の埋め込みレベルがある場合、より浅い(埋め込み元の構造体に近い)レベルで定義されたメソッドが優先されます。
  3. 曖昧さ (Ambiguity):
    • 同じレベルで複数の埋め込みフィールドが同じ名前のメソッドを提供し、それらのメソッドが異なるシグネチャを持つ場合、コンパイルエラーとなります。
    • 同じレベルで複数の埋め込みフィールドが同じ名前のメソッドを提供し、それらのメソッドが同じシグネチャを持つ場合、そのメソッドは埋め込み元の構造体からは直接アクセスできなくなります。これは「曖昧さ」として扱われ、コンパイルエラーにはなりませんが、そのメソッドを呼び出すには明示的に埋め込みフィールドを指定する必要があります。
    • 特に、エクスポートされた(大文字で始まる)匿名フィールドとエクスポートされていない(小文字で始まる)匿名フィールドの両方を通じて同じ名前のメソッドが埋め込まれる場合、これは曖昧さとして扱われ、そのメソッドは埋め込み元の構造体からは直接アクセスできなくなります。 このコミットが対処しているのはこのケースです。

go/doc パッケージ

go/doc パッケージは、Goのソースコードからドキュメンテーションを生成するためのツールです。Goの標準ライブラリの一部であり、go doc コマンドや godoc サーバーの基盤となっています。このパッケージは、コード内のコメント、型定義、関数シグネチャなどを解析し、構造化されたドキュメントを生成します。メソッドの埋め込みや競合解決といったGo言語の複雑なセマンティクスを正確に反映することが求められます。

技術的詳細

このコミットの核心は、Goのメソッド解決ルールにおける「曖昧さ」の特定のケース、特にエクスポートされた型とエクスポートされていない型の両方から同じ名前のメソッドが埋め込まれる場合の go/doc の振る舞いを検証することです。

Goの言語仕様では、構造体 S が匿名フィールド AB を埋め込み、AB の両方が同じ名前のメソッド M を持つ場合、S.M() の呼び出しは曖昧であると見なされ、コンパイルエラーにはなりませんが、S のメソッドセットには M が含まれません。したがって、S のインスタンスから直接 M を呼び出すことはできません。呼び出すには s.A.M() または s.B.M() のように明示的に指定する必要があります。

このルールは、埋め込まれたフィールドの可視性(エクスポートされているか否か)にも適用されます。例えば、T4 のケースでは、t2 (エクスポートされていない型) と T2 (エクスポートされた型) の両方が M() メソッドを持っています。T4 はこれら両方を匿名フィールドとして埋め込んでいます。この状況では、T4 のメソッドセットには M() は含まれません。なぜなら、t2.M()T2.M() のどちらを「昇格」させるべきかについて曖昧さが生じるためです。Goのコンパイラはこの曖昧さを解決せず、結果として T4M() メソッドを持たないと見なされます。

go/doc ツールは、この言語のセマンティクスを正確に反映する必要があります。つまり、T4 のドキュメントを生成する際に、M() メソッドを T4 の直接のメソッドとして表示してはなりません。このコミットは、この正しい挙動を保証するためのテストケースを追加しています。

具体的には、e.go 内の T4 の定義と、それに対応する e.0.golden および e.1.golden ファイルの期待される出力が、この曖昧さによるメソッドの非表示を反映するように変更されています。これにより、go/doc がこのような複雑な埋め込みシナリオにおいても正確なドキュメントを生成できることが保証されます。

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

このコミットでは、主に src/pkg/go/doc/testdata/ ディレクトリ内の3つのファイルが変更されています。

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

    • パッケージコメントがより明確な説明に更新されました。
    • 既存の T1 構造体に関するコメントが修正され、競合が「トップレベル」ではなく「埋め込まれた(レベル1)」メソッドに関するものであることが明確化されました。
    • 新しい構造体 T4 が追加されました。この T4 は、エクスポートされていない型 t2 とエクスポートされた型 T2 の両方を匿名フィールドとして埋め込んでいます。t2T2 は両方とも M() メソッドを持っています。
      // ----------------------------------------------------------------------------
      // Don't show conflicting methods M embedded via an exported and non-exported
      // type.
      
      // T1 has no embedded (level 1) M method due to conflict.
      type T4 struct {
      	t2
      	T2
      }
      
  2. src/pkg/go/doc/testdata/e.0.golden:

    • e.go の変更に合わせて、T1 のコメントが更新されました。
    • 新しく追加された T4 構造体とそのコメントが、go/doc の出力に表示されるべき内容として追加されました。T4 の下には M() メソッドがリストされていないことが重要です。
  3. src/pkg/go/doc/testdata/e.1.golden:

    • e.0.golden と同様に、T1 のコメントが更新されました。
    • T4 構造体とそのコメントが追加されました。ここでも、T4 の下には M() メソッドがリストされていません。

これらの .golden ファイルは、go/doc ツールが e.go を解析した際に生成されるべき期待される出力を表しています。テストは、go/doc の実際の出力がこれらの .golden ファイルと一致するかどうかを検証します。

コアとなるコードの解説

このコミットのコアとなる変更は、e.go に追加された T4 構造体と、それに対応する .golden ファイルの更新です。

e.go の関連部分を再掲します。

// t2.M should not appear as method in a Tx type.
func (t2) M() {}

// T2.M should appear as method of T2.
func (T2) M() {}

// T1 has no embedded (level 1) M method due to conflict.
type T1 struct {
	t1
	t2
}

// ----------------------------------------------------------------------------
// Don't show conflicting methods M embedded via an exported and non-exported
// type.

// T1 has no embedded (level 1) M method due to conflict.
type T4 struct {
	t2
	T2
}

ここで、t2 はエクスポートされていない型であり、M() メソッドを持っています。T2 はエクスポートされた型であり、こちらも M() メソッドを持っています。

T4 構造体は、t2T2 の両方を匿名フィールドとして埋め込んでいます。Goのメソッド解決ルールに従うと、T4t2.M()T2.M() の両方を「昇格」させようとしますが、これらは同じ名前のメソッドであり、かつ異なる可視性(エクスポートされているか否か)を持つ型から来ているため、曖昧さが発生します。この曖昧さにより、M() メソッドは T4 のメソッドセットには含まれません。つまり、T4 のインスタンスから直接 t4.M() のように呼び出すことはできません。

このコミットは、go/doc がこの言語のセマンティクスを正しく理解し、ドキュメントに T4 のメソッドとして M() を表示しないことを保証します。e.0.goldene.1.golden の出力では、T4 の定義の下に M() メソッドがリストされていないことが確認できます。これは、go/doc がGoの言語仕様に厳密に従い、曖昧なメソッドをドキュメントに含めないという正しい振る舞いをしていることを示しています。

このテストケースの追加により、将来的に go/doc の内部ロジックが変更された場合でも、このような特定のメソッド競合シナリオにおける正しいドキュメント生成が維持されることが保証されます。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメントと仕様
  • Go言語の構造体埋め込みとメソッドセットに関する一般的な解説記事
  • GitHub上のGoリポジトリのコミット履歴と関連するIssue/CL (Change List)
  • go/doc パッケージのソースコード (特にテストファイル)