[インデックス 11134] ファイルの概要
このコミットは、Go言語の標準ライブラリにおけるドキュメンテーション生成に関連する重要なリファクタリングを含んでいます。具体的には、go/doc
パッケージにあったコメントテキスト抽出機能CommentText
を、抽象構文木(AST)を扱うgo/ast
パッケージ内のast.CommentGroup
型にText
メソッドとして移動させる変更です。これにより、go/doc
パッケージの依存関係が整理され、godoc
ツールのみがgo/doc
をインポートする形になります。
コミット
- コミットハッシュ:
f0f6aa59cce7c4b59c259c536ef4d1223f127683
- 作者: Russ Cox rsc@golang.org
- コミット日時: 2012年1月12日 木曜日 11:34:02 -0800
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/f0f6aa59cce7c4b59c259c536ef4d1223f127683
元コミット内容
go/doc: move CommentText to ast.CommentGroup's Text method
Now only godoc imports go/doc.
R=gri
CC=golang-dev
https://golang.org/cl/5541045
変更の背景
この変更の主な背景は、Go言語の標準ライブラリにおけるパッケージ間の依存関係の整理と、API設計の改善にあります。
元々、Goのソースコードからドキュメンテーションを生成するgo/doc
パッケージには、CommentText
という関数が存在し、これはgo/ast
パッケージのast.CommentGroup
型(コメントのグループを表すASTノード)を受け取り、そのコメントからマーカー(//
や/* */
など)を取り除いた純粋なテキストを抽出する役割を担っていました。
しかし、この設計には以下の問題がありました。
- 不必要な依存関係:
go/doc
パッケージがast.CommentGroup
を処理するためにgo/ast
パッケージに依存していました。コメントテキストの抽出は、ast.CommentGroup
自身の振る舞いとして自然に属する機能であり、ドキュメンテーション生成というよりはASTノードの基本的な操作に近いものでした。 - APIの整合性:
ast.CommentGroup
はASTの一部であり、そのテキスト表現を取得する機能は、その型自身が提供すべきであるという考え方です。外部のパッケージ(go/doc
)がその内部構造を操作するような形は、APIの整合性やカプセル化の観点から望ましくありませんでした。 godoc
以外の利用: コミットメッセージにある「Now only godoc imports go/doc.」という記述が示すように、この変更の目的の一つは、go/doc
パッケージの利用をgodoc
ツールに限定することでした。CommentText
がgo/doc
にある限り、godoc
以外のツールやライブラリもコメントテキストを抽出するためにgo/doc
をインポートする必要があり、これがgo/doc
の利用範囲を広げていました。CommentText
をgo/ast
に移動することで、go/doc
の役割をより純粋なドキュメンテーション生成に特化させ、他のパッケージがコメントテキストを必要とする場合は直接go/ast
を利用するように促すことができます。
これらの理由から、CommentText
関数をast.CommentGroup
のメソッドとして再配置することで、よりクリーンで論理的なAPI設計を目指しました。
前提知識の解説
このコミットを理解するためには、以下のGo言語の概念とパッケージに関する知識が必要です。
-
Go言語の抽象構文木 (AST):
- Go言語のコンパイラやツールは、ソースコードを直接解析するのではなく、まずそのコードの抽象構文木(Abstract Syntax Tree, AST)を構築します。ASTは、プログラムの構造を木構造で表現したものです。
go/ast
パッケージは、GoのソースコードのASTを表現するためのデータ構造と、それらを操作するための関数を提供します。例えば、関数宣言、変数宣言、式、コメントなどもASTノードとして表現されます。go/token
パッケージは、ソースコード内の位置(ファイル、行、列)やトークン(キーワード、識別子、演算子など)を扱うための基本的な型を提供し、go/ast
パッケージと密接に連携します。
-
go/ast.CommentGroup
:go/ast
パッケージには、CommentGroup
という構造体があります。これは、Goのソースコード内のコメントのグループ(例えば、関数や型の宣言の直前にあるドキュメンテーションコメントなど)を表します。CommentGroup
は、複数の*Comment
(個々のコメント行やブロックコメント)のリストを保持しています。
-
go/doc
パッケージ:go/doc
パッケージは、Goのソースコードから抽出されたASTを解析し、人間が読める形式のドキュメンテーションを生成するための機能を提供します。これは、godoc
ツールの中核をなすパッケージです。- このパッケージは、パッケージ、型、関数、変数などのドキュメンテーションコメントを抽出し、整形する役割を担います。
-
godoc
ツール:godoc
は、Go言語のソースコードからドキュメンテーションを生成し、Webブラウザで表示したり、プレーンテキストとして出力したりする公式ツールです。go/doc
パッケージを内部的に利用しています。
-
ビルドスクリプト (
buildscript_*.sh
):- Go言語の初期のビルドシステムでは、各プラットフォーム(darwin, freebsd, linux, netbsd, openbsd, plan9, windows)およびアーキテクチャ(386, amd64, arm)ごとにシェルスクリプト(
buildscript_*.sh
)が存在し、Goの標準ライブラリやツールをコンパイル・インストールする手順が記述されていました。 - これらのスクリプトは、Goのコンパイラ(
8g
,6g
,5g
など)やアーカイバ(gopack
)を呼び出し、各パッケージのオブジェクトファイルやアーカイブファイルを生成し、適切なディレクトリに配置していました。 - パッケージ間の依存関係の変更は、これらのビルドスクリプトにおけるコンパイル順序や依存パッケージの指定に影響を与える可能性があります。
- Go言語の初期のビルドシステムでは、各プラットフォーム(darwin, freebsd, linux, netbsd, openbsd, plan9, windows)およびアーキテクチャ(386, amd64, arm)ごとにシェルスクリプト(
技術的詳細
このコミットの技術的な核心は、go/doc
パッケージからCommentText
関数を削除し、その機能をgo/ast
パッケージのast.CommentGroup
型にText()
メソッドとして実装し直した点にあります。
変更前:
go/doc
パッケージには、以下のようなシグネチャを持つCommentText
関数がありました。
func CommentText(comment *ast.CommentGroup) string
この関数は、go/ast
パッケージのast.CommentGroup
型のポインタを受け取り、コメントマーカー(//
, /*
, */
)や余分な空白を取り除いた整形済みのコメントテキストを文字列として返していました。
変更後:
-
go/ast.CommentGroup
へのText()
メソッドの追加:src/pkg/go/ast/ast.go
ファイルに、ast.CommentGroup
型に対するText()
メソッドが追加されました。このメソッドは、以前go/doc.CommentText
が持っていたロジック(コメントマーカーの除去、行頭・行末の空白のトリミング、連続する空行の単一化など)を内包しています。 これにより、ast.CommentGroup
オブジェクト自体が、そのコメントの整形済みテキスト表現を提供する責任を持つようになりました。 -
go/doc.CommentText
の削除:src/pkg/go/doc/comment.go
ファイルから、CommentText
関数が完全に削除されました。 -
関連パッケージの更新:
go/doc.CommentText
を利用していたGoの様々なツールやパッケージが、新しく追加されたast.CommentGroup.Text()
メソッドを使用するように変更されました。これには、cmd/cgo
,cmd/go
,cmd/godoc
,cmd/gotest
,go/build
,go/doc/example
,go/doc/reader
などが含まれます。これらのファイルでは、doc.CommentText(cg)
のような呼び出しがcg.Text()
に置き換えられています。 -
ビルドスクリプトの変更: Goのビルドスクリプト(
src/buildscript_*.sh
)も多数変更されています。これは、パッケージ間の依存関係の変更(特にgo/doc
がgo/ast
に依存しなくなったこと、またはその逆)に伴い、コンパイル順序やパッケージのビルド方法が調整されたためと考えられます。具体的には、go/doc
のビルドセクションが削除されたり、他のパッケージのビルド順序が調整されたりしています。これは、go/doc
がCommentText
のロジックを持たなくなったことで、そのビルドに必要な依存関係が減少したことを示唆しています。
このリファクタリングにより、go/ast
パッケージはASTノードの基本的な操作(コメントテキストの抽出も含む)に特化し、go/doc
パッケージは純粋にドキュメンテーションの構造化と生成に特化するという、より明確な役割分担が実現されました。これにより、Goのツールチェインのモジュール性が向上し、将来的なメンテナンスや機能拡張が容易になります。
コアとなるコードの変更箇所
このコミットのコアとなる変更は、主に以下の2つのファイルに集約されます。
-
src/pkg/go/ast/ast.go
:ast.CommentGroup
型にText()
メソッドが追加されました。--- a/src/pkg/go/ast/ast.go +++ b/src/pkg/go/ast/ast.go @@ -9,6 +9,7 @@ package ast import ( "go/token" + "strings" "unicode" "unicode/utf8" ) @@ -76,6 +77,74 @@ type CommentGroup struct { func (g *CommentGroup) Pos() token.Pos { return g.List[0].Pos() } func (g *CommentGroup) End() token.Pos { return g.List[len(g.List)-1].End() } +func isWhitespace(ch byte) bool { return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' } + +func stripTrailingWhitespace(s string) string { + i := len(s) + for i > 0 && isWhitespace(s[i-1]) { + i-- + } + return s[0:i] +} + +// Text returns the text of the comment, +// with the comment markers - //, /*, and */ - removed. +func (g *CommentGroup) Text() string { + if g == nil { + return "" + } + comments := make([]string, len(g.List)) + for i, c := range g.List { + comments[i] = string(c.Text) + } + + lines := make([]string, 0, 10) // most comments are less than 10 lines + for _, c := range comments { + // Remove comment markers. + // The parser has given us exactly the comment text. + switch c[1] { + case '/': + //-style comment + c = c[2:] + // Remove leading space after //, if there is one. + // TODO(gri) This appears to be necessary in isolated + // cases (bignum.RatFromString) - why? + if len(c) > 0 && c[0] == ' ' { + c = c[1:] + } + case '*': + /*-style comment */ + c = c[2 : len(c)-2] + } + + // Split on newlines. + cl := strings.Split(c, "\n") + + // Walk lines, stripping trailing white space and adding to list. + for _, l := range cl { + lines = append(lines, stripTrailingWhitespace(l)) + } + } + + // Remove leading blank lines; convert runs of + // interior blank lines to a single blank line. + n := 0 + for _, line := range lines { + if line != "" || n > 0 && lines[n-1] != "" { + lines[n] = line + n++ + } + } + lines = lines[0:n] + + // Add final "" entry to get trailing newline from Join. + if n > 0 && lines[n-1] != "" { + lines = append(lines, "") + } + + return strings.Join(lines, "\n") +} + // ---------------------------------------------------------------------------- // Expressions and types
-
src/pkg/go/doc/comment.go
:CommentText
関数が削除されました。--- a/src/pkg/go/doc/comment.go +++ b/src/pkg/go/doc/comment.go @@ -7,7 +7,6 @@ package doc import ( - "go/ast" "io" "regexp" "strings" @@ -16,74 +15,6 @@ import ( "unicode/utf8" ) -func isWhitespace(ch byte) bool { return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' } - -func stripTrailingWhitespace(s string) string { - i := len(s) - for i > 0 && isWhitespace(s[i-1]) { - i-- - } - return s[0:i] -} - -// CommentText returns the text of comment, -// with the comment markers - //, /*, and */ - removed. -func CommentText(comment *ast.CommentGroup) string { - if comment == nil { - return "" - } - comments := make([]string, len(comment.List)) - for i, c := range comment.List { - comments[i] = string(c.Text) - } - - lines := make([]string, 0, 10) // most comments are less than 10 lines - for _, c := range comments { - // Remove comment markers. - // The parser has given us exactly the comment text. - switch c[1] { - case '/': - //-style comment - c = c[2:] - // Remove leading space after //, if there is one. - // TODO(gri) This appears to be necessary in isolated - // cases (bignum.RatFromString) - why? - if len(c) > 0 && c[0] == ' ' { - c = c[1:] - } - case '*': - /*-style comment */ - c = c[2 : len(c)-2] - } - - // Split on newlines. - cl := strings.Split(c, "\n") - - // Walk lines, stripping trailing white space and adding to list. - for _, l := range cl { - lines = append(lines, stripTrailingWhitespace(l)) - } - } - - // Remove leading blank lines; convert runs of - // interior blank lines to a single blank line. - n := 0 - for _, line := range lines { - if line != "" || n > 0 && lines[n-1] != "" { - lines[n] = line - n++ - } - } - lines = lines[0:n] - - // Add final "" entry to get trailing newline from Join. - if n > 0 && lines[n-1] != "" { - lines = append(lines, "") - } - - return strings.Join(lines, "\n") -} - var ( ldquo = []byte("“") rdquo = []byte("”")
コアとなるコードの解説
src/pkg/go/ast/ast.go
の変更
ast.CommentGroup
構造体にText()
メソッドが追加されました。このメソッドは、コメントグループが保持する個々のコメント(*ast.Comment
)のテキストを結合し、Goのドキュメンテーションコメントの慣習に従って整形します。
isWhitespace
とstripTrailingWhitespace
: これらのヘルパー関数は、コメントテキストから不要な空白を除去するために導入されました。isWhitespace
は特定のバイトが空白文字であるかを判定し、stripTrailingWhitespace
は文字列の末尾から空白を削除します。Text()
メソッドのロジック:CommentGroup
がnil
の場合は空文字列を返します。CommentGroup
内の各*ast.Comment
の生テキストを取得します。- 各コメントテキストからコメントマーカー(
//
や/* */
、*/
)を削除します。//
コメントの場合は、その後の先頭の空白も除去します。 - コメントテキストを改行で分割し、各行の末尾の空白を
stripTrailingWhitespace
で除去します。 - 整形された行のリストから、先頭の空行を除去し、連続する空行を単一の空行にまとめます。
- 最後に、整形された行を改行文字で結合して返します。
この変更により、ast.CommentGroup
は自身のテキスト表現を生成する自己完結型のエンティティとなり、go/doc
パッケージがこの低レベルな整形ロジックを持つ必要がなくなりました。
src/pkg/go/doc/comment.go
の変更
このファイルからは、以前go/doc
パッケージが提供していたCommentText
関数が完全に削除されました。これにより、go/doc
パッケージはgo/ast
パッケージへの直接的な依存を解消し、より高レベルなドキュメンテーション構造の構築に専念できるようになりました。
その他のファイルの変更
src/cmd/cgo/ast.go
, src/cmd/go/pkg.go
, src/cmd/go/test.go
, src/cmd/godoc/dirtrees.go
, src/cmd/gotest/gotest.go
, src/pkg/go/build/dir.go
, src/pkg/go/doc/example.go
, src/pkg/go/doc/reader.go
などのファイルでは、go/doc.CommentText
の呼び出しが、新しく導入されたast.CommentGroup.Text()
メソッドの呼び出しに置き換えられています。
例 (src/cmd/cgo/ast.go
):
--- a/src/cmd/cgo/ast.go
+++ b/src/cmd/cgo/ast.go
@@ -9,7 +9,6 @@ package main
import (
"fmt"
"go/ast"
- "go/doc"
"go/parser"
"go/scanner"
"go/token"
@@ -79,7 +78,7 @@ func (f *File) ReadGo(name string) {
}
if cg != nil {
f.Preamble += fmt.Sprintf("#line %d %q\\n", sourceLine(cg), name)
- f.Preamble += doc.CommentText(cg) + "\\n"
+ f.Preamble += cg.Text() + "\\n"
}
}
}
この変更は、go/doc
パッケージへの依存を削除し、ast.CommentGroup
が提供する新しいメソッドを直接利用することで、コードの簡潔性と依存関係の明確化を実現しています。
ビルドスクリプトの変更
多数のsrc/buildscript_*.sh
ファイルが変更されていますが、これは主にgo/doc
パッケージのビルドプロセスからCommentText
に関連するステップが削除されたこと、およびパッケージ間の依存関係の変更に伴うビルド順序の調整を反映したものです。これらの変更は、Goのビルドシステムが、リファクタリングされたパッケージ構造に適応したことを示しています。
関連リンク
- Go CL (Change List) へのリンク: https://golang.org/cl/5541045
参考にした情報源リンク
- Go言語の公式ドキュメンテーション: https://go.dev/
go/ast
パッケージのドキュメンテーション: https://pkg.go.dev/go/astgo/doc
パッケージのドキュメンテーション: https://pkg.go.dev/go/doc- Go言語のソースコード (GitHub): https://github.com/golang/go
- Go言語のビルドシステムに関する情報 (Goの初期のビルドプロセスに関する一般的な知識)