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

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

このコミットは、Go言語の標準ライブラリにおけるgo/astパッケージからgo/docパッケージへのExampleコード抽出ロジックの移動に関するものです。具体的には、GoのソースコードからExample関数(ExampleFooのような形式で記述され、_test.goファイルに配置されることが多い)を解析し、その情報を提供する機能がgo/astからgo/docへと移管されました。これにより、Exampleコードの処理がより適切なパッケージに集約され、go/docパッケージの責務が明確化されました。

コミット

commit b6e2d6b778aa63d10db72feb3b03fb0becac38da
Author: Robert Griesemer <gri@golang.org>
Date:   Fri Feb 17 12:00:12 2012 -0800

    go/doc: move Example code from go/ast to go/doc.
    
    Fixes #3048.
    
    R=rsc
    CC=golang-dev
    https://golang.org/cl/5672081

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

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

元コミット内容

このコミットの元の内容は、go/astパッケージに存在していたExampleコードを処理するロジックをgo/docパッケージに移動することです。これには、Example構造体、Examples関数、およびExampleの出力を解析する関連ヘルパー関数が含まれます。この移動に伴い、これらの機能を利用していたcmd/go(Goコマンドラインツール)やcmd/godoc(Goドキュメンテーションツール)などのコンシューマ側も、新しいgo/docパッケージのAPIを使用するように変更されています。

変更の背景

この変更の背景には、Go言語の標準ライブラリにおけるパッケージの責務の明確化があります。

  1. Issue #3048の解決: このコミットはGoのIssue #3048「go/ast should not know about Example functions」を修正します。このIssueでは、go/astパッケージがExample関数に関する知識を持つべきではないという点が指摘されていました。go/astパッケージはGoのソースコードの抽象構文木(AST)を表現するためのものであり、特定のドキュメンテーションやテストの慣習(Example関数など)に関するロジックを持つことは、その責務を超えていると考えられました。
  2. go/docの責務の強化: go/docパッケージは、Goのソースコードからドキュメンテーションを生成するための情報(パッケージ、型、関数、変数、定数、そしてExample)を抽出する役割を担っています。Example関数は、Goのドキュメンテーションシステムにおいて非常に重要な要素であり、コードの動作例を示すために使用されます。したがって、Exampleコードの解析と抽出のロジックは、ASTの構造そのものよりも、ドキュメンテーション生成の文脈でより適切にgo/docパッケージに属すると判断されました。
  3. コードの分離と保守性の向上: Exampleコードの処理ロジックをgo/astからgo/docに移動することで、各パッケージの関心事が分離され、コードベース全体の保守性と理解度が向上します。go/astは純粋なAST表現に集中し、go/docはドキュメンテーション関連の処理に集中できるようになります。

前提知識の解説

このコミットを理解するためには、以下のGo言語の概念とツールに関する知識が必要です。

  1. Go言語のパッケージシステム: Goのコードはパッケージに分割され、再利用可能なモジュールとして機能します。各パッケージは特定の責務を持ちます。
  2. go/astパッケージ: Goのソースコードを解析して生成される抽象構文木(Abstract Syntax Tree: AST)を表現するためのデータ構造と関数を提供します。プログラミング言語のコンパイラやツールがコードを理解・操作する際の基盤となります。
  3. go/docパッケージ: GoのソースコードからドキュメンテーションコメントやExample関数などの情報を抽出し、構造化されたドキュメンテーションデータを生成するための機能を提供します。godocツールはこのパッケージを利用してドキュメンテーションを生成します。
  4. GoのExample関数: Goでは、func ExampleFoo()func ExamplePackage_Method()のようにExampleプレフィックスを持つ関数を_test.goファイルに記述することで、コードの実行例を示すことができます。これらの関数はテストの一部として実行され、その出力が期待される出力と一致するか検証されます。また、godocツールによって自動的にドキュメンテーションに組み込まれ、ユーザーがコードの動作を理解するのに役立ちます。
  5. godocツール: Go言語の公式ドキュメンテーションツールです。Goのソースコードからドキュメンテーションを抽出し、HTML形式で表示したり、コマンドラインで参照したりすることができます。Example関数もgodocによって表示されます。
  6. go testコマンド: Goのテストを実行するためのコマンドです。Example関数もgo testによって実行され、その出力が検証されます。
  7. src/cmd/dist/build.c: GoのツールチェインをビルドするためのC言語のスクリプトです。Goの標準ライブラリパッケージのビルド順序やクリーンアップ対象などを定義しています。
  8. go.mod / go.sum: Goモジュールシステムにおける依存関係管理ファイルです。このコミットが行われた2012年時点ではGoモジュールは存在せず、Goのビルドシステムは異なる方法で依存関係を管理していました。しかし、概念としてはパッケージ間の依存関係を管理する仕組みが存在していました。

技術的詳細

このコミットの技術的詳細は、主に以下の点に集約されます。

  1. ファイルのリネームとパッケージ名の変更:
    • src/pkg/go/ast/example.gosrc/pkg/go/doc/example.goにリネームされました。
    • これにより、ファイル内のパッケージ宣言がpackage astからpackage docに変更されました。
  2. 型定義の移動と修飾:
    • go/astパッケージに存在していたExample構造体(Example関数のメタデータを保持)がgo/docパッケージに移動しました。
    • Example構造体内のフィールド(Code, Comments)や、Examples関数、exampleOutput関数が引数として受け取る型(File, GenDecl, FuncDecl, CommentGroup)は、元々go/astパッケージ内で定義されていました。これらがgo/docパッケージに移動したことで、これらの型を参照する際にはast.Node, ast.CommentGroup, ast.File, ast.GenDecl, ast.FuncDeclのように明示的にastパッケージを修飾する必要が生じました。
  3. 依存関係の更新:
    • src/pkg/go/doc/example.go(旧src/pkg/go/ast/example.go)は、go/astパッケージの型を使用するため、新たにimport "go/ast"が追加されました。
    • src/cmd/go/test.gosrc/cmd/godoc/godoc.goは、Example関数を処理するためにgo/ast.Examplesではなくgo/doc.Examplesを使用するように変更されました。これに伴い、これらのファイルでもimport "go/doc"が追加されています。
    • src/cmd/dist/build.cでは、ビルド順序とクリーンアップ対象にpkg/go/docが追加されました。これは、go/docパッケージがGoのビルドシステムにおいて重要なコンポーネントとして認識され、適切にビルドおよび管理される必要があることを示しています。
  4. APIの変更:
    • ast.Examples関数は削除され、代わりにdoc.Examples関数が提供されるようになりました。この関数は、*ast.Fileのスライスを受け取り、*doc.Exampleのスライスを返します。
    • godocツール内のexample_htmlFunc関数やPageInfo構造体、getPageInfo関数など、Example情報を扱う部分の型シグネチャが*ast.Exampleから*doc.Exampleに変更されました。

この変更は、Goのツールチェイン内部のアーキテクチャを改善し、各コンポーネントの責務をより明確にするための重要なステップでした。

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

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

  1. src/pkg/go/{ast => doc}/example.go (ファイルのリネームと内容変更):

    • src/pkg/go/ast/example.goからsrc/pkg/go/doc/example.goへのファイル名変更。
    • パッケージ宣言がpackage astからpackage docへ変更。
    • import "go/ast"の追加。
    • Example構造体内のCodeCommentsフィールドの型がNode*CommentGroupからast.Node*ast.CommentGroupへ変更。
    • Examples関数の引数files ...*Filefiles ...*ast.Fileへ変更。
    • 関数内部で参照されるGenDeclFuncDeclCommentGroupなどの型がast.GenDeclast.FuncDeclast.CommentGroupへ変更。
  2. src/cmd/go/test.go:

    • import "go/doc"の追加。
    • ast.Examples(f)の呼び出しがdoc.Examples(f)へ変更。
  3. src/cmd/godoc/godoc.go:

    • example_htmlFunc関数の引数examples []*ast.Exampleexamples []*doc.Exampleへ変更。
    • PageInfo構造体のExamples []*ast.ExampleExamples []*doc.Exampleへ変更。
    • getPageInfo関数内でexamples []*ast.Exampleの宣言がexamples []*doc.Exampleへ変更。
    • ast.Examples(files...)の呼び出しがdoc.Examples(files...)へ変更。
  4. src/cmd/dist/build.c:

    • buildorder配列に"pkg/go/doc"を追加。
    • cleantab配列に"pkg/go/doc"を追加。

コアとなるコードの解説

src/pkg/go/{ast => doc}/example.go の変更

このファイルは、Example関数を解析し、その構造を表現するExample型と、ファイルからExampleを抽出するExamples関数を定義しています。

変更前 (package ast):

package ast

type Example struct {
	Name     string // name of the item being exemplified
	Doc      string // example function doc string
	Code     Node
	Comments []*CommentGroup
	Output   string // expected output
}

func Examples(files ...*File) []*Example { ... }

CodeCommentsの型、Examples関数の引数*Fileは、同じastパッケージ内で定義されている型を参照していました。

変更後 (package doc):

package doc

import (
	"go/ast" // astパッケージをインポート
	"go/token"
	"regexp"
	"sort"
)

type Example struct {
	Name     string // name of the item being exemplified
	Doc      string // example function doc string
	Code     ast.Node // ast.Nodeに明示的に変更
	Comments []*ast.CommentGroup // ast.CommentGroupに明示的に変更
	Output   string // expected output
}

func Examples(files ...*ast.File) []*Example { // 引数もast.Fileに明示的に変更
	var list []*Example
	for _, file := range files {
		// ...
		if g, ok := decl.(*ast.GenDecl); ok && g.Tok != token.IMPORT { // ast.GenDeclに明示的に変更
			// ...
		}
		f, ok := decl.(*ast.FuncDecl) // ast.FuncDeclに明示的に変更
		if !ok {
			continue
		}
		// ...
	}
	// ...
}

func exampleOutput(fun *ast.FuncDecl, comments []*ast.CommentGroup) string { // ast.FuncDecl, ast.CommentGroupに明示的に変更
	// ...
	var last *ast.CommentGroup // ast.CommentGroupに明示的に変更
	// ...
}

この変更により、Example構造体やExamples関数はgo/docパッケージの一部となりました。しかし、Example関数の解析には依然としてASTの構造(ast.Node, ast.File, ast.FuncDeclなど)が必要なため、go/astパッケージをインポートし、これらの型をast.で修飾して使用するようになりました。これは、Exampleの抽出ロジックがASTの知識に依存しつつも、そのロジック自体はドキュメンテーション生成の責務を持つgo/docに属するという、より適切な分離を示しています。

src/cmd/go/test.gosrc/cmd/godoc/godoc.go の変更

これらのファイルは、Example関数を実際に利用するツール(go testgodoc)のコードです。

変更前:

// src/cmd/go/test.go
import (
	// ...
	"go/ast"
	// ...
)
// ...
for _, e := range ast.Examples(f) { // astパッケージのExamples関数を呼び出し
	// ...
}

// src/cmd/godoc/godoc.go
import (
	// ...
	"go/ast"
	// ...
)
// ...
func example_htmlFunc(funcName string, examples []*ast.Example, fset *token.FileSet) string { // ast.Example型を使用
	// ...
}
// ...
examples = append(examples, ast.Examples(files...)...) // astパッケージのExamples関数を呼び出し

変更後:

// src/cmd/go/test.go
import (
	// ...
	"go/ast"
	"go/doc" // docパッケージをインポート
	// ...
)
// ...
for _, e := range doc.Examples(f) { // docパッケージのExamples関数を呼び出し
	// ...
}

// src/cmd/godoc/godoc.go
import (
	// ...
	"go/ast"
	"go/doc" // docパッケージをインポート
	// ...
)
// ...
func example_htmlFunc(funcName string, examples []*doc.Example, fset *token.FileSet) string { // doc.Example型を使用
	// ...
}
// ...
examples = append(examples, doc.Examples(files...)...) // docパッケージのExamples関数を呼び出し

これらの変更は、Exampleコードの抽出ロジックがgo/astからgo/docへ移動したことに伴う、API利用箇所の更新です。これにより、go testgodocは、Exampleに関する情報を取得する際に、より高レベルな抽象化を提供するgo/docパッケージを利用するようになりました。

src/cmd/dist/build.c の変更

このファイルはGoのビルドシステムの一部であり、Goの標準ライブラリパッケージのビルド順序やクリーンアップ対象を定義しています。

変更前: go/docは明示的にリストされていませんでした。

変更後:

static char *buildorder[] = {
	// ...
	"pkg/text/template",
	"pkg/go/doc", // 追加
	"cmd/go",
};

static char *cleantab[] = {
	// ...
	"pkg/go/build",
	"pkg/go/doc", // 追加
	"pkg/go/parser",
	// ...
};

pkg/go/docbuildordercleantabに追加されたことで、go/docパッケージがGoのビルドプロセスにおいて正式なビルド対象およびクリーンアップ対象として認識されるようになりました。これは、go/docがExampleコードの処理という重要な機能を担うようになったため、そのビルドと管理が適切に行われる必要があることを示しています。

関連リンク

参考にした情報源リンク

  • Go Issue #3048の議論内容
  • Go言語の公式ドキュメンテーション(go/astおよびgo/docパッケージ)
  • Go言語のソースコード(コミット前後の差分)
  • Go言語のExample関数に関する一般的な知識
  • godocツールの機能に関する知識
  • go testコマンドの機能に関する知識
  • Go言語のビルドシステムに関する一般的な知識
  • Go言語の歴史と進化に関する情報