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

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

このコミットは、Go言語の公式ドキュメントツールである godoc の内部実装における重要なリファクタリングとバグ修正を含んでいます。具体的には、テンプレートに渡されるコンテキスト情報を *token.FileSet からより包括的な *PageInfo オブジェクトへと変更し、これにより将来的な機能拡張とより柔軟なフォーマットを可能にしています。

コミット

commit 12cf2ff00ba0a0816c1e572dae1476341f0bf3ed
Author: Robert Griesemer <gri@golang.org>
Date:   Tue Mar 26 18:28:16 2013 -0700

    godoc: pass *PageInfos instead of *token.FileSets in templates
    
    - convert all formatters that require a *token.FileSet to
      consistenly use a *PageInfo as first argument instead
    - adjust templates correspondingly
    - fix outstanding bug from previous CL 8005044
    
    Going forward, with this change the affected functions have
    access to the full page "context" (PageInfo), not just the
    respective file set. This will permit better context-dependent
    formatting in the future.
    
    R=golang-dev, bradfitz
    CC=golang-dev
    https://golang.org/cl/7860049

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

https://github.com/golang/go/commit/12cf2ff00ba0a0816c1e572dae1476341f0bf3ed

元コミット内容

godoc: pass *PageInfos instead of *token.FileSets in templates

このコミットは、godoc ツールがテンプレート内で使用するデータ構造を、*token.FileSet から *PageInfo へと変更することを目的としています。これにより、関連するフォーマッタ関数とテンプレートが調整され、以前の変更リスト (CL 8005044) から残っていたバグも修正されます。この変更により、関数は単なるファイルセットだけでなく、ページ全体の「コンテキスト」(PageInfo)にアクセスできるようになり、将来的にコンテキストに応じたより良いフォーマットが可能になります。

変更の背景

godoc はGo言語のソースコードからドキュメントを生成するツールです。以前のバージョンでは、ドキュメント生成の際に、コードの構文木(AST)のノードをフォーマットするために *token.FileSet(ファイルセット)が頻繁に引数として渡されていました。FileSet はソースコードのファイルと位置情報を管理するための重要な情報ですが、それだけではドキュメント生成に必要な全てのコンテキスト(例えば、コード例、パッケージ全体の情報、ASTのルートノードなど)をカバーできませんでした。

この制限により、特にコードスニペット内の識別子にリンクを張る際などに、十分なコンテキストがないために正確な処理ができないバグ(BUG(gri)としてコメントされていたもの)が存在していました。また、将来的に godoc のフォーマット機能を拡張する際にも、より豊富なコンテキスト情報が必要となることが予想されました。

このコミットは、これらの課題を解決し、godoc の内部構造をより堅牢で拡張性の高いものにするために行われました。

前提知識の解説

このコミットを理解するためには、以下のGo言語の標準ライブラリと概念に関する知識が必要です。

  • go/token パッケージ:
    • token.FileSet: Goのソースコードのファイルと、そのファイル内の位置情報(行番号、列番号、オフセットなど)を管理するための構造体です。コンパイラやツールがソースコードを解析する際に、コードのどの部分がどこにあるかを正確に特定するために使用されます。
  • go/ast パッケージ:
    • ast.Node: Goのソースコードを抽象構文木(AST)として表現する際の基本的なインターフェースです。ASTは、ソースコードの構造を木構造で表現したもので、コンパイラやコード分析ツールがコードの意味を理解するために利用します。
  • go/doc パッケージ:
    • doc.Example: Goのコード例(Example)に関する情報を持つ構造体です。godoc はこの情報を使用して、ドキュメントにコード例を埋め込みます。
  • Go言語のテンプレート (text/template, html/template):
    • Goのテンプレートエンジンは、データ構造をテンプレートに渡して、動的にテキストやHTMLを生成します。
    • . (ドット): 現在のデータコンテキストを参照します。
    • $ (ダラー): テンプレートのルートデータコンテキストを参照します。このコミットでは、*PageInfo がルートコンテキストとして渡されるようになるため、$*PageInfo を指すようになります。
  • ポインタ: Go言語におけるポインタは、変数のメモリアドレスを指す値です。関数にポインタを渡すことで、元の変数を変更したり、大きなデータ構造をコピーせずに効率的に渡したりすることができます。このコミットでは、PageInfo の値渡しからポインタ渡し (*PageInfo) へと変更されています。
  • godoc ツール: Go言語のパッケージやプログラムのドキュメントを生成し、表示するためのコマンドラインツールです。Goのソースコード内のコメントや構造から自動的にドキュメントを抽出します。

技術的詳細

このコミットの核心は、godoc の内部でドキュメント生成のコンテキストを管理する方法の変更です。

  1. *PageInfo の導入と一元化:

    • 以前は、nodeFuncexample_htmlFunc のようなフォーマッタ関数は、*token.FileSet[]*doc.Example のように、必要な情報を個別に引数として受け取っていました。
    • この変更により、これらの関数は *PageInfo という単一の構造体を最初の引数として受け取るようになります。PageInfo は、FileSetExamples、そしてパッケージのASTのルートノード (PAst) など、ドキュメント生成に必要な全てのコンテキスト情報をカプセル化しています。
    • これにより、関数のシグネチャが簡素化され、必要な情報へのアクセスが一元化されます。
  2. テンプレートの変更:

    • lib/godoc/package.html および lib/godoc/package.txt テンプレートでは、$.FSet のようにルートコンテキストから FileSet を直接参照していた箇所が、$(現在のコンテキスト、つまり *PageInfo)を引数として渡す形式に変更されています。例えば、{{node_html .Decl $.FSet}}{{node_html $ .Decl}} になります。これは、node_html 関数が *PageInfo を受け取るように変更されたことに対応しています。
  3. node_htmlFunc のバグ修正:

    • node_htmlFunc は、ASTノードをHTMLとしてフォーマットする関数です。以前は、*declLinks が有効な場合に、識別子にリンクを張る処理を行っていましたが、info.PAst != nil(つまり、完全なソースコードが表示されている場合)には、型チェックなしでは識別子の解決が不十分であるため、誤ったリンクが生成される可能性がありました。
    • このコミットでは、if n, _ := node.(ast.Node); n != nil && *declLinks && info.PAst == nil { という条件が追加されました。これにより、完全なソースコードが表示されている場合には識別子リンクが生成されないようになり、以前のバグが修正されました。この修正は、PageInfoPAst(パッケージのAST)情報を持つようになったことで可能になりました。
  4. getPageInfo の戻り値の変更:

    • src/cmd/godoc/godoc.go 内の getPageInfo 関数は、PageInfo の値ではなく、*PageInfo(ポインタ)を返すように変更されました。これにより、main.go などで getPageInfo の結果を受け取る際に、同じ PageInfo オブジェクトへの参照を共有できるようになり、メモリ効率と一貫性が向上します。

これらの変更は、godoc のコードベースをよりモジュール化し、将来の機能追加(例えば、より高度なクロスリファレンスやコード分析に基づくドキュメント生成)のための基盤を強化するものです。

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

主要な変更は以下のファイルに集中しています。

  • lib/godoc/package.html: HTMLテンプレート。node_htmlexample_htmlposLink_url などの関数呼び出しで、$.FSet の代わりに $ を引数として渡すように変更。
  • lib/godoc/package.txt: テキストテンプレート。HTMLテンプレートと同様の変更。
  • src/cmd/godoc/godoc.go: godoc の主要なロジックが含まれるファイル。
    • nodeFunc, node_htmlFunc, example_textFunc, example_htmlFunc, posLink_urlFunc の関数シグネチャが変更され、*token.FileSet[]*doc.Example の代わりに *PageInfo を受け取るようになった。
    • node_htmlFunc 内で、識別子リンク生成の条件に info.PAst == nil が追加された。
    • getPageInfo 関数の戻り値が PageInfo から *PageInfo に変更された。
  • src/cmd/godoc/main.go: godoc コマンドのエントリポイント。getPageInfo の戻り値の型変更に合わせて、PageInfo 型の変数が *PageInfo 型に変更された。

コアとなるコードの解説

src/cmd/godoc/godoc.go における変更例

// 変更前
func node_htmlFunc(node interface{}, fset *token.FileSet) string {
    var buf1 bytes.Buffer
    writeNode(&buf1, fset, node) // fset を直接使用

    var buf2 bytes.Buffer
    // BUG(gri): ...
    if n, _ := node.(ast.Node); n != nil && *declLinks {
        LinkifyText(&buf2, buf1.Bytes(), n)
    } else {
        FormatText(&buf2, buf1.Bytes(), -1, true, "", nil)
    }
    return buf2.String()
}

// 変更後
func node_htmlFunc(info *PageInfo, node interface{}) string {
    var buf1 bytes.Buffer
    writeNode(&buf1, info.FSet, node) // info.FSet を使用

    var buf2 bytes.Buffer
    // Don't linkify full source text (info.PAst != nil) - identifier
    // resolution is not strong enough without full type checking.
    if n, _ := node.(ast.Node); n != nil && *declLinks && info.PAst == nil { // info.PAst == nil が追加
        LinkifyText(&buf2, buf1.Bytes(), n)
    } else {
        FormatText(&buf2, buf1.Bytes(), -1, true, "", nil)
    }
    return buf2.String()
}

この変更では、node_htmlFunc*token.FileSet の代わりに *PageInfo を受け取るようになり、内部で info.FSet を使用してファイルセットにアクセスしています。さらに、info.PAst == nil という条件が追加され、PageInfo が持つAST情報に基づいて、完全なソースコードが表示されている場合には識別子リンクを生成しないように制御しています。これにより、以前のバグが修正され、より正確なドキュメント生成が可能になりました。

テンプレートにおける変更例

lib/godoc/package.html からの抜粋:

<!-- 変更前 -->
<dd><a href="#{{$name_html}}">{{node_html .Decl $.FSet}}</a></dd>

<!-- 変更後 -->
<dd><a href="#{{$name_html}}">{{node_html $ .Decl}}</a></dd>

この例では、node_html 関数に渡される引数が変更されています。以前は、現在のコンテキスト (.) の Decl フィールドと、ルートコンテキスト ($) の FSet フィールドを渡していました。変更後は、node_html 関数が *PageInfo を受け取るようになったため、ルートコンテキストである $(この場合は *PageInfo オブジェクト自体)と Decl フィールドを渡すように変更されています。これにより、node_html 関数は PageInfo オブジェクトから必要な FileSet やその他のコンテキスト情報を取得できるようになります。

関連リンク

参考にした情報源リンク