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

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

このコミットは、Go言語のドキュメンテーションツールであるgodocにおけるExample関数の出力表示方法に関する重要な変更を導入しています。具体的には、Example関数の期待される出力の記述方法を、従来のExample関数直前のコメントから、関数本体の末尾に// Output:というプレフィックスを付けたコメントとして記述する新しい規約へと移行しています。これにより、Exampleコードの可読性が向上し、特にファイル全体をExampleとして扱う場合に、出力がコードの自然な流れの中に組み込まれるようになります。

コミット

commit 11e113db573369e1fd5f92844269d31ae3815c25
Author: Andrew Gerrand <adg@golang.org>
Date:   Thu Feb 16 11:50:28 2012 +1100

    godoc: make example code more readable with new comment convention
    go/doc: move Examples to go/ast
    cmd/go: use go/doc to read examples
    src/pkg: update examples to use new convention
    
    This is to make whole file examples more readable. When presented as a
    complete function, preceding an Example with its output is confusing.
    The new convention is to put the expected output in the final comment
    of the example, preceded by the string "output:" (case insensitive).
    
    An idiomatic example looks like this:
    
    // This example demonstrates Foo by doing bar and quux.
    func ExampleFoo() {
            // example body that does bar and quux
    
            // Output:
            // example output
    }
    
    R=rsc, gri
    CC=golang-dev
    https://golang.org/cl/5673053

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

https://github.com/golang/go/commit/11e113db573369e1fd5f92844269d31ae3815c25

元コミット内容

このコミットの元の内容は、Go言語のgodocツールにおけるExampleコードの表示方法を改善することに焦点を当てています。主な変更点は以下の通りです。

  1. 新しいコメント規約の導入: Example関数の期待される出力を、関数本体の最後のコメントとして記述する新しい規約を導入しました。このコメントは// Output:(大文字・小文字を区別しない)で始まり、その後に期待される出力が続きます。
  2. go/docからgo/astへのExamples関数の移動: Example関数を解析するロジックが、go/docパッケージからgo/astパッケージへ移動されました。これにより、Exampleの解析がより低レベルのAST(抽象構文木)レベルで行われるようになり、より柔軟な処理が可能になります。
  3. cmd/goの更新: go testコマンドがExampleを読み込む際に、新しいgo/astパッケージの機能を利用するように更新されました。
  4. 既存のExampleコードの更新: src/pkg以下の既存のExampleコードが、新しいコメント規約に準拠するように修正されました。

この変更の目的は、特にファイル全体をExampleとして表示する場合に、Exampleコードの可読性を高めることです。従来の方式では、Example関数の前に出力コメントが置かれていたため、コードと出力の関連性が分かりにくくなることがありました。新しい規約により、出力がコードの実行結果として自然に配置され、理解しやすくなります。

変更の背景

Go言語のgodocツールは、ソースコードから直接ドキュメンテーションを生成する強力な機能を提供しています。その中でも、Example関数は、コードの具体的な使用例を示すために非常に有用な機能です。Example関数は、Exampleというプレフィックスを持つ関数として定義され、go testコマンドによって実行され、その出力がgodocによってドキュメンテーションに組み込まれます。

このコミットが行われた2012年2月時点では、Example関数の期待される出力は、Example関数の直前のコメントブロックに記述されていました。例えば、以下のような形式です。

// hello
func ExampleHello() {
    fmt.Println("hello")
}

この形式は、短いExampleでは問題ありませんでしたが、Example関数がファイル全体をカバーするような、より複雑なシナリオでは問題が生じました。特に、godocがExampleコードを表示する際に、Example関数全体がそのまま表示されるため、その前に置かれた出力コメントが、あたかもExampleコードの一部であるかのように見えてしまい、混乱を招く可能性がありました。

このコミットの背景には、このような「ファイル全体Example」の可読性を向上させたいという明確な意図がありました。開発者は、Exampleコードとそれに対応する出力がより自然な形で関連付けられることを望んでいました。これにより、ユーザーがドキュメンテーションを読んだ際に、Exampleコードが何を行い、どのような結果を生成するのかを直感的に理解できるようになります。

また、go/docパッケージがExampleの解析を担当していましたが、より低レベルでASTを直接操作できるgo/astパッケージにExample解析ロジックを移動することで、将来的な拡張性や柔軟性を確保するという技術的な背景もありました。

前提知識の解説

このコミットを理解するためには、以下のGo言語の概念とツールに関する知識が役立ちます。

1. Go言語のExample関数

Go言語では、パッケージの使用例を示すために特別な関数を記述できます。これらの関数は「Example関数」と呼ばれ、以下の命名規則に従います。

  • func Example(): パッケージ全体のExample。
  • func ExampleF(): 関数FのExample。
  • func ExampleT(): 型TのExample。
  • func ExampleT_M(): 型TのメソッドMのExample。

Example関数は、通常_test.goファイル内に記述されます。go testコマンドを実行すると、Example関数も実行され、その標準出力がキャプチャされます。

2. godocツール

godocは、Go言語のソースコードからドキュメンテーションを生成し、Webブラウザで表示するためのツールです。godocは、パッケージのコメント、関数、型、変数、定数などの定義を解析し、それらを整形して表示します。Example関数が記述されている場合、godocはそのExampleコードと、go testによってキャプチャされた出力をドキュメンテーションに含めます。

3. go testコマンド

go testコマンドは、Goパッケージのテストを実行するための主要なツールです。これには、ユニットテスト、ベンチマークテスト、そしてExample関数の実行が含まれます。Example関数が実行される際、その標準出力はキャプチャされ、Example関数のコメントに記述された期待される出力と比較されます。一致しない場合、テストは失敗します。

4. go/astパッケージ

go/astパッケージは、Go言語のソースコードの抽象構文木(AST: Abstract Syntax Tree)を表現するためのデータ構造と関数を提供します。ASTは、ソースコードの構造を木構造で表現したもので、コンパイラやツールがコードを解析するために使用します。このパッケージを使用することで、Goのコードをプログラム的に検査、変更、生成することができます。

5. go/docパッケージ

go/docパッケージは、go/astパッケージによって生成されたASTから、Goパッケージのドキュメンテーションを抽出するための機能を提供します。このパッケージは、コメント、宣言、Example関数などを解析し、それらを構造化されたドキュメンテーションデータとして提供します。

6. コメントの役割

Go言語では、コメントは単なる説明文以上の役割を果たすことがあります。特に、パッケージ、関数、型、変数などの宣言の直前に記述されたコメントは、godocによってドキュメンテーションとして扱われます。Example関数の出力コメントも、このドキュメンテーション生成プロセスの一部として特別に扱われます。

技術的詳細

このコミットの技術的な変更は、主に以下の3つの領域にわたります。

1. Example出力の新しい解析ロジック

最も重要な変更は、Example関数の期待される出力を解析する方法です。 従来のgo/docパッケージでは、Example関数のDocフィールド(Example関数宣言の直前のコメント)から出力を取得していました。

新しいアプローチでは、go/astパッケージに移動されたExamples関数内で、Example関数の本体内のコメントを走査し、// Output:で始まる最後のコメントを探します。

  • src/pkg/go/ast/example.go:
    • Example構造体にCodeフィールド(ast.Node型)とCommentsフィールド([]*ast.CommentGroup型)が追加されました。これにより、Exampleのコード本体と関連するコメントグループを直接参照できるようになります。
    • Examples関数は、*ast.Packageではなく、可変引数...*ast.Fileを受け取るように変更されました。これにより、個々のファイルからExampleを抽出する柔軟性が向上します。
    • exampleOutputという新しいヘルパー関数が導入されました。この関数は、*ast.FuncDecl(Example関数)と関連するコメントグループを受け取り、関数本体内のコメントを逆順に走査します。outputPrefix = regexp.MustCompile((?i)^[[:space:]]*output:)という正規表現を使用して、// Output:(大文字・小文字を区別しない)で始まるコメントを探し、その後のテキストをExampleの出力として抽出します。

2. go/docからgo/astへのExample解析ロジックの移動

  • src/pkg/go/doc/example.goからsrc/pkg/go/ast/example.goへのファイル移動: Example解析のコアロジックがgo/docパッケージからgo/astパッケージに移動されました。これは、Exampleの解析がASTの構造に密接に関連しているため、より低レベルのASTパッケージに配置することが適切であるという設計判断に基づいています。これにより、go/astがExampleの構造を直接理解し、go/docはそれを消費する役割に特化できるようになります。
  • go/docパッケージ内のExamples関数は削除され、代わりにgo/ast.Examplesを呼び出すように変更されました。

3. godoccmd/goの更新

  • src/cmd/godoc/godoc.go:
    • example_htmlFunc関数のシグネチャが変更され、[]*doc.Exampleの代わりに[]*ast.Exampleを受け取るようになりました。
    • Exampleコードの表示ロジックが更新され、eg.Codeeg.Commentsを使用して、新しいコメント規約に基づいて出力を適切に処理するようになりました。特に、exampleOutputRxという正規表現(regexp.MustCompile((?i)//[[:space:]]*output:))を使用して、Exampleコード内の// Output:コメントを検出し、その部分をHTML出力から除外する処理が追加されました。これにより、godocのWebページ上でExampleコードが表示される際に、// Output:行が重複して表示されるのを防ぎます。
  • src/cmd/go/test.go:
    • testFuncs.load関数内で、Example関数の解析にast.Examples(f)を使用するように変更されました。これにより、go testコマンドがExampleを実行する際に、新しいgo/astパッケージのExample解析ロジックが利用されるようになります。
    • Exampleの出力が空の場合に実行しないというロジックは維持されていますが、出力の取得元がf.Doc.Text()からe.Outputast.Example構造体から取得)に変更されました。

これらの変更により、GoのExampleシステム全体が、新しい出力コメント規約に準拠し、より堅牢で一貫性のある動作をするようになりました。

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

このコミットにおけるコアとなるコードの変更は、主に以下のファイルに集中しています。

  1. src/pkg/go/{doc => ast}/example.go (ファイル移動と内容変更):

    • ファイルがsrc/pkg/go/doc/example.goからsrc/pkg/go/ast/example.goに移動されました。
    • Example構造体の定義が変更され、Body *printer.CommentedNodeCode NodeComments []*CommentGroupに分割されました。
    • Examples関数のシグネチャがpkg *ast.Packageからfiles ...*Fileに変更されました。
    • exampleOutputという新しい関数が追加され、Example関数の本体内のコメントから// Output:で始まる行を解析して出力を抽出するロジックが実装されました。
    --- a/src/pkg/go/doc/example.go
    +++ b/src/pkg/go/ast/example.go
    @@ -2,37 +2,37 @@
     // Use of this source code is governed by a BSD-style
     // license that can be found in the LICENSE file.
     
    -// Extract example functions from package ASTs.
    +// Extract example functions from file ASTs.
     
    -package doc
    +package ast
     
     import (
    -	"go/ast"
    -	"go/printer"
     	"go/token"
    +	"regexp"
     	"strings"
     	"unicode"
     	"unicode/utf8"
     )
     
     type Example struct {
    -	Name   string                 // name of the item being demonstrated
    -	Body   *printer.CommentedNode // code
    -	Output string                 // expected output
    +	Name     string // name of the item being exemplified
    +	Code     Node
    +	Comments []*CommentGroup
    +	Output   string // expected output
     }
     
    -func Examples(pkg *ast.Package) []*Example {
    +func Examples(files ...*File) []*Example {
     	var list []*Example
    -	for _, file := range pkg.Files {
    +	for _, file := range files {
     	\thasTests := false // file contains tests or benchmarks
     	\tnumDecl := 0      // number of non-import declarations in the file
     	\tvar flist []*Example
     	\tfor _, decl := range file.Decls {
    -	\t\tif g, ok := decl.(*ast.GenDecl); ok && g.Tok != token.IMPORT {
    +	\t\tif g, ok := decl.(*GenDecl); ok && g.Tok != token.IMPORT {
     	\t\t\tnumDecl++
     	\t\t\tcontinue
     	\t\t}\n-	\t\tf, ok := decl.(*ast.FuncDecl)
    +	\t\tf, ok := decl.(*FuncDecl)
     	\t\tif !ok {
     	\t\t\tcontinue
     	\t\t}\n@@ -46,25 +46,47 @@ func Examples(pkg *ast.Package) []*Example {
     	\t\t\t\tcontinue
     	\t\t\t}\n     	\t\tflist = append(flist, &Example{\n-	\t\t\t\tName: name[len("Example"):],\n-	\t\t\t\tBody: &printer.CommentedNode{\n-	\t\t\t\t\tNode:     f.Body,\n-	\t\t\t\t\tComments: file.Comments,\n-	\t\t\t\t},\n-	\t\t\t\tOutput: f.Doc.Text(),
    +	\t\t\t\tName:     name[len("Example"):],\n+	\t\t\t\tCode:     f.Body,\n+	\t\t\t\tComments: file.Comments,\n+	\t\t\t\tOutput:   exampleOutput(f, file.Comments),\n     	\t\t})\n     	\t}\n     	\tif !hasTests && numDecl > 1 && len(flist) == 1 {\n     	\t\t// If this file only has one example function, some\n     	\t\t// other top-level declarations, and no tests or\n     	\t\t// benchmarks, use the whole file as the example.\n-	\t\t\tflist[0].Body.Node = file
    +	\t\t\tflist[0].Code = file
     	\t\t}\n     	\tlist = append(list, flist...)\n     	}\n     	return list\n     }\n     \n    +var outputPrefix = regexp.MustCompile(`(?i)^[[:space:]]*output:`)\n    +\n    +func exampleOutput(fun *FuncDecl, comments []*CommentGroup) string {\n    +\t// find the last comment in the function\n    +\tvar last *CommentGroup\n    +\tfor _, cg := range comments {\n    +\t\tif cg.Pos() < fun.Pos() {\n    +\t\t\tcontinue\n    +\t\t}\n    +\t\tif cg.End() > fun.End() {\n    +\t\t\tbreak\n    +\t\t}\n    +\t\tlast = cg\n    +\t}\n    +\tif last != nil {\n    +\t\t// test that it begins with the correct prefix\n    +\t\ttext := last.Text()\n    +\t\tif loc := outputPrefix.FindStringIndex(text); loc != nil {\n    +\t\t\treturn strings.TrimSpace(text[loc[1]:])\n    +\t\t}\n    +\t}\n    +\treturn "" // no suitable comment found\n    +}\n    +\n     // isTest tells whether name looks like a test, example, or benchmark.\n     // It is a Test (say) if there is a character after Test that is not a\n     // lower-case letter. (We don't want Testiness.)
    
  2. src/cmd/godoc/godoc.go (Example表示ロジックの更新):

    • example_htmlFunc関数がast.Example型を受け取るように変更されました。
    • Exampleコードから// Output:コメントを削除するための正規表現exampleOutputRxが追加され、HTML生成時にこのコメントが重複して表示されないように処理が加えられました。
    --- a/src/cmd/godoc/godoc.go
    +++ b/src/cmd/godoc/godoc.go
    @@ -499,7 +499,9 @@ func startsWithUppercase(s string) bool {
     	return unicode.IsUpper(r)
     }
     
    -func example_htmlFunc(funcName string, examples []*doc.Example, fset *token.FileSet) string {
    +var exampleOutputRx = regexp.MustCompile(`(?i)//[[:space:]]*output:`)
    +
    +func example_htmlFunc(funcName string, examples []*ast.Example, fset *token.FileSet) string {
     	var buf bytes.Buffer
     	for _, eg := range examples {
     	\tname := eg.Name
    @@ -517,16 +519,28 @@ func example_htmlFunc(funcName string, examples []*doc.Example, fset *token.File
     	\t}\n     \n     	\t// print code\n    -	\tcode := node_htmlFunc(eg.Body, fset)
    +	\tcnode := &printer.CommentedNode{Node: eg.Code, Comments: eg.Comments}
    +	\tcode := node_htmlFunc(cnode, fset)
    +	\tout := eg.Output
    +\n    +	\t// additional formatting if this is a function body
     	\tif len(code) > 0 && code[0] == '{' {
    -	\t\t// unindent and remove surrounding braces
    +	\t\t// unindent
     	\t\tcode = strings.Replace(code, "\n    ", "\n", -1)
    +	\t\t// remove surrounding braces
     	\t\tcode = code[2 : len(code)-2]
    +	\t\t// remove output comment
    +	\t\tif loc := exampleOutputRx.FindStringIndex(code); loc != nil {
    +	\t\t\tcode = strings.TrimSpace(code[:loc[0]])
    +	\t\t}\n    +	\t} else {\n    +	\t\t// drop output, as the output comment will appear in the code
    +	\t\tout = ""
     	\t}\n     \n     	\terr := exampleHTML.Execute(&buf, struct {\n     	\t\tName, Code, Output string\n    -	\t}{eg.Name, code, eg.Output})
    +	\t}{eg.Name, code, out})
     	\tif err != nil {
     	\t\tlog.Print(err)
     	\t}\n@@ -552,7 +566,6 @@ func example_nameFunc(s string) string {
     func example_suffixFunc(name string) string {
     	_, suffix := splitExampleName(name)
     	return suffix
    -\n     }
     
     func splitExampleName(s string) (name, suffix string) {
    @@ -966,7 +979,7 @@ type PageInfo struct {
     	FSet     *token.FileSet // corresponding file set
     	PAst     *ast.File      // nil if no single AST with package exports
     	PDoc     *doc.Package   // nil if no single package documentation
    -	Examples []*doc.Example // nil if no example code
    +	Examples []*ast.Example // nil if no example code
     	Dirs     *DirList       // nil if no directory information
     	DirTime  time.Time      // directory time stamp
     	DirFlat  bool           // if set, show directory in a flat (non-indented) manner
    @@ -1115,7 +1128,7 @@ func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, mode PageInf
     	}\n     \n     	// get examples from *_test.go files\n    -	var examples []*doc.Example
    +	var examples []*ast.Example
     	filter = func(d os.FileInfo) bool {
     	\treturn isGoFile(d) && strings.HasSuffix(d.Name(), "_test.go")
     	}\n@@ -1123,7 +1136,11 @@ func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, mode PageInf
     	\tlog.Println("parsing test files:", err)
     	} else {\n     	\tfor _, testpkg := range testpkgs {\n    -	\t\texamples = append(examples, doc.Examples(testpkg)...)
    +	\t\tvar files []*ast.File
    +	\t\tfor _, f := range testpkg.Files {
    +	\t\t\tfiles = append(files, f)
    +	\t\t}\n    +	\t\texamples = append(examples, ast.Examples(files...)...)
     	\t}\n     	}\n     
    
  3. src/cmd/go/test.go (Example読み込みロジックの更新):

    • testFuncs.load関数内で、Exampleの読み込みにast.Examples(f)が使用されるようになりました。これにより、go testがExampleの出力を解析する際に、新しい規約が適用されます。
    --- a/src/cmd/go/test.go
    +++ b/src/cmd/go/test.go
    @@ -183,9 +183,9 @@ where xxx is a suffix not beginning with an upper case letter.
      
      Here is an example of an example:
      
    -	// The output of this example function.
      	func ExamplePrintln() {
      		Println("The output of this example function.")
    +		// Output: The output of this example function.
      	}
      
      The entire test file is presented as the example when it contains a single
    @@ -717,17 +717,16 @@ func (t *testFuncs) load(filename, pkg string, seen *bool) error {
     		case isTest(name, "Benchmark"):
     			t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, ""})
     			*seen = true
    -		case isTest(name, "Example"):
    -			output := n.Doc.Text()
    -			if output == "" {
    -				// Don't run examples with no output.
    -				continue
    -			}
    -			t.Examples = append(t.Examples, testFunc{pkg, name, output})
    -			*seen = true
     		}
     	}
    -\n    +	for _, e := range ast.Examples(f) {
    +	if e.Output == "" {
    +		// Don't run examples with no output.
    +		continue
    +	}
    +	t.Examples = append(t.Examples, testFunc{pkg, "Example" + e.Name, e.Output})
    +	*seen = true
    +	}
     	return nil
     }
     
    
  4. src/pkg/testing/testing.go (Exampleドキュメントの更新):

    • testingパッケージのドキュメントが更新され、新しい// Output:コメント規約が説明されました。
    --- a/src/pkg/testing/testing.go
    +++ b/src/pkg/testing/testing.go
    @@ -38,16 +38,25 @@
     //         }\n     //     }\n     //\n    -// The package also runs and verifies example code. Example functions\n    -// include an introductory comment that is compared with the standard output\n    -// of the function when the tests are run, as in this example of an example:\n    +// The package also runs and verifies example code. Example functions may\n    +// include a concluding comment that begins with "Output:" and is compared with\n    +// the standard output of the function when the tests are run, as in these\n    +// examples of an example:\n     //\n    -//     // hello
     //     func ExampleHello() {
     //             fmt.Println("hello")
    +//             // Output: hello
     //     }\n     //\n    -// Example functions without comments are compiled but not executed.
    +//     func ExampleSalutations() {
    +//             fmt.Println("hello, and")
    +//             fmt.Println("goodbye")
    +//             // Output:\n    +//             // hello, and\n    +//             // goodbye\n    +//     }\n    +//\n    +// Example functions without output comments are compiled but not executed.
     //
     // The naming convention to declare examples for a function F, a type T and\n     // method M on type T are:
    

これらの変更は、GoのExampleシステムがどのようにExampleコードを解析し、その出力をドキュメンテーションに組み込むかという、その根幹部分に影響を与えています。

コアとなるコードの解説

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

このファイルは、Example関数の解析ロジックの新しい本拠地となります。

  • Example構造体:

    type Example struct {
        Name     string // name of the item being exemplified
        Code     Node
        Comments []*CommentGroup
        Output   string // expected output
    }
    

    Codeフィールドは、Example関数の本体(またはファイル全体Exampleの場合はファイル全体)のASTノードを保持します。Commentsフィールドは、そのExampleに関連するすべてのコメントグループを保持します。これにより、exampleOutput関数がExample関数内のコメントを効率的に検索できるようになります。

  • Examples関数:

    func Examples(files ...*File) []*Example {
        // ...
        flist = append(flist, &Example{
            Name:     name[len("Example"):],
            Code:     f.Body, // Example関数の本体をCodeとして設定
            Comments: file.Comments, // ファイル全体のコメントを渡す
            Output:   exampleOutput(f, file.Comments), // 新しいヘルパー関数で出力を抽出
        })
        // ...
    }
    

    この関数は、与えられたGoのASTファイルからExample関数を抽出し、Example構造体のスライスとして返します。重要なのは、OutputフィールドがexampleOutputヘルパー関数によって設定される点です。

  • exampleOutput関数:

    var outputPrefix = regexp.MustCompile(`(?i)^[[:space:]]*output:`)
    
    func exampleOutput(fun *FuncDecl, comments []*CommentGroup) string {
        var last *CommentGroup
        for _, cg := range comments {
            if cg.Pos() < fun.Pos() { // Example関数の開始位置より前のコメントはスキップ
                continue
            }
            if cg.End() > fun.End() { // Example関数の終了位置より後のコメントはスキップ
                break
            }
            last = cg // Example関数内のコメントを順に取得
        }
        if last != nil {
            text := last.Text()
            if loc := outputPrefix.FindStringIndex(text); loc != nil {
                return strings.TrimSpace(text[loc[1]:]) // "output:"以降のテキストを抽出
            }
        }
        return "" // 適切なコメントが見つからない場合は空文字列
    }
    

    この関数は、Example関数のASTノードと、そのファイル内のすべてのコメントグループを受け取ります。Example関数内のコメントのみを対象とし、その中で最後に現れる// Output:で始まるコメントを探します。正規表現outputPrefixは、大文字・小文字を区別せずにoutput:という文字列を検出します。検出された場合、そのプレフィックス以降のテキストがExampleの期待される出力として返されます。

src/cmd/godoc/godoc.go の変更

このファイルは、godocがExampleをHTMLとして表示する方法を制御します。

  • example_htmlFunc関数:
    var exampleOutputRx = regexp.MustCompile(`(?i)//[[:space:]]*output:`)
    
    func example_htmlFunc(funcName string, examples []*ast.Example, fset *token.FileSet) string {
        // ...
        if len(code) > 0 && code[0] == '{' {
            // ...
            if loc := exampleOutputRx.FindStringIndex(code); loc != nil {
                code = strings.TrimSpace(code[:loc[0]]) // Outputコメントをコードから削除
            }
        } else {
            // drop output, as the output comment will appear in the code
            out = "" // ファイル全体Exampleの場合、Outputコメントはコード内に含まれるため、別途出力しない
        }
        // ...
    }
    
    exampleOutputRxは、exampleOutput関数で使用されるoutputPrefixとほぼ同じ正規表現ですが、こちらはgodocのHTML出力から// Output:コメント行を削除するために使用されます。これにより、ExampleコードがHTMLとして表示される際に、// Output:行がコードの一部として表示されず、重複を避けることができます。ファイル全体Exampleの場合、out変数を空にすることで、godocがExampleの出力を別途表示するのではなく、コード内のコメントとしてのみ表示されるようにします。

src/cmd/go/test.go の変更

このファイルは、go testコマンドがExampleを実行する方法を制御します。

  • testFuncs.load関数:
    func (t *testFuncs) load(filename, pkg string, seen *bool) error {
        // ...
        for _, e := range ast.Examples(f) { // ast.Examplesを使用
            if e.Output == "" {
                // Don't run examples with no output.
                continue
            }
            t.Examples = append(t.Examples, testFunc{pkg, "Example" + e.Name, e.Output})
            *seen = true
        }
        return nil
    }
    
    go testは、Example関数を読み込む際に、新しいast.Examples関数を使用するようになりました。これにより、go testも新しい// Output:コメント規約に従ってExampleの出力を取得し、テストの比較に使用します。

これらの変更により、GoのExampleシステムは、Exampleコードの可読性を向上させ、より自然な形で出力と関連付けられるようになりました。

関連リンク

参考にした情報源リンク