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

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

このコミットは、Go言語の標準ライブラリであるgo/docパッケージのAPIを合理化し、より使いやすく、将来の拡張性を持たせるための変更を導入しています。主な変更点としては、型名から冗長なDocサフィックスを削除し、エクスポートされた型にNameフィールドを追加し、メソッドに関する情報を拡充し、パッケージの動作を制御するためのブール値の代わりにModeフィールドを導入しています。これにより、go/docパッケージを利用するツール(例: godoc)が、よりクリーンで一貫性のあるAPIを通じてドキュメント情報を取得できるようになります。

コミット

commit eac31c67a8fd94b9e91043a40b3a3cb1bf6daa4f
Author: Robert Griesemer <gri@golang.org>
Date:   Thu Jan 12 17:36:57 2012 -0800

    go/doc: streamlined go/doc API
    
    - the main changes are removing the Doc suffix
      from the exported types, so instead of
      doc.TypeDoc one will have doc.Type, etc.
    
    - All exported types now have a Name (or Names) field.
      For Values, the Names field lists all declared variables
      or constants.
    
    - Methods have additional information about where they are
      coming from.
    
    - There's a mode field instead of a bool to
      control the package's operation, which makes
      it easier to extend w/o API changes.
    
    Except for the partially implemented new Method type,
    this is based on existing code. A clean rewrite is in
    progress based on this new API.
    
    R=rsc, kevlar
    CC=golang-dev
    https://golang.org/cl/5528060

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

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

元コミット内容

go/doc: streamlined go/doc API

- the main changes are removing the Doc suffix
  from the exported types, so instead of
  doc.TypeDoc one will have doc.Type, etc.

- All exported types now have a Name (or Names) field.
  For Values, the Names field lists all declared variables
  or constants.

- Methods have additional information about where they are
  coming from.

- There's a mode field instead of a bool to
  control the package's operation, which makes
  it easier to extend w/o API changes.

Except for the partially implemented new Method type,
this is based on existing code. A clean rewrite is in
progress based on this new API.

R=rsc, kevlar
CC=golang-dev
https://golang.org/cl/5528060

変更の背景

このコミットの背景には、Go言語のドキュメンテーションツールであるgodocの基盤となるgo/docパッケージのAPIを改善し、より直感的で拡張性の高いものにするという目的があります。

従来のgo/docパッケージのAPIは、型名にDocというサフィックス(例: TypeDoc, ValueDoc, FuncDoc, PackageDoc)が付与されており、これは冗長であり、Goの慣習的な命名規則(例えば、io.Readerではなくio.ReaderDocとはしない)とは異なっていました。このコミットでは、この冗長なサフィックスを削除し、Type, Value, Func, Packageといったより簡潔な名前に変更することで、APIの利用を簡素化しています。

また、ドキュメント生成の柔軟性を高めるため、パッケージの動作を制御するブール値のフラグ(exportsOnlyなど)を、より拡張性の高いMode型に置き換えています。これにより、将来的にドキュメント抽出のオプションが増えた場合でも、APIの変更なしに対応できるようになります。

さらに、ドキュメントの品質と詳細度を向上させるため、エクスポートされたすべての型にNameフィールドを追加し、Value型には関連する変数や定数の名前をリストするNamesフィールドを導入しています。これにより、ドキュメント生成時にこれらの情報をより容易に利用できるようになります。メソッドに関しても、その起源に関する追加情報を提供することで、より詳細なドキュメント生成を可能にしています。

これらの変更は、godocツールが生成するドキュメントの品質と、go/docパッケージの保守性および拡張性を向上させることを目的としています。コミットメッセージにある「A clean rewrite is in progress based on this new API.」という記述からも、このAPI変更が将来的な大規模なリファクタリングの基盤となることが示唆されています。

前提知識の解説

このコミットを理解するためには、以下のGo言語および関連ツールの基本的な知識が必要です。

  1. Go言語のパッケージとドキュメンテーション:

    • パッケージ: Go言語のコードはパッケージにまとめられます。パッケージは関連する機能の集合であり、再利用可能なコードの単位です。
    • エクスポートされた識別子: Goでは、識別子(変数、関数、型など)の名前が大文字で始まる場合、その識別子はパッケージ外にエクスポートされ、他のパッケージから利用可能になります。小文字で始まる場合はパッケージ内でのみ利用可能です。
    • Goのドキュメンテーションコメント: Goでは、エクスポートされた識別子の直前に書かれたコメントがその識別子のドキュメンテーションとして扱われます。godocツールはこのコメントを解析してドキュメントを生成します。
  2. go/astパッケージ:

    • go/astパッケージは、Goのソースコードを抽象構文木(AST: Abstract Syntax Tree)として表現するための型と関数を提供します。コンパイラやコード分析ツールは、このASTを操作してコードを理解・変換します。
    • ast.Package: 複数のGoソースファイルから構成されるパッケージ全体のAST表現です。
    • ast.File: 単一のGoソースファイルのAST表現です。
    • ast.GenDecl: var, const, type宣言を表すASTノードです。
    • ast.FuncDecl: 関数宣言を表すASTノードです。
    • ast.TypeSpec: 型宣言(type MyType struct {}など)を表すASTノードです。
  3. go/tokenパッケージ:

    • go/tokenパッケージは、Goのソースコード内のトークン(キーワード、識別子、演算子など)と、それらのソースコード上の位置(行番号、列番号など)を扱うための型と定数を提供します。
    • token.FileSet: 複数のソースファイルをまとめて管理し、ファイル内の位置情報を効率的に扱うための構造体です。
  4. go/docパッケージ:

    • go/docパッケージは、go/astパッケージが生成したASTからドキュメンテーションコメントを抽出し、構造化されたドキュメント情報を生成するためのライブラリです。godocツールはこのパッケージを利用してHTMLやプレーンテキスト形式のドキュメントを生成します。
    • このコミットの変更の中心となるパッケージです。
  5. godocツール:

    • Go言語の標準ドキュメンテーションツールです。ソースコードから自動的にドキュメントを生成し、Webサーバーとして提供したり、コマンドラインで表示したりできます。

これらの知識があることで、コミットがgo/docパッケージの内部構造と、それがgodocのようなツールにどのように影響するかをより深く理解できます。

技術的詳細

このコミットは、go/docパッケージの内部構造と外部APIにわたる複数の重要な変更を含んでいます。

  1. 型名の変更とAPIの簡素化:

    • src/pkg/go/doc/doc.goにおいて、PackageDoc, ValueDoc, TypeDoc, FuncDocといった型名からDocサフィックスが削除され、それぞれPackage, Value, Type, Funcに変更されました。
    • これにより、APIがよりGoの慣習に沿ったものになり、利用時の冗長性が排除されます。例えば、doc.TypeDocの代わりにdoc.Typeを使用するようになります。
    • この変更に伴い、src/cmd/godoc/godoc.golib/godoc/package.html, lib/godoc/package.txtなど、これらの型を参照している箇所がすべて更新されています。
  2. NameおよびNamesフィールドの追加:

    • Package型にはNameフィールドが追加され、パッケージ名が直接アクセスできるようになりました(以前はPackageName)。
    • Type型にはNameフィールドが追加され、型名が直接アクセスできるようになりました。
    • Value型にはNames []stringフィールドが追加されました。これは、varconst宣言で複数の識別子が同時に宣言されている場合に、それらの名前をリストするために使用されます。これにより、ドキュメント生成時にグループ化された宣言内の個々の名前を正確に表示できるようになります。
    • この変更は、src/pkg/go/doc/reader.gomakeValues関数でspecNamesヘルパー関数を導入し、Value構造体のNamesフィールドを埋めることで実現されています。
  3. Method型の導入と情報拡充:

    • src/pkg/go/doc/doc.goに新しいMethod型が導入されました。これは既存のFunc型を埋め込み、さらにRecv(元のレシーバの基底型)とLevel(埋め込みレベル)といったメソッド固有の情報を保持できるように設計されています。
    • コミットメッセージには「partially implemented new Method type」とあり、RecvLevelフィールドはまだ完全に設定されていないことが示唆されています。しかし、この構造の導入により、将来的にメソッドのドキュメントをより詳細に、例えばそれがどの型から埋め込まれたものかといった情報を含めて生成する基盤が作られました。
    • Type型内のMethodsフィールドが[]*FuncDocから[]*Methodに変更されています。
    • src/pkg/go/doc/reader.gomakeTypeDocs(変更後はmakeTypes)関数内で、TypeMethodsフィールドを構築する際に、FuncMethod型にラップする処理が追加されています。
  4. Modeフィールドの導入(ブール値から列挙型へ):

    • src/pkg/go/doc/doc.goにおいて、NewPackageDoc関数がexportsOnly bool引数を受け取っていたのに対し、新しいNew関数はmode Mode引数を受け取るようになりました。
    • Modeint型の新しい型として定義され、AllDecls Mode = 1 << iotaという定数が導入されています。これにより、ドキュメント抽出の動作を制御するためのフラグをビットマスクとして表現できるようになり、将来的に新しいオプションを追加する際にAPIを変更する必要がなくなります。
    • src/pkg/go/doc/reader.godocReader構造体もexportsOnly boolからmode Modeに変更され、初期化関数initもこれに合わせて更新されています。
    • src/cmd/godoc/godoc.goでは、getPageInfo関数内でdoc.NewPackageDocの呼び出しがdoc.Newに置き換えられ、exportsOnlyのロジックがdocModeの計算に変換されています。
  5. ファイル処理順序のソート:

    • src/pkg/go/doc/doc.goNew関数内で、パッケージ内のファイルを処理する前にファイル名をソートするsort.Strings(filenames)が追加されました。これにより、異なる環境(32bit/64bitなど)でドキュメントを生成した際に、結果が常に一貫するようになります。

これらの変更は、go/docパッケージのAPIをより現代的で、Goの設計思想に合致させ、将来の拡張に備えるための重要なステップです。

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

このコミットにおける主要なコード変更は、src/pkg/go/doc/doc.gosrc/pkg/go/doc/reader.gosrc/pkg/go/doc/filter.go、そしてsrc/cmd/godoc/godoc.goに集中しています。

src/pkg/go/doc/doc.go

--- a/src/pkg/go/doc/doc.go
+++ b/src/pkg/go/doc/doc.go
@@ -5,67 +5,96 @@
 // Package doc extracts source code documentation from a Go AST.
 package doc
 
-import "go/ast"
+import (
+	"go/ast"
+	"sort"
+)
 
-// PackageDoc is the documentation for an entire package.
-type PackageDoc struct {
-	Doc         string
-	PackageName string
-	ImportPath  string
-	Filenames   []string
-	Consts      []*ValueDoc
-	Types       []*TypeDoc
-	Vars        []*ValueDoc
-	Funcs       []*FuncDoc
-	Bugs        []string
+// Package is the documentation for an entire package.
+type Package struct {
+	Doc        string
+	Name       string
+	ImportPath string
+	Imports    []string // TODO(gri) this field is not computed at the moment
+	Filenames  []string
+	Consts     []*Value
+	Types      []*Type
+	Vars       []*Value
+	Funcs      []*Func
+	Bugs       []string
 }
 
 // Value is the documentation for a (possibly grouped) var or const declaration.
-type ValueDoc struct {
-	Doc  string
-	Decl *ast.GenDecl
+type Value struct {
+	Doc   string
+	Names []string // var or const names in declaration order
+	Decl  *ast.GenDecl
 
 	order int
 }
 
-// TypeDoc is the documentation for type declaration.
-type TypeDoc struct {
-	Doc       string
-	Type      *ast.TypeSpec
-	Decl      *ast.GenDecl
-	Consts    []*ValueDoc // sorted list of constants of (mostly) this type
-	Vars      []*ValueDoc // sorted list of variables of (mostly) this type
-	Factories []*FuncDoc  // sorted list of functions returning this type
-	Methods   []*FuncDoc  // sorted list of methods (including embedded ones) of this type
+type Method struct {
+	*Func
+	// TODO(gri) The following fields are not set at the moment. 
+	Recv  *Type // original receiver base type
+	Level int   // embedding level; 0 means Func is not embedded
+}
+
+// Type is the documentation for type declaration.
+type Type struct {
+	Doc     string
+	Name    string
+	Type    *ast.TypeSpec
+	Decl    *ast.GenDecl
+	Consts  []*Value  // sorted list of constants of (mostly) this type
+	Vars    []*Value  // sorted list of variables of (mostly) this type
+	Funcs   []*Func   // sorted list of functions returning this type
+	Methods []*Method // sorted list of methods (including embedded ones) of this type
 
-\tmethods  []*FuncDoc // top-level methods only
-\tembedded methodSet  // embedded methods only
+\tmethods  []*Func   // top-level methods only
+\tembedded methodSet // embedded methods only
 \torder    int
 }
 
 // Func is the documentation for a func declaration.
-type FuncDoc struct {\n \tDoc  string\n-\tRecv ast.Expr // TODO(rsc): Would like string here\n+type Func struct {\n \tDoc  string\n+\t// TODO(gri) remove Recv once we switch to new implementation\n+\tRecv ast.Expr // TODO(rsc): Would like string here\n \tName string\n \tDecl *ast.FuncDecl\n }\n \n-// NewPackageDoc computes the package documentation for the given package\n-// and import path. If exportsOnly is set, only exported objects are\n-// included in the documentation.\n-func NewPackageDoc(pkg *ast.Package, importpath string, exportsOnly bool) *PackageDoc {\n+// Mode values control the operation of New.\n+type Mode int\n+\n+const (\n+\t// extract documentation for all package-level declarations,\n+\t// not just exported ones\n+\tAllDecls Mode = 1 << iota\n+)\n+\n+// New computes the package documentation for the given package.\n+func New(pkg *ast.Package, importpath string, mode Mode) *Package {\n \tvar r docReader\n-\tr.init(pkg.Name, exportsOnly)\n+\tr.init(pkg.Name, mode)\n \tfilenames := make([]string, len(pkg.Files))\n+\t// sort package files before reading them so that the\n+\t// result is the same on different machines (32/64bit)\n \ti := 0\n-\tfor filename, f := range pkg.Files {\n-\t\tif exportsOnly {\n+\tfor filename := range pkg.Files {\n+\t\tfilenames[i] = filename\n+\t\ti++\n+\t}\n+\tsort.Strings(filenames)\n+\n+\t// process files in sorted order\n+\tfor _, filename := range filenames {\n+\t\tf := pkg.Files[filename]\n+\t\tif mode&AllDecls == 0 {\n \t\t\tr.fileExports(f)\n \t\t}\n \t\tr.addFile(f)\n-\t\tfilenames[i] = filename\n-\t\ti++\n \t}\n \treturn r.newDoc(importpath, filenames)\n }\n```

### `src/cmd/godoc/godoc.go`

```diff
--- a/src/cmd/godoc/godoc.go
+++ b/src/cmd/godoc/godoc.go
@@ -917,17 +917,17 @@ func remoteSearchURL(query string, html bool) string {\n }\n \n type PageInfo struct {\n-\tDirname  string          // directory containing the package\n-\tPList    []string        // list of package names found\n-\tFSet     *token.FileSet  // corresponding file set\n-\tPAst     *ast.File       // nil if no single AST with package exports\n-\tPDoc     *doc.PackageDoc // nil if no single package documentation\n-\tExamples []*doc.Example  // nil if no example code\n-\tDirs     *DirList        // nil if no directory information\n-\tDirTime  time.Time       // directory time stamp\n-\tDirFlat  bool            // if set, show directory in a flat (non-indented) manner\n-\tIsPkg    bool            // false if this is not documenting a real package\n-\tErr      error           // I/O error or nil\n+\tDirname  string         // directory containing the package\n+\tPList    []string       // list of package names found\n+\tFSet     *token.FileSet // corresponding file set\n+\tPAst     *ast.File      // nil if no single AST with package exports\n+\tPDoc     *doc.Package   // nil if no single package documentation\n+\tExamples []*doc.Example // nil if no example code\n+\tDirs     *DirList       // nil if no directory information\n+\tDirTime  time.Time      // directory time stamp\n+\tDirFlat  bool           // if set, show directory in a flat (non-indented) manner\n+\tIsPkg    bool           // false if this is not documenting a real package\n+\tErr      error          // I/O error or nil\n }\n \n func (info *PageInfo) IsEmpty() bool {\
@@ -1084,17 +1084,20 @@ func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, mode PageInf\n \n \t// compute package documentation\n \tvar past *ast.File\n-\tvar pdoc *doc.PackageDoc\n+\tvar pdoc *doc.Package\n \tif pkg != nil {\n-\t\texportsOnly := mode&noFiltering == 0\n+\t\tvar docMode doc.Mode\n+\t\tif mode&noFiltering != 0 {\n+\t\t\tdocMode = doc.AllDecls\n+\t\t}\n \t\tif mode&showSource == 0 {\n \t\t\t// show extracted documentation\n-\t\t\tpdoc = doc.NewPackageDoc(pkg, path.Clean(relpath), exportsOnly) // no trailing \'/\' in importpath\n+\t\t\tpdoc = doc.New(pkg, path.Clean(relpath), docMode) // no trailing \'/\' in importpath\n \t\t} else {\n \t\t\t// show source code\n \t\t\t// TODO(gri) Consider eliminating export filtering in this mode,\n \t\t\t//           or perhaps eliminating the mode altogether.\n-\t\t\tif exportsOnly {\n+\t\t\tif docMode&doc.AllDecls == 0 {\n \t\t\t\tast.PackageExports(pkg)\n \t\t\t}\n \t\t\tpast = ast.MergePackageFiles(pkg, ast.FilterUnassociatedComments)\
@@ -1189,13 +1192,13 @@ func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n \tcase info.PDoc != nil:\n \t\tswitch {\n \t\tcase info.IsPkg:\n-\t\t\ttitle = \"Package \" + info.PDoc.PackageName\n-\t\tcase info.PDoc.PackageName == fakePkgName:\n+\t\t\ttitle = \"Package \" + info.PDoc.Name\n+\t\tcase info.PDoc.Name == fakePkgName:\n \t\t\t// assume that the directory name is the command name\n \t\t\t_, pkgname := path.Split(relpath)\n \t\t\ttitle = \"Command \" + pkgname\n \t\tdefault:\n-\t\t\ttitle = \"Command \" + info.PDoc.PackageName\n+\t\t\ttitle = \"Command \" + info.PDoc.Name\n \t\t}\n \tdefault:\n \t\ttitle = \"Directory \" + relativeURL(info.Dirname)\

src/pkg/go/doc/reader.go

--- a/src/pkg/go/doc/reader.go
+++ b/src/pkg/go/doc/reader.go
@@ -53,19 +53,19 @@ func (info *typeInfo) addEmbeddedType(embedded *typeInfo, isPtr bool) {\n // printing the corresponding AST node).\n //\n type docReader struct {\n-\tdoc         *ast.CommentGroup // package documentation, if any\n-\tpkgName     string\n-\texportsOnly bool\n-\tvalues      []*ast.GenDecl // consts and vars\n-\ttypes       map[string]*typeInfo\n-\tembedded    map[string]*typeInfo // embedded types, possibly not exported\n-\tfuncs       map[string]*ast.FuncDecl\n-\tbugs        []*ast.CommentGroup\n+\tdoc      *ast.CommentGroup // package documentation, if any\n+\tpkgName  string\n+\tmode     Mode\n+\tvalues   []*ast.GenDecl // consts and vars\n+\ttypes    map[string]*typeInfo\n+\tembedded map[string]*typeInfo // embedded types, possibly not exported\n+\tfuncs    map[string]*ast.FuncDecl\n+\tbugs     []*ast.CommentGroup\n }\n \n-func (doc *docReader) init(pkgName string, exportsOnly bool) {\n+func (doc *docReader) init(pkgName string, mode Mode) {\n \tdoc.pkgName = pkgName\n-\tdoc.exportsOnly = exportsOnly\n+\tdoc.mode = mode\n \tdoc.types = make(map[string]*typeInfo)\n \tdoc.embedded = make(map[string]*typeInfo)\n \tdoc.funcs = make(map[string]*ast.FuncDecl)\
@@ -347,10 +347,10 @@ func (doc *docReader) addFile(src *ast.File) {\n // ----------------------------------------------------------------------------\n // Conversion to external representation\n \n-type sortValueDoc []*ValueDoc\n+type sortValue []*Value\n \n-func (p sortValueDoc) Len() int      { return len(p) }\n-func (p sortValueDoc) Swap(i, j int) { p[i], p[j] = p[j], p[i] }\n+func (p sortValue) Len() int      { return len(p) }\n+func (p sortValue) Swap(i, j int) { p[i], p[j] = p[j], p[i] }\n \n func declName(d *ast.GenDecl) string {\n \tif len(d.Specs) != 1 {\
@@ -367,7 +367,7 @@ func declName(d *ast.GenDecl) string {\n \treturn \"\"\n }\n \n-func (p sortValueDoc) Less(i, j int) bool {\n+func (p sortValue) Less(i, j int) bool {\n \t// sort by name\n \t// pull blocks (name = \"\") up to top\n \t// in original order\
@@ -377,32 +377,45 @@ func (p sortValueDoc) Less(i, j int) bool {\n \treturn p[i].order < p[j].order\n }\n \n-func makeValueDocs(list []*ast.GenDecl, tok token.Token) []*ValueDoc {\n-\td := make([]*ValueDoc, len(list)) // big enough in any case\n+func specNames(specs []ast.Spec) []string {\n+\tnames := make([]string, len(specs)) // reasonable estimate\n+\tfor _, s := range specs {\n+\t\t// should always be an *ast.ValueSpec, but be careful\n+\t\tif s, ok := s.(*ast.ValueSpec); ok {\n+\t\t\tfor _, ident := range s.Names {\n+\t\t\t\tnames = append(names, ident.Name)\n+\t\t\t}\n+\t\t}\n+\t}\n+\treturn names\n+}\n+\n+func makeValues(list []*ast.GenDecl, tok token.Token) []*Value {\n+\td := make([]*Value, len(list)) // big enough in any case\n \tn := 0\n \tfor i, decl := range list {\n \t\tif decl.Tok == tok {\n-\t\t\td[n] = &ValueDoc{decl.Doc.Text(), decl, i}\n+\t\t\td[n] = &Value{decl.Doc.Text(), specNames(decl.Specs), decl, i}\n \t\t\tn++\n \t\t\tdecl.Doc = nil // doc consumed - removed from AST\n \t\t}\n \t}\n \td = d[0:n]\n-\tsort.Sort(sortValueDoc(d))\n+\tsort.Sort(sortValue(d))\n \treturn d\n }\n \n-type sortFuncDoc []*FuncDoc\n+type sortFunc []*Func\n \n-func (p sortFuncDoc) Len() int           { return len(p) }\n-func (p sortFuncDoc) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }\n-func (p sortFuncDoc) Less(i, j int) bool { return p[i].Name < p[j].Name }\n+func (p sortFunc) Len() int           { return len(p) }\n+func (p sortFunc) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }\n+func (p sortFunc) Less(i, j int) bool { return p[i].Name < p[j].Name }\n \n-func makeFuncDocs(m map[string]*ast.FuncDecl) []*FuncDoc {\n-\td := make([]*FuncDoc, len(m))\n+func makeFuncs(m map[string]*ast.FuncDecl) []*Func {\n+\td := make([]*Func, len(m))\n \ti := 0\n \tfor _, f := range m {\n-\t\tdoc := new(FuncDoc)\n+\t\tdoc := new(Func)\n \t\tdoc.Doc = f.Doc.Text()\n \t\tf.Doc = nil // doc consumed - remove from ast.FuncDecl node\n \t\tif f.Recv != nil {\
@@ -413,34 +426,40 @@ func makeFuncDocs(m map[string]*ast.FuncDecl) []*FuncDoc {\n \t\td[i] = doc\n \t\ti++\n \t}\n-\tsort.Sort(sortFuncDoc(d))\n+\tsort.Sort(sortFunc(d))\n \treturn d\n }\n \n-type methodSet map[string]*FuncDoc\n+type methodSet map[string]*Func\n \n-func (mset methodSet) add(m *FuncDoc) {\n+func (mset methodSet) add(m *Func) {\n \tif mset[m.Name] == nil {\n \t\tmset[m.Name] = m\n \t}\n }\n \n-func (mset methodSet) sortedList() []*FuncDoc {\n-\tlist := make([]*FuncDoc, len(mset))\n+type sortMethod []*Method\n+\n+func (p sortMethod) Len() int           { return len(p) }\n+func (p sortMethod) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }\n+func (p sortMethod) Less(i, j int) bool { return p[i].Func.Name < p[j].Func.Name }\n+\n+func (mset methodSet) sortedList() []*Method {\n+\tlist := make([]*Method, len(mset))\n \ti := 0\n \tfor _, m := range mset {\n-\t\tlist[i] = m\n+\t\tlist[i] = &Method{Func: m}\n \t\ti++\n \t}\n-\tsort.Sort(sortFuncDoc(list))\n+\tsort.Sort(sortMethod(list))\n \treturn list\n }\n \n-type sortTypeDoc []*TypeDoc\n+type sortType []*Type\n \n-func (p sortTypeDoc) Len() int      { return len(p) }\n-func (p sortTypeDoc) Swap(i, j int) { p[i], p[j] = p[j], p[i] }\n-func (p sortTypeDoc) Less(i, j int) bool {\n+func (p sortType) Len() int      { return len(p) }\n+func (p sortType) Swap(i, j int) { p[i], p[j] = p[j], p[i] }\n+func (p sortType) Less(i, j int) bool {\n \t// sort by name\n \t// pull blocks (name = \"\") up to top\n \t// in original order\
@@ -453,14 +472,14 @@ func (p sortTypeDoc) Less(i, j int) bool {\n // NOTE(rsc): This would appear not to be correct for type ( )\n // blocks, but the doc extractor above has split them into\n // individual declarations.\n-func (doc *docReader) makeTypeDocs(m map[string]*typeInfo) []*TypeDoc {\n+func (doc *docReader) makeTypes(m map[string]*typeInfo) []*Type {\n \t// TODO(gri) Consider computing the embedded method information\n-\t//           before calling makeTypeDocs. Then this function can\n+\t//           before calling makeTypes. Then this function can\n \t//           be single-phased again. Also, it might simplify some\n \t//           of the logic.\n \t//\n-\t// phase 1: associate collected declarations with TypeDocs\n-\tlist := make([]*TypeDoc, len(m))\n+\t// phase 1: associate collected declarations with Types\n+\tlist := make([]*Type, len(m))\n \ti := 0\n \tfor _, old := old := range m {\n \t\t// old typeInfos may not have a declaration associated with them\
@@ -469,7 +488,7 @@ func (doc *docReader) makeTypeDocs(m map[string]*typeInfo) []*TypeDoc {\n \t\tif decl := old.decl; decl != nil || !old.exported() {\n \t\t\t// process the type even if not exported so that we have\n \t\t\t// its methods in case they are embedded somewhere\n-\t\t\tt := new(TypeDoc)\n+\t\t\tt := new(Type)\n \t\t\tif decl != nil {\n \t\t\t\ttypespec := decl.Specs[0].(*ast.TypeSpec)\n \t\t\t\tdoc := typespec.Doc\
@@ -482,10 +501,10 @@ func (doc *docReader) makeTypeDocs(m map[string]*typeInfo) []*TypeDoc {\n \t\t\t\tt.Doc = doc.Text()\n \t\t\t\tt.Type = typespec\n \t\t\t}\n-\t\t\tt.Consts = makeValueDocs(old.values, token.CONST)\n-\t\t\tt.Vars = makeValueDocs(old.values, token.VAR)\n-\t\t\tt.Factories = makeFuncDocs(old.factories)\n-\t\t\tt.methods = makeFuncDocs(old.methods)\n+\t\t\tt.Consts = makeValues(old.values, token.CONST)\n+\t\t\tt.Vars = makeValues(old.values, token.VAR)\n+\t\t\tt.Funcs = makeFuncs(old.factories)\n+\t\t\tt.methods = makeFuncs(old.methods)\n \t\t\t// The list of embedded types\' methods is computed from the list\n \t\t\t// of embedded types, some of which may not have been processed\n \t\t\t// yet (i.e., their forward link is nil) - do this in a 2nd phase.\
@@ -496,7 +515,7 @@ func (doc *docReader) makeTypeDocs(m map[string]*typeInfo) []*TypeDoc {\n \t\t\told.forward = t // old has been processed\n \t\t\t// only add the type to the final type list if it\n \t\t\t// is exported or if we want to see all types\n-\t\t\tif old.exported() || !doc.exportsOnly {\n+\t\t\tif old.exported() || doc.mode&AllDecls != 0 {\n \t\t\t\tlist[i] = t\n \t\t\t\ti++\n \t\t\t}\
@@ -540,7 +559,7 @@ func (doc *docReader) makeTypeDocs(m map[string]*typeInfo) []*TypeDoc {\n \t\t}\n \t}\n \n-\t// phase 3: compute final method set for each TypeDoc\n+\t// phase 3: compute final method set for each Type\n \tfor _, d := range list {\n \t\tif len(d.embedded) > 0 {\n \t\t\t// there are embedded methods - exclude\
@@ -557,12 +576,15 @@ func (doc *docReader) makeTypeDocs(m map[string]*typeInfo) []*TypeDoc {\n \t\t\t}\n \t\t\td.Methods = mset.sortedList()\n \t\t} else {\n-\t\t\t// no embedded methods\n-\t\t\td.Methods = d.methods\n+\t\t\t// no embedded methods - convert into a Method list\n+\t\t\td.Methods = make([]*Method, len(d.methods))\n+\t\t\tfor i, m := range d.methods {\n+\t\t\t\td.Methods[i] = &Method{Func: m}\n+\t\t\t}\n \t\t}\n \t}\n \n-\tsort.Sort(sortTypeDoc(list))\n+\tsort.Sort(sortType(list))\n \treturn list\n }\n \
@@ -589,7 +611,7 @@ func collectEmbeddedMethods(mset methodSet, info *typeInfo, recvTypeName string,\n \t}\n }\n \n-func customizeRecv(m *FuncDoc, embeddedIsPtr bool, recvTypeName string) *FuncDoc {\n+func customizeRecv(m *Func, embeddedIsPtr bool, recvTypeName string) *Func {\n \tif m == nil || m.Decl == nil || m.Decl.Recv == nil || len(m.Decl.Recv.List) != 1 {\n \t\treturn m // shouldn\'t happen, but be safe\n \t}\
@@ -619,7 +641,7 @@ func customizeRecv(m *FuncDoc, embeddedIsPtr bool, recvTypeName string) *FuncDoc\n \treturn &newM\n }\n \n-func makeBugDocs(list []*ast.CommentGroup) []string {\n+func makeBugs(list []*ast.CommentGroup) []string {\n \td := make([]string, len(list))\n \tfor i, g := range list {\n \t\td[i] = g.Text()\
@@ -629,20 +651,20 @@ func makeBugDocs(list []*ast.CommentGroup) []string {\n \n // newDoc returns the accumulated documentation for the package.\n //\n-func (doc *docReader) newDoc(importpath string, filenames []string) *PackageDoc {\n-\tp := new(PackageDoc)\n-\tp.PackageName = doc.pkgName\n+func (doc *docReader) newDoc(importpath string, filenames []string) *Package {\n+\tp := new(Package)\n+\tp.Name = doc.pkgName\n \tp.ImportPath = importpath\n \tsort.Strings(filenames)\n \tp.Filenames = filenames\n \tp.Doc = doc.doc.Text()\n-\t// makeTypeDocs may extend the list of doc.values and\n+\t// makeTypes may extend the list of doc.values and\n \t// doc.funcs and thus must be called before any other\n \t// function consuming those lists\n-\tp.Types = doc.makeTypeDocs(doc.types)\n-\tp.Consts = makeValueDocs(doc.values, token.CONST)\n-\tp.Vars = makeValueDocs(doc.values, token.VAR)\n-\tp.Funcs = makeFuncDocs(doc.funcs)\n-\tp.Bugs = makeBugDocs(doc.bugs)\n+\tp.Types = doc.makeTypes(doc.types)\n+\tp.Consts = makeValues(doc.values, token.CONST)\n+\tp.Vars = makeValues(doc.values, token.VAR)\n+\tp.Funcs = makeFuncs(doc.funcs)\n+\tp.Bugs = makeBugs(doc.bugs)\n \treturn p\n }\n```

## コアとなるコードの解説

### `src/pkg/go/doc/doc.go`

*   **型名の変更**: `PackageDoc`, `ValueDoc`, `TypeDoc`, `FuncDoc`がそれぞれ`Package`, `Value`, `Type`, `Func`にリネームされました。これはAPIの簡潔化とGoの命名慣習への準拠を目的としています。
*   **`Package`型の変更**:
    *   `PackageName`フィールドが`Name`に変更されました。
*   **`Value`型の変更**:
    *   `Names []string`フィールドが追加されました。これにより、`const`や`var`のグループ宣言において、複数の識別子名を保持できるようになります。
*   **`Method`型の新規導入**:
    *   `Method`という新しい構造体が定義されました。これは`Func`型を埋め込み、さらに`Recv`(レシーバの基底型)と`Level`(埋め込みレベル)といったメソッド固有の情報を保持するよう設計されています。これにより、埋め込みインターフェースなどから継承されたメソッドのドキュメントをより詳細に生成する基盤が作られました。
*   **`Type`型の変更**:
    *   `Factories []*FuncDoc`が`Funcs []*Func`に変更されました。
    *   `Methods []*FuncDoc`が`Methods []*Method`に変更されました。
*   **`Mode`型の導入**:
    *   `Mode`という新しい`int`型が定義され、`AllDecls`という定数が追加されました。これは、ドキュメント抽出の動作を制御するためのビットマスクとして機能し、将来的な拡張性を高めます。
*   **`NewPackageDoc`から`New`への変更**:
    *   `NewPackageDoc`関数が`New`関数にリネームされ、引数も`exportsOnly bool`から`mode Mode`に変更されました。
    *   パッケージ内のファイルを処理する前に`sort.Strings(filenames)`が追加され、ファイル処理順序の一貫性が保証されるようになりました。

### `src/cmd/godoc/godoc.go`

*   `PageInfo`構造体内の`PDoc`フィールドの型が`*doc.PackageDoc`から`*doc.Package`に変更されました。
*   `getPageInfo`関数内で、`doc.NewPackageDoc`の呼び出しが`doc.New`に置き換えられ、`exportsOnly`のロジックが新しい`doc.Mode`型に変換されています。
*   ドキュメントのタイトル生成ロジックにおいて、`info.PDoc.PackageName`が`info.PDoc.Name`に変更されました。

### `src/pkg/go/doc/reader.go`

*   `docReader`構造体内の`exportsOnly bool`フィールドが`mode Mode`に変更され、初期化関数`init`もこれに合わせて更新されました。
*   `makeValueDocs`関数が`makeValues`にリネームされ、`Value`構造体の`Names`フィールドを埋めるために`specNames`ヘルパー関数が導入されました。
*   `makeFuncDocs`関数が`makeFuncs`にリネームされました。
*   `methodSet`の`sortedList`メソッドが`[]*FuncDoc`を返す代わりに`[]*Method`を返すように変更され、`sortMethod`という新しいソートヘルパーが導入されました。
*   `makeTypeDocs`関数が`makeTypes`にリネームされ、`Type`構造体の`Methods`フィールドを構築する際に、`Func`を`Method`型にラップするロジックが追加されました。
*   `makeBugDocs`関数が`makeBugs`にリネームされました。
*   `newDoc`関数内で、`PackageDoc`の代わりに`Package`がインスタンス化され、`PackageName`の代わりに`Name`が設定されるようになりました。また、各種`make*Docs`関数が新しい`make*`関数に置き換えられました。

これらの変更は、`go/doc`パッケージの内部実装と、それを呼び出す`godoc`ツールとの間の整合性を保ちつつ、APIの改善と将来の拡張性確保を目指したものです。

## 関連リンク

*   Go言語のドキュメンテーション: [https://go.dev/doc/effective_go#commentary](https://go.dev/doc/effective_go#commentary)
*   `go/doc`パッケージのドキュメンテーション: [https://pkg.go.dev/go/doc](https://pkg.go.dev/go/doc) (現在のAPI)
*   `godoc`ツール: [https://go.dev/blog/godoc](https://go.dev/blog/godoc)

## 参考にした情報源リンク

*   コミットメッセージに記載されているGoのコードレビューシステムへのリンク: [https://golang.org/cl/5528060](https://golang.org/cl/5528060) (現在はGoのGerritにリダイレクトされます)
*   Go言語の公式ドキュメンテーション
*   `go/ast`パッケージのドキュメンテーション
*   `go/token`パッケージのドキュメンテーション
*   `go/doc`パッケージのソースコード (変更前後の比較)
*   `godoc`ツールのソースコード (変更前後の比較)
*   Go言語の命名規則に関する一般的な情報源 (例: Effective Go)
*   Go言語のAPI設計に関する議論 (GoのメーリングリストやIssueトラッカーなど)