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

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

このコミットは、Go言語の標準ライブラリの一部である go/doc パッケージの内部構造を整理し、クリーンアップすることを目的としています。go/doc パッケージは、Goのソースコードからドキュメンテーションを抽出し、go doc コマンドや godoc サーバーなどで利用されるドキュメントを生成するための基盤を提供します。

この変更の主な目的は、パッケージ内でエクスポートされるデータ構造の定義と、それらのデータ構造を構築するための実装ロジックを分離することです。これにより、コードの可読性、保守性、および理解しやすさが向上します。

コミット

  • コミットハッシュ: 22dfc77c99687c973b116f88e4295d88e6c11d7d
  • 作者: Robert Griesemer gri@golang.org
  • コミット日時: Mon Jan 9 16:14:01 2012 -0800

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

https://github.com/golang/go/commit/22dfc77c99687c973b116f88e4295d88e6c11d7d

元コミット内容

go/doc: first steps towards cleaning up go/doc

- separated exported data structures from doc reader
  by extracting all exported data structures into doc.go
  and moving the implementation into reader.go
- added missing documentation comments
- no API or semantic changes (but moved positions of
  PackageDoc.Doc and TypeDoc.Decl field up for consistency)
- runs all tests

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

変更の背景

go/doc パッケージは、Go言語のドキュメンテーションシステムの中核をなす重要なコンポーネントです。コミット当時の go/doc パッケージは、エクスポートされるドキュメンテーションデータ構造の定義と、それらのデータ構造を生成するための複雑なロジックが doc.go という単一のファイルに混在していました。

このような構造は、時間の経過とともにコードベースが成長するにつれて、以下の問題を引き起こす可能性があります。

  1. 可読性の低下: 多数の型定義、関数、およびロジックが1つのファイルに集中することで、コードの流れを追うのが困難になります。
  2. 保守性の低下: 特定の機能(例: ドキュメントの読み込みロジックの変更)を修正する際に、関連性の低い他の部分(例: データ構造の定義)に意図せず影響を与えてしまうリスクが高まります。
  3. 理解の困難さ: パッケージの公開APIと内部実装の区別が曖昧になり、新規開発者がパッケージの役割や使い方を理解するのに時間がかかります。

このコミットは、これらの問題を解決するための「最初の一歩」として、コードの責務分離 (Separation of Concerns) の原則を適用し、パッケージの内部構造を整理することを目的としています。具体的には、外部に公開されるデータ構造と、それらを処理する内部ロジックを物理的に異なるファイルに分割することで、パッケージのモジュール性を高め、将来的な拡張や変更を容易にすることを目指しました。

前提知識の解説

Go言語のgo/docパッケージ

go/doc パッケージは、Go言語のソースコードからドキュメンテーションコメントを抽出し、構造化された形式で表現するための標準ライブラリです。このパッケージは、go doc コマンドや godoc サーバーがGoのパッケージ、関数、型、変数などのドキュメントを表示するために利用しています。

go/doc パッケージは、Goのソースコードを抽象構文木 (AST) として解析し、そのASTを走査してドキュメンテーションコメントや宣言情報を収集します。収集された情報は、PackageDocTypeDoc、``FuncDoc`などの構造体に格納され、プログラム的にアクセス可能な形で提供されます。

AST (Abstract Syntax Tree)

AST(抽象構文木)は、ソースコードの構造を木構造で表現したものです。コンパイラやリンター、コード分析ツールなどがソースコードを理解し、処理するために利用します。Go言語では、go/ast パッケージがGoソースコードのASTを表現するための型と関数を提供しています。go/doc パッケージは、このASTを解析することで、コード内の要素(関数、型、変数など)とその関連するドキュメンテーションコメントを特定します。

コードの責務分離 (Separation of Concerns)

責務分離とは、ソフトウェア設計の原則の一つで、プログラムを異なる機能や関心事に基づいて独立したモジュールに分割することです。これにより、各モジュールが単一の明確な責任を持つようになり、コードの理解、テスト、保守が容易になります。

このコミットでは、go/doc パッケージにおいて、以下の2つの主要な責務を分離しています。

  1. ドキュメンテーションデータ構造の定義: 外部に公開されるドキュメンテーション情報の構造(例: PackageDocが持つべきフィールドなど)を定義する責務。
  2. ドキュメンテーション読み込みロジック: ソースコードのASTを解析し、ドキュメンテーション情報を抽出し、上記のデータ構造にマッピングする内部的な処理ロジックの責務。

これらの責務を異なるファイルに分離することで、それぞれの変更が互いに影響を与えにくくなり、パッケージ全体の健全性が保たれます。

技術的詳細

このコミットの技術的な核心は、src/pkg/go/doc/doc.go ファイルの肥大化を解消し、責務を明確に分離することにあります。

  1. doc.go から reader.go へのロジックの移動:

    • 既存の doc.go ファイルから、ドキュメンテーションを読み込み、ASTを走査して情報を収集する内部ロジック(例: docReader 構造体とそのメソッド、addValue, addFunc, addDecl, makeTypeDocs などのヘルパー関数)が、新しく作成された src/pkg/go/doc/reader.go ファイルに完全に移動されました。
    • これにより、doc.go は主に PackageDoc, ValueDoc, TypeDoc, FuncDoc といった、パッケージが外部に公開するドキュメンテーションデータ構造の定義のみを含むようになりました。
  2. 不足していたドキュメンテーションコメントの追加:

    • 移動されたコードや既存のコードに対して、不足していたドキュメンテーションコメントが追加されました。これにより、コードの意図や機能がより明確になり、将来的な開発者がコードを理解しやすくなります。
  3. APIおよびセマンティックな変更なし:

    • このコミットは、go/doc パッケージの外部APIや既存の動作に影響を与えません。これは、内部的なリファクタリングであり、パッケージの利用者にとっては透過的な変更であることを意味します。
    • ただし、PackageDoc.DocTypeDoc.Decl フィールドの順序が、構造体定義内で一貫性を保つために変更されました。これはAPIのセマンティクスには影響しませんが、構造体のメモリレイアウトや、リフレクションを使用する一部のツールに影響を与える可能性はゼロではありません(ただし、Goの標準ライブラリでは通常問題になりません)。
  4. ビルドスクリプトとMakefileの更新:

    • 新しい reader.go ファイルが導入されたため、Goのビルドシステムがこの新しいファイルを認識し、コンパイルプロセスに含める必要があります。
    • これに対応するため、src/pkg/go/doc/Makefilereader.goGOFILES 変数に追加されました。
    • また、Goのクロスコンパイルおよびビルドプロセスを制御する各プラットフォーム固有のビルドスクリプト (src/buildscript_*.sh) も更新され、go/doc パッケージのコンパイル時に reader.go が含まれるように変更されました。これにより、様々なOSやアーキテクチャで go/doc パッケージが正しくビルドされることが保証されます。
  5. テストの実行:

    • コミットメッセージに「runs all tests」と明記されているように、この変更が既存の機能に影響を与えないことを確認するために、すべてのテストが実行され、合格したことが示されています。これは、リファクタリングの安全性と品質を保証する上で非常に重要です。

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

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

  • src/pkg/go/doc/doc.go:

    • このファイルからは、docReader 構造体、addDoc, lookupTypeInfo, baseTypeName, addValue, setFunc, addFunc, addDecl, copyCommentList, bug_markers, bug_content, addFile, sortValueDoc, declName, makeValueDocs, FuncDoc, sortFuncDoc, makeFuncDocs, methodSet, TypeDoc, sortTypeDoc, makeTypeDocs, collectEmbeddedMethods, customizeRecv, makeBugDocs, newDoc といった、ドキュメンテーションの読み込みと処理に関する多くの内部ロジックが削除されました。
    • 残されたのは、PackageDoc, ValueDoc, TypeDoc, FuncDoc といった、外部に公開されるドキュメンテーションデータ構造の定義と、NewPackageDoc 関数(これは reader.go に移動された docReader を利用する)のみです。
  • src/pkg/go/doc/reader.go:

    • このファイルは新規作成されました。
    • doc.go から削除された、ドキュメンテーションの収集と処理に関するすべての内部ロジック(embeddedType, typeInfo, docReader 構造体、およびそれらの関連メソッドやヘルパー関数)がこのファイルに移動されました。
  • src/pkg/go/doc/Makefile:

    • GOFILES 変数に reader.go が追加されました。これにより、make コマンドで go/doc パッケージをビルドする際に reader.go がコンパイル対象に含まれるようになります。
    --- a/src/pkg/go/doc/Makefile
    +++ b/src/pkg/go/doc/Makefile
    @@ -11,6 +11,7 @@ GOFILES=\
     	example.go\
     	exports.go\
     	filter.go\
    +\treader.go\
    
     include ../../../Make.pkg
    
  • src/buildscript_*.sh (各プラットフォームのビルドスクリプト):

    • go/doc パッケージをコンパイルするコマンド (8g, 6g, 5g など) の引数に ./reader.go が追加されました。これにより、各プラットフォームでのビルド時に新しい reader.go ファイルが確実にコンパイルされるようになります。
    --- a/src/buildscript_darwin_386.sh
    +++ b/src/buildscript_darwin_386.sh
    @@ -460,7 +460,7 @@ cp "$WORK"/text/template.a "$GOROOT"/pkg/darwin_386/text/template.a
    
     mkdir -p "$WORK"/go/doc/_obj/
     cd "$GOROOT"/src/pkg/go/doc
    -8g -o "$WORK"/go/doc/_obj/_go_.8 -p go/doc -I "$WORK" ./comment.go ./doc.go ./example.go ./exports.go ./filter.go
    +8g -o "$WORK"/go/doc/_obj/_go_.8 -p go/doc -I "$WORK" ./comment.go ./doc.go ./example.go ./exports.go ./filter.go ./reader.go
     gopack grc "$WORK"/go/doc.a "$WORK"/go/doc/_obj/_go_.8
     cp "$WORK"/go/doc.a "$GOROOT"/pkg/darwin_386/go/doc.a
    

    (他のビルドスクリプトも同様の変更が適用されています。)

コアとなるコードの解説

このコミットによって、go/doc パッケージの内部構造は、データ構造の定義と、そのデータ構造を構築するロジックという2つの主要な部分に明確に分離されました。

doc.go に残された主要な構造体

doc.go ファイルには、go/doc パッケージが外部に公開するドキュメンテーション情報の構造が定義されています。これらは、go doc コマンドや godoc サーバーが利用する、Goのソースコードから抽出されたドキュメントの「モデル」となります。

  • PackageDoc:

    • パッケージ全体のドキュメンテーションを表す構造体です。
    • Doc (パッケージコメント)、PackageNameImportPathFilenames (パッケージを構成するファイル名)、Consts (定数)、Types (型)、Vars (変数)、Funcs (関数)、Bugs (BUGコメント) などのフィールドを持ちます。
    • このコミットでは、Doc フィールドと TypeDoc.Decl フィールドの順序が、構造体定義内で一貫性を保つために変更されました。
  • ValueDoc:

    • 定数 (const) または変数 (var) の宣言グループのドキュメンテーションを表します。
    • Doc (コメント) と Decl (元のASTノードである *ast.GenDecl) を持ちます。
  • TypeDoc:

    • 型 (type) 宣言のドキュメンテーションを表します。
    • Doc (コメント)、Type (元のASTノードである *ast.TypeSpec)、Decl (元のASTノードである *ast.GenDecl)、Consts (この型に関連する定数)、Vars (この型に関連する変数)、Factories (この型を返すファクトリ関数)、Methods (この型に属するメソッド) などのフィールドを持ちます。
  • FuncDoc:

    • 関数 (func) 宣言のドキュメンテーションを表します。
    • Doc (コメント)、Recv (レシーバ、メソッドの場合)、Name (関数名)、Decl (元のASTノードである *ast.FuncDecl) を持ちます。

reader.go に移動された主要なロジック

reader.go ファイルには、GoのソースコードのASTを解析し、上記の PackageDoc などの構造体を構築するための内部的なロジックがカプセル化されています。

  • docReader 構造体:

    • 単一のパッケージのドキュメンテーションを蓄積するための主要な内部構造体です。
    • パッケージコメント (doc)、パッケージ名 (pkgName)、収集された定数・変数 (values)、型情報 (types)、埋め込み型情報 (embedded)、関数情報 (funcs)、BUGコメント (bugs) などの内部状態を保持します。
    • この構造体は、ASTを走査し、関連するドキュメンテーションコメントや宣言情報を収集する役割を担います。
  • docReader の主要なメソッド:

    • init(pkgName string): docReader を初期化し、内部マップなどを準備します。
    • addDoc(comments *ast.CommentGroup): パッケージレベルのドキュメンテーションコメントを追加します。
    • lookupTypeInfo(name string): 指定された名前の型情報 (typeInfo) を検索または作成します。
    • baseTypeName(typ ast.Expr, allTypes bool): 型の基本名を抽出します。
    • addValue(decl *ast.GenDecl): 定数または変数の宣言 (*ast.GenDecl) を docReader に追加し、必要に応じて関連する型に紐付けます。
    • setFunc(table map[string]*ast.FuncDecl, f *ast.FuncDecl): 関数を内部テーブルに設定します。
    • addFunc(fun *ast.FuncDecl): 関数宣言 (*ast.FuncDecl) を docReader に追加し、それがメソッドであるか、ファクトリ関数であるか、通常の関数であるかを判断して適切に処理します。
    • addDecl(decl ast.Decl): ASTのトップレベル宣言 (ast.Decl) を処理し、それが定数、変数、型、関数のいずれであるかに応じて適切な add* メソッドを呼び出します。
    • addFile(src *ast.File): ソースファイルのAST (*ast.File) を docReader に追加し、パッケージコメント、宣言、BUGコメントを収集します。
    • makeValueDocs, makeFuncDocs, makeTypeDocs, makeBugDocs: 収集された内部情報を、外部に公開される ValueDoc, FuncDoc, TypeDoc, string (BUGコメント) のスライスに変換し、ソートするヘルパー関数群です。特に makeTypeDocs は、型の埋め込みフィールドからメソッドを収集する複雑なロジックを含んでいます。
    • collectEmbeddedMethods, customizeRecv: 埋め込み型からのメソッド収集と、レシーバのカスタマイズに関するロジックです。

NewPackageDoc 関数の役割

doc.go に残された NewPackageDoc 関数は、go/doc パッケージの主要なエントリポイントです。この関数は、go/ast パッケージによって解析された *ast.Package とインポートパスを受け取り、内部で docReader を利用してドキュメンテーション情報を収集し、最終的に PackageDoc 構造体を返します。

このコミットにより、NewPackageDoc 関数は、ドキュメンテーションの収集と処理の詳細を reader.go に委譲する形となり、doc.go はより高レベルなAPIインターフェースとしての役割を明確に果たすようになりました。

関連リンク

  • Gerrit Change-ID: https://golang.org/cl/5527063

参考にした情報源リンク

  • この解説は、提供されたコミット情報とGo言語の go/doc パッケージに関する一般的な知識に基づいて作成されました。追加の外部情報源は使用していません。