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

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

このコミットは、Go言語のgo/astパッケージにおけるCommentGroup.Textメソッドのドキュメントを改善し、関連するテストケースを追加するものです。CommentGroup.Textメソッドは、Goのソースコードからコメントを抽出し、整形されたテキストとして返す機能を提供します。この変更により、コメントのテキスト抽出ロジックがより明確になり、特にgo/docパッケージのようなドキュメント生成ツールが、より正確なコメントテキストを取得できるようになります。

コミット

commit 581e7c2a78bcb3833bc23ac47f77c1b62dab4f40
Author: Robert Griesemer <gri@golang.org>
Date:   Tue May 22 10:30:35 2012 -0700

    go/ast: document CommentGroup.Text and add test case.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/6206096

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

https://github.com/golang/go/commit/581e7c2a78bcb3833bc23ac47f77c1b62dab4f40

元コミット内容

go/ast: document CommentGroup.Text and add test case.

このコミットメッセージは、go/astパッケージ内のCommentGroup.Textメソッドのドキュメントを更新し、その動作を検証するためのテストケースを追加したことを示しています。

変更の背景

Go言語のツールチェインにおいて、ソースコードの抽象構文木(AST)を扱うgo/astパッケージは非常に重要な役割を担っています。特に、コードコメントは単なる注釈ではなく、go docコマンドやGoDocウェブサイトのようなドキュメント生成ツールによって利用される、公式なドキュメントの一部として扱われます。

CommentGroup.Textメソッドは、複数のコメント(Comment構造体のスライス)から構成されるCommentGroupから、整形された単一のテキストを抽出するために使用されます。しかし、元の実装では、コメントマーカー(//, /*, */)の除去や、行コメントの先頭のスペースの扱い、空行の処理などに関して、その動作が不明瞭であったり、期待通りでなかったりする可能性がありました。

このコミットの背景には、CommentGroup.Textメソッドが返すテキストの正確性と一貫性を向上させ、特にドキュメント生成ツールがより高品質な出力を生成できるようにするという目的があります。具体的には、go/docパッケージがCommentGroup.Textを利用してドキュメントコメントを解析するため、このメソッドの挙動が正確であることは、Goの公式ドキュメントの品質に直結します。

また、既存のコードベースでCommentGroup.Textの動作が一部のケースで期待通りでないことが判明した可能性も考えられます。例えば、bignum.RatFromStringのような特定のケースで、行コメントの先頭のスペースの扱いが問題となっていたことが、コミットメッセージのコメントアウトされた部分から示唆されています。このような具体的な問題に対処し、メソッドの堅牢性を高めることも変更の動機となっています。

前提知識の解説

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

  1. Go言語のコメント:

    • 行コメント: // で始まり、行末までがコメントになります。
    • ブロックコメント: /* で始まり、*/ で終わる複数行にわたるコメントです。
    • Goでは、エクスポートされた識別子(関数、変数、型など)の直前にあるコメントは、その識別子のドキュメントコメントとして扱われます。
  2. go/astパッケージ:

    • Go言語のソースコードを抽象構文木(AST: Abstract Syntax Tree)として表現するためのデータ構造と関数を提供します。
    • ASTは、プログラムの構造を木構造で表現したもので、コンパイラ、リンタ、コードフォーマッタ、ドキュメント生成ツールなど、Goのコードを解析・操作する様々なツールで利用されます。
    • ast.File: Goの単一のソースファイルを表すASTのルートノード。
    • ast.Comment: 単一のコメント(// comment または /* comment */)を表す構造体。コメントのテキストと位置情報を含みます。
    • ast.CommentGroup: 連続する複数のコメント(ast.Commentのスライス)を表す構造体。通常、宣言やステートメントの前に現れるドキュメントコメントは、このCommentGroupとして扱われます。
  3. CommentGroup.Text()メソッド:

    • ast.CommentGroup型に定義されたメソッドで、そのコメントグループに含まれるコメントのテキストを整形して返します。
    • このメソッドの目的は、コメントマーカーや余分な空白、空行などを適切に処理し、人間が読みやすい形式のドキュメントテキストを生成することです。
  4. go/docパッケージ:

    • Goのソースコードからドキュメントを抽出・生成するためのパッケージです。
    • go docコマンドやGoDocウェブサイトは、このパッケージを利用してGoの標準ライブラリやユーザーのコードのドキュメントを表示します。
    • go/docは、go/astパッケージによって生成されたASTを解析し、特にCommentGroup.Text()メソッドを利用してドキュメントコメントの内容を取得します。
  5. テスト駆動開発 (TDD) の原則:

    • このコミットでは、既存のメソッドのドキュメントを改善するとともに、その動作を検証するためのテストケースが追加されています。これは、コードの変更が意図した通りに機能することを保証し、将来のリファクタリングや変更によるデグレードを防ぐための重要なプラクティスです。

これらの知識があることで、コミットがgo/astパッケージのどの部分に影響を与え、それがGoのツールチェイン全体にどのような影響を与えるのかを深く理解できます。

技術的詳細

このコミットの技術的詳細は、go/astパッケージのCommentGroup.Textメソッドの動作変更と、その動作を検証するための新しいテストケースの追加に集約されます。

CommentGroup.Textメソッドのドキュメント変更

変更前は、CommentGroup.Textのドキュメントは非常に簡潔でした。

// Text returns the text of the comment,
// with the comment markers - //, /*, and */ - removed.

これに対し、変更後はより詳細かつ正確な説明が追加されています。

// Text returns the text of the comment.
// Comment markers (//, /*, and */), the first space of a line comment, and
// leading and trailing empty lines are removed. Multiple empty lines are
// reduced to one, and trailing space on lines is trimmed. Unless the result
// is empty, it is newline-terminated.

この新しいドキュメントは、以下の重要な動作を明示しています。

  • コメントマーカーの除去: //, /*, */ は除去されます。これは以前からそうでしたが、改めて明記されています。
  • 行コメントの先頭スペースの除去: // の直後にある最初のスペースが除去されます。これは、// foo のようなコメントが foo として抽出されることを保証します。これは特にExampleテスト(Goのドキュメントテスト)で重要です。
  • 先頭および末尾の空行の除去: コメントグループ全体の先頭と末尾にある空行は除去されます。
  • 複数の空行の単一化: 連続する複数の空行は、単一の空行にまとめられます。これにより、ドキュメントコメントの整形が改善されます。
  • 行末のスペースのトリム: 各行の末尾にあるスペースは除去されます。
  • 改行文字による終端: 結果が空でない限り、末尾に改行文字 (\n) が追加されます。これは、複数のコメントが結合された場合に、それぞれのコメントが新しい行で始まるようにするためです。

これらの変更は、go/docパッケージがコメントを解析する際に、より一貫性のある整形されたテキストを取得できるようにすることを目的としています。

CommentGroup.Textメソッドの実装変更

src/pkg/go/ast/ast.goの変更点を見ると、//-style commentの処理部分に修正が加えられています。

--- a/src/pkg/go/ast/ast.go
+++ b/src/pkg/go/ast/ast.go
@@ -104,11 +108,9 @@ func (g *CommentGroup) Text() string {
 		// The parser has given us exactly the comment text.
 		switch c[1] {
 		case '/':
-			//-style comment
+			//-style comment (no newline at the end)
 			c = c[2:]
-			// Remove leading space after //, if there is one.
-			// TODO(gri) This appears to be necessary in isolated
-			//           cases (bignum.RatFromString) - why?
+			// strip first space - required for Example tests
 			if len(c) > 0 && c[0] == ' ' {
 				c = c[1:]
 			}

この変更は、行コメント(//)の処理において、//の直後のスペースを無条件に除去するロジックを明確にしています。以前はTODOコメントでその必要性が疑問視されていましたが、このコミットで「Exampleテストに必要」という理由が明記され、その動作が意図的なものであることが示されています。これは、go testコマンドがExample関数を検出してドキュメントとして表示する際に、コメントの整形が重要であることを示唆しています。

新しいテストケース TestCommentText の追加

src/pkg/go/ast/ast_test.goTestCommentTextという新しいテスト関数が追加されています。このテストは、CommentGroup.Textメソッドの様々な入力に対する期待される出力を網羅的に検証します。

テストデータはcommentsという構造体のスライスで定義されており、各要素は以下の情報を含みます。

  • list []string: CommentGroupを構成する個々のコメント文字列のリスト。これらはComment構造体のTextフィールドとして使用されます。
  • text string: CommentGroup.Text()メソッドが返すことが期待される整形済みテキスト。

テストケースの例:

  • {[]string{"//"}, ""}: 空の行コメントは空文字列になる。
  • {[]string{"// "}, ""}: スペースのみの行コメントも空文字列になる。
  • {[]string{"// foo "}, "foo\n"}: 行末のスペースがトリムされ、末尾に改行が追加される。
  • {[]string{"// foo", "// bar"}, "foo\nbar\n"}: 複数の行コメントが結合され、それぞれが改行で区切られる。
  • {[]string{"// foo", "//", "//", "//", "// bar"}, "foo\n\nbar\n"}: 複数の空行が単一の空行にまとめられる。
  • {[]string{"/* Foo */"}, " Foo\n"}: ブロックコメントのマーカーが除去され、末尾に改行が追加される。
  • {[]string{"/* Foo*/", "/**/", "/**/", "/**/", "// Bar"}, " Foo\n\nBar\n"}: ブロックコメントと行コメントが混在し、空コメントが適切に処理され、複数の空行が単一化される。

これらのテストケースは、CommentGroup.Textメソッドがドキュメントで説明されているすべての整形ルール(マーカー除去、スペーストリム、空行処理、改行終端)を正しく適用していることを確認します。これにより、メソッドの動作が明確になり、将来の変更に対する回帰テストとしても機能します。

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

このコミットにおけるコアとなるコードの変更箇所は以下の2つのファイルです。

  1. src/pkg/go/ast/ast.go:

    • CommentGroup構造体のText()メソッドのドキュメントコメントが更新されました。
    • Text()メソッドの実装内で、行コメント(//スタイル)の処理ロジックが微修正され、//の直後のスペースを削除する意図が明確化されました。
  2. src/pkg/go/ast/ast_test.go:

    • TestCommentTextという新しいテスト関数が追加されました。
    • このテスト関数は、CommentGroup.Text()メソッドの様々な入力パターンと期待される出力パターンを定義したcommentsというテストデータスライスを含んでいます。
    • テストループ内で、定義されたlistからCommentGroupを構築し、Text()メソッドを呼び出し、その結果が期待されるtextと一致するかを検証しています。

コアとなるコードの解説

src/pkg/go/ast/ast.go の変更点

// Text returns the text of the comment.
// Comment markers (//, /*, and */), the first space of a line comment, and
// leading and trailing empty lines are removed. Multiple empty lines are
// reduced to one, and trailing space on lines is trimmed. Unless the result
// is empty, it is newline-terminated.
func (g *CommentGroup) Text() string {
    // ... (既存のコード) ...
    switch c[1] {
    case '/':
        //-style comment (no newline at the end)
        c = c[2:]
        // strip first space - required for Example tests
        if len(c) > 0 && c[0] == ' ' {
            c = c[1:]
        }
    // ... (既存のコード) ...
}
  • ドキュメントの更新: 最も重要な変更は、CommentGroup.Text()メソッドのドキュメントコメントが大幅に詳細化された点です。これにより、このメソッドがどのようなルールでコメントテキストを整形するのかが明確になりました。特に、「行コメントの最初のスペースの除去」「先頭と末尾の空行の除去」「複数の空行の単一化」「行末のスペースのトリム」「結果が空でない場合の改行終端」といった具体的な動作が明記されています。これは、このメソッドのAPIとしての契約を明確にする上で非常に重要です。
  • 行コメント処理の明確化: switch c[1]ブロック内のcase '/'(行コメント処理)において、//の直後のスペースを削除するロジックのコメントが// strip first space - required for Example testsに変更されました。これは、この特定のスペース除去がGoのExampleテスト機能(go docで表示されるコード例)の整形要件を満たすために必要であることを示しています。これにより、以前のTODOコメントで示唆されていた不明瞭さが解消され、この動作が意図的なものであることが確認されました。

src/pkg/go/ast/ast_test.go の追加点

package ast

import (
	"testing"
)

var comments = []struct {
	list []string
	text string
}{
	// ... (多数のテストケース) ...
	{[]string{"// foo", "//", "//", "//", "// bar"}, "foo\n\nbar\n"},
	{[]string{"/* Foo*/", "/**/", "/**/", "/**/", "// Bar"}, " Foo\n\nBar\n"},
	// ...
}

func TestCommentText(t *testing.T) {
	for i, c := range comments {
		list := make([]*Comment, len(c.list))
		for i, s := range c.list {
			list[i] = &Comment{Text: s}
		}

		text := (&CommentGroup{list}).Text()
		if text != c.text {
			t.Errorf("case %d: got %q; expected %q", i, text, c.text)
		}
	}
}
  • commentsテストデータ: CommentGroup.Text()メソッドの様々な入力パターンと、それに対応する期待される出力テキストを定義した構造体のスライスです。これにより、単体テストがデータ駆動型で実行され、多様なシナリオ(空コメント、スペースのみのコメント、複数行コメント、ブロックコメントと行コメントの混在、空行の処理など)を網羅的にテストできます。
  • TestCommentText関数:
    • forループでcommentsスライスをイテレートし、各テストケースを実行します。
    • 各テストケースのlist(コメント文字列のスライス)から、*Commentのスライスを作成し、それをCommentGroupListフィールドに設定します。
    • 作成したCommentGroupインスタンスに対してText()メソッドを呼び出し、結果をtext変数に格納します。
    • textが期待されるc.textと一致しない場合、t.Errorfを呼び出してテスト失敗を報告します。これにより、どのテストケースでどのような不一致が発生したかが明確に示されます。

この新しいテストケースは、CommentGroup.Text()メソッドの動作が、新しいドキュメントで記述された仕様と一致していることを保証するための重要な回帰テストとして機能します。これにより、将来の変更がこのメソッドの既存の動作を破壊しないことが確認できます。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Goのソースコードリポジトリ (GitHub)
  • Goのコードレビューシステム (Gerrit) - コミットメッセージに記載されているhttps://golang.org/cl/6206096は、このコミットのGerritレビューへのリンクです。
  • GoのExampleテストに関する情報
  • 抽象構文木 (AST) に関する一般的な情報
  • テスト駆動開発 (TDD) に関する一般的な情報
  • Goのコメントの慣習に関する情報