[インデックス 15262] ファイルの概要
このコミットは、Go言語のドキュメンテーション生成ツールである go/doc
パッケージに、任意の種類の「ノート」(注釈)をサポートする機能を追加するものです。これまでの BUG(userid):
形式のバグコメントと同様に、// MARKER(userid): comment
という形式で、開発者がコード内に特定の注釈を埋め込めるようになります。MARKER
は2文字以上の大文字のアルファベットで構成されます。
コミット
commit d0f3475fda0a2ed3583a3b993be4cd526a0712a8
Author: Cosmos Nicolaou <cnicolaou@google.com>
Date: Thu Feb 14 20:20:32 2013 -0800
go/doc: add support for arbitrary notes
Add support for arbitrary notes of the form // MARKER(userid): comment
in the same vein as BUG(userid): A marker must be two or more upper case [A-Z] letters.
R=gri, rsc, bradfitz, jscrockett01
CC=golang-dev
https://golang.org/cl/7322061
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d0f3475fda0a2ed3583a3b993be4cd526a0712a8
元コミット内容
go/doc
パッケージに任意のノート形式のサポートを追加します。
BUG(userid):
と同様に、// MARKER(userid): comment
形式の任意のノートをサポートします。マーカーは2文字以上の大文字の [A-Z] 文字である必要があります。
変更の背景
Go言語のドキュメンテーションツール go/doc
は、コード内のコメントからドキュメントを生成する際に、特定の形式のコメントを特別に扱います。特に // BUG(userid): comment
という形式のコメントは、生成されるドキュメント内で「バグ」として認識され、一覧表示される機能がありました。
このコミットの背景には、BUG
コメントだけでなく、TODO
(未実装のタスク)、FIXME
(修正が必要な箇所)、SECURITY
(セキュリティ関連の懸念)など、開発者がコードベースに埋め込みたい様々な種類の注釈(ノート)が存在するというニーズがあります。これらの注釈も BUG
コメントと同様に、ドキュメントとして抽出・集約できると、コードの保守性やプロジェクト管理の効率が向上します。
この変更は、既存の BUG
コメントの仕組みを汎用化し、任意のマーカー(2文字以上の大文字アルファベット)を持つノートを認識・収集できるようにすることで、より柔軟なドキュメンテーションとコード管理を可能にすることを目的としています。
前提知識の解説
go/doc
パッケージ: Go言語の標準ライブラリの一つで、Goのソースコードからドキュメンテーションを生成するためのツールです。go doc
コマンドやgodoc
サーバーの基盤となっています。ソースコードのAST(抽象構文木)を解析し、パッケージ、型、関数、変数などのドキュメントコメントを抽出します。go/ast
パッケージ: Go言語のソースコードを抽象構文木(AST)として表現するためのデータ構造と関数を提供します。go/doc
はこのgo/ast
を利用してソースコードを解析し、コメントなどの情報を取得します。- コメントグループ (
ast.CommentGroup
): Goのソースコードにおけるコメントは、ast
パッケージではast.CommentGroup
として扱われます。これは複数のast.Comment
(個々のコメント行)をまとめたものです。 - 正規表現 (Regular Expression): 特定の文字列パターンを記述するための言語です。このコミットでは、コメントのテキストから
MARKER(userid):
のような特定のパターンを識別するために正規表現が使用されています。Go言語ではregexp
パッケージで正規表現を扱います。 BUG(userid):
コメント: 従来のgo/doc
でサポートされていた特殊なコメント形式です。userid
はバグを報告したユーザーの識別子(例:BUG(rsc):
)。このコメントはgo/doc
によって抽出され、生成されるドキュメントの「Bugs」セクションに表示されます。
技術的詳細
このコミットの主要な技術的変更点は、go/doc
パッケージがコメントを解析し、特定の形式のノートを抽出するメカニズムを汎用化したことです。
-
Package
構造体の拡張:src/pkg/go/doc/doc.go
にあるPackage
構造体に、新しいフィールドNotes map[string][]string
が追加されました。これは、抽出された任意のノートを格納するためのマップです。キーはノートのマーカー(例: "TODO", "SECBUG")、値はそのマーカーに属するコメント文字列のリストです。type Package struct { // ... 既存のフィールド ... Bugs []string // Notes such as TODO(userid): or SECURITY(userid): // along the lines of BUG(userid). Any marker with 2 or more upper // case [A-Z] letters is recognised. // BUG is explicitly not included in these notes but will // be in a subsequent change when the Bugs field above is removed. Notes map[string][]string // ... 既存のフィールド ... }
-
reader
構造体の拡張:src/pkg/go/doc/reader.go
にあるreader
構造体は、ソースファイルを読み込み、ドキュメント情報を収集する内部的なヘルパ構造体です。この構造体にもnotes map[string][]string
フィールドが追加され、解析中にノートを一時的に保持します。type reader struct { // ... 既存のフィールド ... bugs []string notes map[string][]string // 新規追加 // ... 既存のフィールド ... }
-
新しい正規表現の導入: コメントからノートを識別するために、新しい正規表現
noteMarker
が導入されました。var ( noteMarker = regexp.MustCompile(`^/[/*][ \t]*([A-Z][A-Z]+)\(.+\):[ \t]*(.*)`) // MARKER(uid) noteContent = regexp.MustCompile(`[^ \n\r\t]+`) // at least one non-whitespace char )
noteMarker
://
または/*
で始まり、その後に空白、2文字以上の大文字アルファベット(([A-Z][A-Z]+)
がマーカーをキャプチャ)、括弧で囲まれた任意の文字((.+)
がuserid
部分をキャプチャ)、コロン、空白、そして残りのコメント内容((.*)
がコメント本文をキャプチャ)が続くパターンにマッチします。noteContent
: コメント本文が空でないことを確認するための正規表現です。
-
readNote
関数の追加:readNote
という新しいヘルパ関数が追加されました。この関数はast.CommentGroup
を受け取り、それがMARKER(userid): comment
形式のノートであるかを判定し、もしそうであればマーカーとコメント本文を返します。func readNote(c *ast.CommentGroup) (marker, annotation string) { text := c.List[0].Text // コメントグループの最初の行のテキストを取得 if m := noteMarker.FindStringSubmatch(text); m != nil { if btxt := m[2]; noteContent.MatchString(btxt) { // 非空のMARKERコメントの場合 list := append([]*ast.Comment(nil), c.List...) // コメントリストをコピー list[0].Text = m[2] // 最初のコメント行のテキストをコメント本文に置き換え return m[1], (&ast.CommentGroup{List: list}).Text() // マーカーと整形されたコメント本文を返す } } return "", "" // マッチしない場合は空文字列を返す }
-
readFile
関数の変更:readFile
関数は、ソースファイル内のコメントを走査し、readNote
を呼び出してノートを識別するように変更されました。- 識別されたノートは
r.notes
マップに追加されます。 - 既存の
BUG
コメントについては、一時的にr.bugs
フィールドにも追加されますが、これは将来的にr.bugs
フィールドが削除されるまでの過渡的な措置であることがコメントで示されています。
// collect MARKER(...): annotations for _, c := range src.Comments { if marker, text := readNote(c); marker != "" { // Remove r.bugs in a separate CL along with // any necessary changes to client code. if marker == "BUG" { r.bugs = append(r.bugs, text) // BUGコメントは既存のbugsフィールドにも追加 } else { if r.notes == nil { // notesマップがnilの場合の初期化 r.notes = make(map[string][]string) } r.notes[marker] = append(r.notes[marker], text) // その他のノートはnotesマップに追加 } } }
- 識別されたノートは
-
readPackage
関数の変更:readPackage
関数は、reader
構造体のnotes
マップを初期化するように変更されました。func (r *reader) readPackage(pkg *ast.Package, mode Mode) { // ... r.notes = make(map[string][]string) // notesマップの初期化 // ... }
-
テストデータの更新:
src/pkg/go/doc/testdata/
以下のテストファイル (a0.go
,a1.go
) にTODO
やSECBUG
といった新しい形式のノートコメントが追加され、それに対応するgolden
ファイル (a.0.golden
,a.1.golden
,a.2.golden
) も更新され、新しいノートが正しく抽出・表示されることを確認しています。 また、ドキュメント生成のテンプレートファイルsrc/pkg/go/doc/testdata/template.txt
も更新され、Notes
フィールドの内容をレンダリングするロジックが追加されました。
これらの変更により、go/doc
は BUG
コメントだけでなく、任意のマーカーを持つコメントを解析し、構造化されたデータとして Package.Notes
に格納できるようになりました。
コアとなるコードの変更箇所
src/pkg/go/doc/doc.go
:Package
構造体にNotes map[string][]string
フィールドを追加 (L.18-23)New
関数でr.notes
をPackage.Notes
に割り当て (L.95)
src/pkg/go/doc/reader.go
:reader
構造体にnotes map[string][]string
フィールドを追加 (L.152)bug_markers
とbug_content
の正規表現をnoteMarker
とnoteContent
に置き換え/拡張 (L.400-402)readNote
関数を新規追加 (L.404-416)readFile
関数内でコメントの収集ロジックをreadNote
を使用するように変更し、r.notes
を埋めるように修正 (L.469-481)readPackage
関数でr.notes
マップを初期化 (L.492)
src/pkg/go/doc/testdata/a.0.golden
,src/pkg/go/doc/testdata/a.1.golden
,src/pkg/go/doc/testdata/a.2.golden
:SECBUG
とTODO
のノートセクションを追加 (L.14-20)
src/pkg/go/doc/testdata/a0.go
://TODO(uid): todo0
と// SECBUG(uid): sec hole 0
コメントを追加 (L.9-12)
src/pkg/go/doc/testdata/a1.go
://TODO(uid): todo1
と//TODO(): ignored
コメントを追加 (L.9-12)
src/pkg/go/doc/testdata/template.txt
:Notes
フィールドをレンダリングするためのテンプレートロジックを追加 (L.64-68)
コアとなるコードの解説
このコミットの核心は、go/doc
がコードコメントを解析し、特定のパターンにマッチするものを「ノート」として抽出する能力を汎用化した点にあります。
-
Package.Notes
フィールド:doc.go
に追加されたNotes map[string][]string
は、最終的に生成されるドキュメントデータ構造の一部となります。これにより、go/doc
を利用するアプリケーション(例:godoc
コマンド)は、パッケージ内のすべてのTODO
やSECBUG
などのノートに簡単にアクセスできるようになります。キーがノートの種類(マーカー)を表し、値がその種類のノートのリストであるため、非常に構造化された形で情報が提供されます。 -
reader.notes
フィールドとreadNote
関数:reader.go
に追加されたreader.notes
は、ファイル解析中に一時的にノートを収集するための内部マップです。readNote
関数は、この機能の中核をなすパーシングロジックです。正規表現noteMarker
を使用してコメントの最初の行を解析し、MARKER(userid): comment
のパターンにマッチするかどうかを判断します。マッチした場合、マーカー(例: "TODO")と、MARKER(userid):
の部分を除いた実際のコメント本文を抽出して返します。この関数は、コメントグループ全体をコピーし、最初の行のテキストをコメント本文に置き換えることで、複数行にわたるコメントも正しく処理できるようにしています。 -
readFile
におけるノートの収集ロジック:readFile
関数は、GoのソースファイルをASTとして読み込み、その中のコメントを走査します。各コメントグループに対してreadNote
を呼び出し、有効なノートが返された場合、そのノートをreader.notes
マップに追加します。 特筆すべきは、BUG
マーカーの扱いに関する部分です。if marker == "BUG" { r.bugs = append(r.bugs, text) } else { if r.notes == nil { r.notes = make(map[string][]string) } r.notes[marker] = append(r.notes[marker], text) }
これは、既存の
Package.Bugs
フィールドとの互換性を一時的に維持するためのものです。将来的にはPackage.Bugs
フィールドは削除され、すべてのノート(BUG
を含む)がPackage.Notes
に一元化されることがコメントで示唆されています。これにより、go/doc
の内部構造がよりクリーンで汎用的なものになります。 -
template.txt
の更新: ドキュメント生成のテンプレートが更新されたことで、go doc
コマンドやgodoc
サーバーが、新しいNotes
フィールドの内容をユーザーに表示できるようになります。これにより、開発者がコードに埋め込んだTODO
やSECBUG
などの注釈が、生成されるドキュメントに自動的に反映され、プロジェクトの透明性と管理が向上します。
これらの変更により、go/doc
は単なるAPIドキュメント生成ツールから、コードベース全体の重要な注釈を抽出・集約する、より強力なツールへと進化しました。
関連リンク
- Go言語のドキュメンテーション: https://go.dev/doc/
go/doc
パッケージのドキュメンテーション: https://pkg.go.dev/go/docgo/ast
パッケージのドキュメンテーション: https://pkg.go.dev/go/ast- Go Code Review Comments (BUG コメントに関する記述): https://go.dev/wiki/CodeReviewComments#bug-comments
参考にした情報源リンク
- コミットメッセージ内の CL (Change List) リンク: https://golang.org/cl/7322061 (Goの Gerrit コードレビューシステムへのリンク)