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

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

go/doc, godoc: move export filtering into go/doc

コミット

commit 198936f2b871669cefbeb26a6991fbfa3d934254
Author: Robert Griesemer <gri@golang.org>
Date:   Thu Dec 22 15:28:15 2011 -0800

    go/doc, godoc: move export filtering into go/doc
    
    - exports.go contains a stripped-down (but semantically unchanged)
      version of the code in go/ast/filter.go for export filtering
    - filter.go contains the documentation filtering code found before
      at the end of doc.go; this is simply a code move w/o any semantic
      changes
    - godoc now relies on go/doc for export filtering when creating
      documentation. It still has a separate form of export filtering
      for showing the source code version. This needs to be consolidated
      (perhaps the source form view should just be removed?).
    - Stripping of function bodies (stripFunctionBodies function of
      godoc.go) is now happening in doc.go (line 176).
    - doc.NewPackageDoc has an extra parameter "exportsOnly. If set
      to false, the behavior is as before. This function is only called
      once in our source code; a gofix module is probably not warranted.
    - Deleted doc.NewFileDoc - was never called.
    
    This change is mostly a code move w/ some minimal tweaks. It should
    not cause any changes to the behavior of godoc. It's a prerequisite
    for extracting anonymous embedded fields.
    
    R=golang-dev, r
    CC=golang-dev
    https://golang.org/cl/5502072

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

https://github.com/golang/go/commit/198936f2b871669cefbeb26a6991fbfa3d934254

元コミット内容

go/doc, godoc: move export filtering into go/doc

- exports.go contains a stripped-down (but semantically unchanged)
  version of the code in go/ast/filter.go for export filtering
- filter.go contains the documentation filtering code found before
  at the end of doc.go; this is simply a code move w/o any semantic
  changes
- godoc now relies on go/doc for export filtering when creating
  documentation. It still has a separate form of export filtering
  for showing the source code version. This needs to be consolidated
  (perhaps the source form view should just be removed?).
- Stripping of function bodies (stripFunctionBodies function of
  godoc.go) is now happening in doc.go (line 176).
- doc.NewPackageDoc has an extra parameter "exportsOnly. If set
  to false, the behavior is as before. This function is only called
  once in our source code; a gofix module is probably not warranted.
- Deleted doc.NewFileDoc - was never called.

This change is mostly a code move w/ some minimal tweaks. It should
not cause any changes to the behavior of godoc. It's a prerequisite
for extracting anonymous embedded fields.

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5502072

変更の背景

このコミットの主な目的は、Go言語のドキュメント生成ツールであるgodocと、その基盤となるgo/docパッケージにおける「エクスポートフィルタリング」のロジックを再編成し、より適切にモジュール化することにあります。

具体的には、以下の点が背景にあります。

  1. コードの責務の分離: 以前はgodocコマンド自身やgo/docパッケージ内のdoc.goファイルに分散していたエクスポートフィルタリングのロジックを、go/docパッケージ内の専用ファイル(exports.gofilter.go)に集約することで、コードの責務を明確にし、保守性を向上させる狙いがあります。
  2. 将来の機能拡張への対応: コミットメッセージに「It's a prerequisite for extracting anonymous embedded fields.(匿名埋め込みフィールドを抽出するための前提条件である)」と明記されているように、この変更はGo言語のAST(抽象構文木)解析における将来的な機能拡張、特に匿名埋め込みフィールドの適切な処理のための基盤整備として位置づけられています。匿名埋め込みフィールドはGoの重要な機能であり、ドキュメント生成やコード解析において正確に扱うためには、ASTのフィルタリングロジックの改善が必要でした。
  3. 重複コードの排除と一元化: go/ast/filter.goに存在していたエクスポートフィルタリングのコードが、go/doc/exports.goに「セマンティックには変更なしで、より簡潔な形」で移動されることで、関連するロジックが一元化され、重複が排除されます。

これらの背景により、godocの振る舞いを変更することなく、内部構造を改善し、将来の発展に備えるための重要なリファクタリングが行われました。

前提知識の解説

このコミットを理解するためには、以下のGo言語および関連ツールの概念を理解しておく必要があります。

  • Go言語のAST (Abstract Syntax Tree): Go言語のソースコードは、コンパイラやツールによって解析され、その構造が「抽象構文木(AST)」としてメモリ上に表現されます。ASTは、プログラムの構文構造を木構造で表現したもので、各ノードがコードの要素(変数宣言、関数呼び出し、式など)に対応します。go/astパッケージは、このASTを操作するためのGo標準ライブラリです。
  • go/ast パッケージ: Go言語のASTを扱うための標準ライブラリです。ソースコードのパース、ASTの走査、ASTの変更など、AST関連の様々な機能を提供します。このコミットでは、特にast.PackageExportsのようなASTフィルタリングに関連する機能が言及されています。
  • go/doc パッケージ: Go言語のソースコードからドキュメントを生成するための標準ライブラリです。Goのソースコードを解析し、パッケージ、関数、型、変数などのドキュメントコメントを抽出し、構造化されたドキュメントデータ(PackageDocなど)を生成する役割を担います。godocコマンドはこのパッケージを利用してドキュメントを生成します。
  • godoc コマンド: Go言語の公式ドキュメントツールです。Goのソースコードから自動的にドキュメントを生成し、コマンドラインやHTTPサーバーを通じて表示します。開発者がコードに記述したドキュメントコメント(///* */で始まるコメント)を解析し、APIリファレンスなどを生成します。
  • エクスポートフィルタリング (Export Filtering): Go言語では、識別子(変数名、関数名、型名など)が大文字で始まる場合、その識別子はパッケージ外からアクセス可能(エクスポートされている)と見なされます。エクスポートフィルタリングとは、ASTやドキュメントデータから、エクスポートされた識別子のみを抽出し、非エクスポートの識別子を除外する処理のことです。これは、公開APIのドキュメントを生成する際や、外部から利用可能な要素のみを解析する際に不可欠な機能です。
  • 匿名埋め込みフィールド (Anonymous Embedded Fields): Goの構造体(struct)では、フィールド名を指定せずに型を埋め込むことができます。これを匿名埋め込みフィールドと呼びます。これにより、埋め込まれた型のメソッドやフィールドを、その構造体のインスタンスから直接アクセスできるようになります。例えば、type MyStruct struct { io.Reader } と定義すると、MyStructのインスタンスはio.Readerインターフェースのメソッドを直接呼び出すことができます。この機能はコードの再利用性を高めますが、AST解析やドキュメント生成においては、その特殊な性質を正確に扱う必要があります。

これらの概念を理解することで、このコミットがGoのツールチェインの内部でどのように機能し、どのような改善をもたらしているのかを深く把握することができます。

技術的詳細

このコミットは、godocコマンドとgo/docパッケージにおけるエクスポートフィルタリングのロジックを、より構造化された形で再配置することに焦点を当てています。主な技術的変更点は以下の通りです。

  1. エクスポートフィルタリングロジックの分離と移動:

    • これまでgo/ast/filter.goに存在していたエクスポートフィルタリングのコアロジックの一部が、src/pkg/go/doc/exports.goという新しいファイルに移動されました。コミットメッセージによると、この移動は「セマンティックには変更なしで、より簡潔な形」で行われています。exports.goは、ASTノード(識別子、フィールドリスト、宣言など)を走査し、エクスポートされた要素のみを残すための関数群(filterIdentList, filterFieldList, filterType, filterSpec, filterDecl, fileExportsなど)を提供します。
    • src/pkg/go/doc/doc.goの末尾に存在していたドキュメントフィルタリング関連のコード(Filter型、matchFieldsmatchDeclfilterValueDocsfilterFuncDocsfilterTypeDocsPackageDoc.Filterメソッドなど)が、src/pkg/go/doc/filter.goという別の新しいファイルに移動されました。これは純粋なコードの移動であり、セマンティックな変更はありません。
  2. godocgo/doc間の依存関係の変更:

    • 以前はgodocコマンドが独自にエクスポートフィルタリングを行っていた部分がありましたが、この変更により、godocはドキュメント生成時にgo/docパッケージが提供するエクスポートフィルタリング機能に依存するようになりました。
    • src/cmd/godoc/godoc.goからstripFunctionBodies関数が削除され、その機能はsrc/pkg/go/doc/doc.go内のdocReader.addFuncメソッド(約176行目)に移動されました。これにより、関数本体の除去処理がドキュメント生成のより早い段階で行われるようになります。
  3. doc.NewPackageDoc関数の変更:

    • doc.NewPackageDoc関数のシグネチャに、新たにexportsOnly boolというブール型のパラメータが追加されました。このパラメータがtrueに設定された場合、生成されるパッケージドキュメントはエクスポートされた要素のみを含むようになります。falseの場合は、以前と同様の振る舞いをします。
    • コミットメッセージでは、この関数がソースコード内で一度しか呼び出されていないため、gofixモジュール(Goのコードを自動的に修正するツール)は不要であると述べられています。
  4. doc.NewFileDoc関数の削除:

    • src/pkg/go/doc/doc.goからNewFileDoc関数が削除されました。コミットメッセージによると、この関数は「一度も呼び出されていなかった」ため、不要なコードとして削除されました。
  5. 匿名埋め込みフィールド抽出の前提条件:

    • この変更は、GoのASTから匿名埋め込みフィールドを正確に抽出するための前提条件であると明記されています。これは、ASTのフィルタリングロジックがより洗練され、匿名埋め込みフィールドのような複雑な構造も適切に処理できるようになることを示唆しています。

これらの変更は、godocの外部的な振る舞いに影響を与えることなく、内部的なコード構造を改善し、将来の機能拡張(特に匿名埋め込みフィールドのサポート)のための強固な基盤を築くことを目的としています。コードの移動と再編成が主であり、セマンティックな変更は最小限に抑えられています。

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

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

  1. src/cmd/godoc/godoc.go:

    • stripFunctionBodies 関数が削除されました。この関数は、ASTから関数本体を削除する役割を担っていましたが、その機能はgo/docパッケージ内に移動されました。
    • getPageInfo 関数内でdoc.NewPackageDocを呼び出す箇所が変更され、新しいexportsOnlyパラメータが渡されるようになりました。これにより、godocgo/docのエクスポートフィルタリング機能を利用するようになります。
  2. src/pkg/go/doc/Makefile:

    • 新しく追加されたファイルであるexports.gofilter.goが、ビルド対象のGoファイルリストに追加されました。
  3. src/pkg/go/doc/doc.go:

    • stripFunctionBodies関数のロジックが、docReader.addFuncメソッド内にインライン化され、関数が追加される際にその本体がnilに設定されるようになりました。
    • NewFileDoc関数が完全に削除されました。
    • NewPackageDoc関数のシグネチャがfunc NewPackageDoc(pkg *ast.Package, importpath string, exportsOnly bool) *PackageDocに変更され、exportsOnlyパラメータが追加されました。
    • NewPackageDocの内部で、exportsOnlytrueの場合にr.fileExports(f)が呼び出されるようになりました。これは、パッケージ内の各ファイルに対してエクスポートフィルタリングを適用する新しいロジックです。
    • 以前doc.goの末尾にあったフィルタリング関連の関数群(Filter型、matchFieldsmatchDeclfilterValueDocsfilterFuncDocsfilterTypeDocsPackageDoc.Filterメソッド)が削除され、filter.goに移動されました。
  4. src/pkg/go/doc/exports.go (新規ファイル):

    • go/ast/filter.goから移動・簡略化されたエクスポートフィルタリングのロジックが実装されています。
    • filterIdentList, baseName, filterFieldList, filterParamList, filterType, filterSpec, filterSpecList, filterDecl, fileExportsといった関数が含まれており、これらがASTを走査してエクスポートされた要素のみを抽出する役割を担います。
  5. src/pkg/go/doc/filter.go (新規ファイル):

    • src/pkg/go/doc/doc.goから移動された、ドキュメント構造(ValueDoc, FuncDoc, TypeDocなど)に対するフィルタリングロジックが実装されています。
    • Filter型、matchFields, matchDecl, filterValueDocs, filterFuncDocs, filterTypeDocs, PackageDoc.Filterメソッドなどが含まれます。

これらの変更により、エクスポートフィルタリングの責務がgo/docパッケージ内に集約され、より明確な構造を持つようになりました。

コアとなるコードの解説

このコミットのコアとなる変更は、エクスポートフィルタリングのロジックがgo/docパッケージ内に集約され、godocがその機能を利用するように変更された点です。特に重要な変更箇所をいくつかピックアップして解説します。

1. doc.NewPackageDoc のシグネチャ変更と exportsOnly パラメータの導入

src/pkg/go/doc/doc.go における NewPackageDoc 関数の変更は、go/doc パッケージがエクスポートフィルタリングを直接制御するようになったことを示しています。

変更前 (概念):

func NewPackageDoc(pkg *ast.Package, importpath string) *PackageDoc {
    // ... 内部でフィルタリングロジックが適用されるか、
    // 呼び出し元 (godoc) がフィルタリングを行う
}

変更後:

func NewPackageDoc(pkg *ast.Package, importpath string, exportsOnly bool) *PackageDoc {
    var r docReader
    r.init(pkg.Name)
    filenames := make([]string, len(pkg.Files))
    i := 0
    for filename, f := range pkg.Files {
        if exportsOnly { // ★ここが新しいロジック
            r.fileExports(f) // ★新しく追加されたエクスポートフィルタリングの呼び出し
        }
        r.addFile(f)
        filenames[i] = filename
        i++
    }
    // ...
    return r.newDoc(importpath, filenames)
}

この変更により、NewPackageDoc の呼び出し元(主に godoc)は、exportsOnly パラメータを true に設定することで、エクスポートされた要素のみを含むドキュメントを生成するよう go/doc に指示できるようになりました。これにより、フィルタリングの責務が go/doc パッケージに一元化され、godoc 側での重複したフィルタリングロジックが不要になります。

2. stripFunctionBodies の移動

src/cmd/godoc/godoc.go から stripFunctionBodies 関数が削除され、その機能が src/pkg/go/doc/doc.go 内の docReader.addFunc メソッドに移動されました。

変更前 (src/cmd/godoc/godoc.go に存在):

func stripFunctionBodies(pkg *ast.Package) {
    for _, f := range pkg.Files {
        for _, d := range f.Decls {
            if f, ok := d.(*ast.FuncDecl); ok {
                f.Body = nil // 関数本体をnilにする
            }
        }
    }
}

変更後 (src/pkg/go/doc/doc.go 内の docReader.addFunc メソッド):

func (doc *docReader) addFunc(fun *ast.FuncDecl) {
    // strip function body
    fun.Body = nil // ★関数が追加される際に、ここで本体をnilにする
    // ...
}

この変更は、関数本体の除去という処理が、godoc の表示ロジックの一部ではなく、go/doc がドキュメントを生成する際のAST処理の一部として行われるべきであるという設計思想の変更を反映しています。これにより、ドキュメント生成パイプラインがよりクリーンになります。

3. exports.go におけるエクスポートフィルタリングのロジック

src/pkg/go/doc/exports.go は、GoのASTを走査し、エクスポートされた識別子のみを保持するための新しいファイルです。例えば、filterIdentList 関数は以下のように定義されています。

// src/pkg/go/doc/exports.go
func filterIdentList(list []*ast.Ident) []*ast.Ident {
    j := 0
    for _, x := range list {
        if ast.IsExported(x.Name) { // ★ast.IsExported を使用してエクスポートされているかチェック
            list[j] = x
            j++
        }
    }
    return list[0:j]
}

この関数は、ast.Ident(識別子)のリストを受け取り、ast.IsExported 関数(Goの識別子がエクスポートされているか、つまり大文字で始まるかを判定する)を使用して、エクスポートされた識別子のみを新しいリストにコピーして返します。同様のロジックが、フィールド、型、宣言など、様々なASTノードに対して適用される関数群として exports.go に実装されています。

これらの変更は、Goのドキュメント生成システムにおける内部的なクリーンアップとモジュール化を促進し、将来の機能拡張(特に匿名埋め込みフィールドの適切な処理)のための基盤を強化するものです。

関連リンク

参考にした情報源リンク

  • 特になし (Go言語の内部構造に関する一般的な知識に基づいています)