[インデックス 13345] ファイルの概要
このコミットは、Go言語のドキュメンテーションツールであるgodoc
の挙動を修正するものです。具体的には、godoc
が様々なフィルタリングされたビュー(例えば、特定の宣言のみを表示するビュー)において、関連するコメントを表示するように改善しています。
変更されたファイルは以下の2つです。
src/cmd/godoc/godoc.go
:godoc
の主要なロジックが含まれるファイル。AST(抽象構文木)の処理とパッケージのエクスポートに関する変更が含まれます。src/cmd/godoc/main.go
:godoc
コマンドのエントリポイントであり、フィルタリングされた出力の表示方法に関する変更が含まれます。
コミット
commit 8140cd9149493cffc797edafc60a230f1c9b5462
Author: Robert Griesemer <gri@golang.org>
Date: Wed Jun 13 13:32:59 2012 -0700
godoc: show comments in various filtered views
Fixes #3454.
R=rsc
CC=golang-dev
https://golang.org/cl/6305069
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/8140cd9149493cffc797edafc60a230f1c9b5462
元コミット内容
godoc: show comments in various filtered views
Fixes #3454.
R=rsc
CC=golang-dev
https://golang.org/cl/6305069
変更の背景
godoc
はGo言語のソースコードからドキュメンテーションを生成し、表示するためのツールです。Go言語では、関数、変数、型などの宣言の直前に書かれたコメントがその宣言のドキュメンテーションとして扱われます。
このコミットが行われる前は、godoc
が特定のフィルタリングされたビュー(例えば、特定のパッケージ内のエクスポートされたシンボルのみを表示する場合や、正規表現でフィルタリングされた宣言を表示する場合)でドキュメンテーションコメントを適切に表示しないという問題がありました。具体的には、Issue 3454で報告された問題に対応しています。
この問題は、godoc
がASTを処理する際に、コメントと宣言の関連付けがフィルタリングプロセス中に失われたり、適切に処理されなかったりすることが原因でした。結果として、ユーザーはフィルタリングされた出力で完全なドキュメンテーション情報を得ることができませんでした。このコミットは、このコメント表示の不整合を解消し、godoc
の有用性を向上させることを目的としています。
前提知識の解説
このコミットの変更内容を理解するためには、以下のGo言語の標準ライブラリと概念に関する知識が必要です。
-
go/ast
パッケージ:- Go言語のソースコードを抽象構文木(AST: Abstract Syntax Tree)として表現するためのデータ構造と関数を提供します。
ast.File
: 単一のGoソースファイルを表すASTのルートノード。ast.Package
: 複数のast.File
から構成されるGoパッケージを表します。ast.Decl
: 宣言(関数、変数、型など)を表すインターフェース。ast.CommentGroup
: 複数のコメント行をまとめたグループ。ast.NewCommentMap(fset *token.FileSet, node ast.Node)
: ASTノード内のコメントと、それらが関連付けられているASTノード(宣言など)のマッピングを作成します。これにより、どのコメントがどのコード要素に属しているかを追跡できます。CommentMap.Filter(node ast.Node)
:CommentMap
から、指定されたASTノードに関連するコメントのみをフィルタリングして新しいCommentMap
を返します。ast.FileExports(file *ast.File)
: 指定されたファイルからエクスポートされた(大文字で始まる)宣言のみを残し、それ以外の宣言をASTから削除します。ast.PackageExports(pkg *ast.Package)
: パッケージ内のすべてのファイルに対してFileExports
を適用し、エクスポートされた宣言のみを残します。ast.MergePackageFiles(pkg *ast.Package, mode ast.MergeMode)
: 複数のファイルからなるパッケージのASTを単一のファイルASTにマージします。MergeMode
はマージ時の挙動を制御します。ast.FilterUnassociatedComments
: マージ時に、どの宣言にも関連付けられていないコメントをフィルタリングするモード。
-
go/token
パッケージ:- Goソースコード内のトークン(キーワード、識別子、演算子など)と、それらのソースコード上の位置(行番号、列番号、オフセット)を管理します。
token.FileSet
: 複数のファイルにわたるソースコードの位置情報を一元的に管理するためのセット。
-
go/printer
パッケージ:- ASTをGoソースコードとして整形して出力するための機能を提供します。
printer.CommentedNode
: ASTノードと、それに関連付けられたコメントグループを一緒に保持するための構造体。printer
パッケージがASTを整形する際に、この情報を使ってコメントを適切な位置に出力します。
-
godoc
の動作:godoc
は、Goのソースファイルを解析してASTを構築します。- 構築されたASTからドキュメンテーションコメントを抽出し、HTMLなどの形式で整形して表示します。
- フィルタリング機能は、ユーザーが特定の条件(例: エクスポートされたシンボルのみ、正規表現にマッチするシンボルのみ)に基づいて表示内容を絞り込むために使用されます。
技術的詳細
このコミットの核心は、godoc
がASTをフィルタリングする際に、コメントが適切に保持され、最終的な出力に反映されるようにすることです。
以前のgodoc
の実装では、ASTをフィルタリングする際に、コメントと宣言の関連付けが失われる可能性がありました。特に、ast.PackageExports
やast.FilterFile
のような関数がASTを変更する際に、コメントが適切に処理されず、結果としてフィルタリングされたビューでコメントが表示されないという問題が発生していました。
このコミットでは、以下の主要なアプローチでこの問題を解決しています。
- コメントマップの活用:
go/ast
パッケージのast.NewCommentMap
とCommentMap.Filter
を積極的に利用することで、ASTの変更(フィルタリング)が行われた後でも、各宣言にどのコメントが関連付けられているかを正確に追跡できるようにしました。 ast.PackageExports
のローカル実装:ast.PackageExports
のシグネチャが固定されているため、godoc
の内部でコメント処理をカスタマイズするためにpackageExports
というローカル関数を実装しました。この関数は、各ファイルのコメントリストを正しく更新しながらエクスポートされた宣言のみを抽出します。ast.MergePackageFiles
の挙動変更: パッケージファイルをマージする際に、以前はast.FilterUnassociatedComments
というモードを使用していましたが、これを0
(つまり、コメントのフィルタリングを行わない)に変更しました。これにより、マージプロセス中にコメントが誤って削除されるのを防ぎます。コメントのフィルタリングは、より制御された方法で後から行われるようになります。go/printer
との連携: フィルタリングされた宣言を実際に表示する際に、go/printer
パッケージのprinter.CommentedNode
構造体を使用するように変更しました。これにより、宣言とそれに関連するコメントがペアとしてprinter
に渡され、printer
がコメントを適切な位置に整形して出力できるようになります。
これらの変更により、ASTのフィルタリングが行われた後でも、コメント情報が保持され、最終的なgodoc
の出力で正しく表示されるようになりました。
コアとなるコードの変更箇所
src/cmd/godoc/godoc.go
--- a/src/cmd/godoc/godoc.go
+++ b/src/cmd/godoc/godoc.go
@@ -866,6 +866,19 @@ func inList(name string, list []string) bool {
return false
}
+// packageExports is a local implementation of ast.PackageExports
+// which correctly updates each package file's comment list.
+// (The ast.PackageExports signature is frozen, hence the local
+// implementation).
+//
+func packageExports(fset *token.FileSet, pkg *ast.Package) {
+ for _, src := range pkg.Files {
+ cmap := ast.NewCommentMap(fset, src)
+ ast.FileExports(src)
+ src.Comments = cmap.Filter(src).Comments()
+ }
+}
+
// getPageInfo returns the PageInfo for a package directory abspath. If the
// parameter genAST is set, an AST containing only the package exports is
// computed (PageInfo.PAst), otherwise package documentation (PageInfo.Doc)
@@ -1012,9 +1025,9 @@ func (h *docServer) getPageInfo(abspath, relpath, pkgname string, mode PageInfoM
// TODO(gri) Consider eliminating export filtering in this mode,
// or perhaps eliminating the mode altogether.
if mode&noFiltering == 0 {
- ast.PackageExports(pkg)
+ packageExports(fset, pkg)
}
- past = ast.MergePackageFiles(pkg, ast.FilterUnassociatedComments)
+ past = ast.MergePackageFiles(pkg, 0)
}
}
src/cmd/godoc/main.go
--- a/src/cmd/godoc/main.go
+++ b/src/cmd/godoc/main.go
@@ -36,6 +36,7 @@ import (
"fmt"
"go/ast"
"go/build"
+ "go/printer"
"io"
"log"
"net/http"
@@ -424,20 +425,24 @@ func main() {
filter := func(s string) bool { return rx.MatchString(s) }
switch {
case info.PAst != nil:
+ cmap := ast.NewCommentMap(info.FSet, info.PAst)
ast.FilterFile(info.PAst, filter)
// Special case: Don't use templates for printing
// so we only get the filtered declarations without
// package clause or extra whitespace.
for i, d := range info.PAst.Decls {
+ // determine the comments associated with d only
+ comments := cmap.Filter(d).Comments()
+ cn := &printer.CommentedNode{Node: d, Comments: comments}
if i > 0 {
fmt.Println()
}
if *html {
var buf bytes.Buffer
- writeNode(&buf, info.FSet, d)
+ writeNode(&buf, info.FSet, cn)
FormatText(os.Stdout, buf.Bytes(), -1, true, "", nil)
} else {
- writeNode(os.Stdout, info.FSet, d)
+ writeNode(os.Stdout, info.FSet, cn)
}
fmt.Println()
}
コアとなるコードの解説
src/cmd/godoc/godoc.go
の変更
-
packageExports
関数の追加:- この新しい関数は、
ast.PackageExports
のローカルな代替実装です。 - 元の
ast.PackageExports
は、パッケージ内の各ファイルのASTからエクスポートされた宣言のみを抽出しますが、その過程でコメントの関連付けが失われる可能性がありました。 packageExports
関数は、各ソースファイル(src
)に対して以下の処理を行います。cmap := ast.NewCommentMap(fset, src)
: まず、現在のファイル(src
)のAST全体からコメントマップを作成します。これにより、どのコメントがどのASTノード(宣言など)に関連付けられているかの情報が保持されます。ast.FileExports(src)
: ファイルからエクスポートされた宣言のみを残します。この操作はASTを変更します。src.Comments = cmap.Filter(src).Comments()
:ast.FileExports
によってASTが変更された後、元のコメントマップ(cmap
)を使って、変更後のAST (src
) に関連するコメントのみをフィルタリングし、その結果をsrc.Comments
に再設定します。これにより、フィルタリング後もコメントが正しくASTに紐付けられます。
- この変更により、パッケージのエクスポート処理中にコメント情報が失われるのを防ぎます。
- この新しい関数は、
-
getPageInfo
関数内の変更:ast.PackageExports(pkg)
の呼び出しが、新しく定義されたpackageExports(fset, pkg)
に置き換えられました。これにより、コメントの関連付けを維持したままパッケージのエクスポート処理が行われるようになります。ast.MergePackageFiles(pkg, ast.FilterUnassociatedComments)
の呼び出しが、ast.MergePackageFiles(pkg, 0)
に変更されました。- 以前の
ast.FilterUnassociatedComments
モードは、マージ時にどの宣言にも関連付けられていないコメントを削除していました。これが、フィルタリングされたビューでコメントが表示されない原因の一つでした。 0
を指定することで、マージ時にコメントのフィルタリングを行わないようにします。これにより、すべてのコメントがマージされたASTに保持され、後続の処理で適切に扱えるようになります。
- 以前の
src/cmd/godoc/main.go
の変更
-
go/printer
パッケージのインポート:go/printer
パッケージが新しくインポートされました。これは、ASTノードとコメントを組み合わせて整形出力するために使用されます。
-
フィルタリングされた宣言の出力ロジックの変更:
case info.PAst != nil:
ブロック内で、フィルタリングされた宣言(info.PAst.Decls
)を処理するループに大きな変更が加えられました。cmap := ast.NewCommentMap(info.FSet, info.PAst)
: フィルタリングされたAST (info.PAst
) からコメントマップを作成します。これにより、フィルタリング後のASTにおけるコメントと宣言の関連付けを正確に把握できます。ast.FilterFile(info.PAst, filter)
: ユーザーが指定した正規表現フィルタに基づいてASTをフィルタリングします。- ループ内で各宣言
d
を処理する際に、以下の行が追加されました。comments := cmap.Filter(d).Comments()
: 現在の宣言d
に関連するコメントのみを、事前に作成したコメントマップ(cmap
)からフィルタリングして取得します。cn := &printer.CommentedNode{Node: d, Comments: comments}
: 取得した宣言d
と、それに関連するコメントcomments
をprinter.CommentedNode
構造体にラップします。この構造体は、go/printer
パッケージがコメントを考慮してASTノードを整形するために使用されます。
writeNode
関数の呼び出しが変更されました。- 以前は
writeNode(&buf, info.FSet, d)
のように宣言d
を直接渡していましたが、新しいコードではwriteNode(&buf, info.FSet, cn)
のようにprinter.CommentedNode
を渡すようになりました。 - これにより、
writeNode
(内部でgo/printer
を使用していると推測される)が、宣言だけでなく、それに関連するコメントも考慮して整形出力を行うことができるようになります。
- 以前は
これらの変更により、godoc
はフィルタリングされたビューにおいても、各宣言に紐付けられたドキュメンテーションコメントを正確に抽出し、整形して表示することが可能になりました。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/8140cd9149493cffc797edafc60a230f1c9b5462
- Go CL (Change List): https://golang.org/cl/6305069
参考にした情報源リンク
- Go CL 6305069: GODOC: SHOW COMMENTS IN VARIOUS FILTERED VIEWS (このコミットの直接の変更履歴)
- Go言語の
go/ast
パッケージのドキュメンテーション (ASTの構造と操作に関する詳細) - Go言語の
go/token
パッケージのドキュメンテーション (ソースコードの位置情報に関する詳細) - Go言語の
go/printer
パッケージのドキュメンテーション (ASTの整形出力に関する詳細) - Go言語の
godoc
ツールに関する一般的な情報