[インデックス 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 コードレビューシステムへのリンク)