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

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

このコミットは、Go言語の標準ライブラリである go/printer パッケージ内のコード整形ロジックを簡素化し、関連するテストケースを追加するものです。具体的には、exprListMode 型から不要なモードを削除し、exprList 関数の実装を整理することで、コードの可読性と保守性を向上させています。また、複数行にわたる return ステートメントの整形に関するテストケースが追加され、将来的なバグ修正(issue 1207)に備えています。

コミット

commit 6474eda490bbac1d24822e04081ca0f16389ae9d
Author: Robert Griesemer <gri@golang.org>
Date:   Fri Mar 2 11:16:05 2012 -0800

    go/printer: simpler exprList code, more tests
    
    Except for the tests, this is mostly deleting code:
    
    - removed several exprListModes:
      blankStart: easily done explicitly, and trailing blanks
        are cleaned up by the trimmer post-pass
      blankEnd: never used
      commaSep: all exprLists calls had this set
    
    - added test cases for multi-line returns
    (for a later fix of issue 1207)
    
    - no formatting changes
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/5672062

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

https://github.com/golang/go/commit/6474eda490bbac1d24822e04081ca0f16389ae9d

元コミット内容

このコミットの元の内容は以下の通りです。

  • go/printer: exprList コードの簡素化とテストの追加
  • テストを除けば、ほとんどがコードの削除である。
  • いくつかの exprListModes を削除した:
    • blankStart: 明示的に簡単に処理でき、後処理で末尾の空白が除去されるため。
    • blankEnd: 全く使用されていなかったため。
    • commaSep: すべての exprList 呼び出しで常に設定されていたため。
  • 複数行の return ステートメントに関するテストケースを追加した(issue 1207 の将来の修正のため)。
  • フォーマットの変更はない。

変更の背景

この変更の背景には、Go言語のコード整形ツールである go/printer の内部実装の効率化と保守性の向上が挙げられます。go/printer は、Goの抽象構文木(AST)を受け取り、標準的なGoのフォーマット規則に従ってソースコードを生成する役割を担っています。

exprListMode は、式リスト(例えば、関数呼び出しの引数リストや、変数宣言の初期値リストなど)の整形方法を制御するためのフラグの集合でした。しかし、時間の経過とともに、一部のフラグが冗長になったり、常に同じ値に設定されるようになったりしていました。

具体的には、以下の点が問題となっていました。

  • blankStartblankEnd: これらのフラグは、式リストの開始または終了に空白を挿入するかどうかを制御していましたが、これらの空白は明示的に挿入するか、または後処理のトリマーによって適切に処理されるため、不要になっていました。
  • commaSep: このフラグは、式リストの要素がカンマで区切られることを示していましたが、exprList を呼び出すすべての箇所でこのフラグが常に設定されており、実質的に意味をなさなくなっていました。

これらの冗長なフラグを削除することで、exprList 関数の呼び出し側と実装側の両方でコードが簡素化され、理解しやすくなります。また、複数行の return ステートメントの整形に関するテストケースの追加は、既存のバグ(issue 1207)の修正に備えるものであり、将来的なコードの堅牢性を高める目的があります。

前提知識の解説

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

  1. Go言語のAST (Abstract Syntax Tree): Go言語のコンパイラやツールは、ソースコードを直接扱うのではなく、その構造を抽象化した「抽象構文木(AST)」に変換して処理します。go/ast パッケージは、このASTのノード構造を定義しています。go/printer はこのASTを受け取り、整形されたコードを出力します。

  2. go/printer パッケージ: go/printer パッケージは、Goのソースコードを標準的なGoのフォーマット規則に従って整形するためのツールです。gofmt コマンドの基盤としても使用されています。このパッケージは、ASTを走査し、適切なインデント、空白、改行などを挿入して、読みやすいコードを生成します。

  3. token パッケージ: go/token パッケージは、Goのソースコードを構成するトークン(キーワード、識別子、演算子、区切り文字など)とその位置情報(ファイル、行、列)を定義しています。go/printer は、これらのトークン情報を使用して、コードの整形を行います。

  4. exprListexprListMode: exprListgo/printer パッケージ内の内部関数で、式(ast.Expr)のリストを整形する役割を担っています。例えば、関数呼び出しの引数リスト f(a, b, c) や、複合リテラルの要素リスト []int{1, 2, 3} などがこれに該当します。 exprListMode は、exprList 関数の動作を制御するためのビットフラグの列挙型でした。各フラグは、リストの整形に関する特定のオプション(例: カンマ区切り、空白の挿入など)を表していました。

  5. go/printer の整形戦略: go/printer は、単にASTを線形に変換するだけでなく、コードの視覚的な構造を考慮して整形を行います。例えば、行の長さを考慮して自動的に改行を挿入したり、コメントの位置を調整したりします。このコミットで言及されている「trimmer post-pass」は、整形後のコードから不要な空白を除去する後処理ステップを指します。

技術的詳細

このコミットの技術的詳細は、go/printer パッケージ内の exprListMode 列挙型と exprList 関数の変更に集約されます。

exprListMode の簡素化

変更前は、exprListMode に以下のフラグが含まれていました。

type exprListMode uint

const (
	blankStart exprListMode = 1 << iota // print a blank before a non-empty list
	blankEnd                            // print a blank after a non-empty list
	commaSep                            // elements are separated by commas
	commaTerm                           // list is optionally terminated by a comma
	noIndent                            // no extra indentation in multi-line lists
)

このコミットでは、blankStartblankEndcommaSep の3つのフラグが削除されました。

type exprListMode uint

const (
	commaTerm exprListMode = 1 << iota // list is optionally terminated by a comma
	noIndent                           // no extra indentation in multi-line lists
)
  • blankStart の削除: コミットメッセージにあるように、「明示的に簡単に処理でき、後処理で末尾の空白が除去される」ため、このフラグは不要と判断されました。go/printer の内部ロジックで、必要に応じて空白を直接挿入するか、または整形後のトリマーが適切な空白を処理するようになりました。
  • blankEnd の削除: このフラグは「全く使用されていなかった」ため、削除されました。未使用のコードを削除することで、コードベースがクリーンになります。
  • commaSep の削除: このフラグは「すべての exprList 呼び出しで常に設定されていた」ため、冗長でした。これは、式リストの要素が常にカンマで区切られるという go/printer の基本的な整形規則の一部であるため、明示的なフラグとして持つ必要がなくなりました。カンマの挿入は exprList の内部ロジックで常に実行されるようになりました。

これらのフラグの削除により、exprListMode の定義が簡潔になり、exprList を呼び出す際の引数もシンプルになりました。

exprList 関数の変更

exprListMode の変更に伴い、exprList 関数の内部ロジックと、その呼び出し箇所が修正されました。

  • blankStartblankEnd に関連する p.print(blank) の削除: exprList 関数内で mode&blankStart != 0mode&blankEnd != 0 の条件で p.print(blank) を呼び出していた箇所が削除されました。これは、前述の通り、空白の挿入がより直接的な方法で行われるようになったためです。

  • commaSep に関連する条件分岐の削除: exprList 関数内で mode&commaSep != 0 の条件でカンマを挿入していた箇所が削除されました。代わりに、カンマは常に挿入されるようになりました。例えば、単一行のリストを処理する部分では、p.print(x.Pos(), token.COMMA, blank) のように、カンマと空白が常に一緒に挿入されるようになりました。

  • exprList 呼び出し箇所の修正: go/printer/nodes.go 内の exprList を呼び出している複数の箇所で、commaSep フラグが削除されたため、対応する引数が 0(デフォルト値)に変更されました。例えば、commaSep|commaTermcommaTerm に、commaSep0 に変更されています。これにより、呼び出し側のコードも簡素化されています。

テストケースの追加

src/pkg/go/printer/testdata/statements.inputsrc/pkg/go/printer/testdata/statements.golden ファイルに、複数行にわたる return ステートメントの整形に関する新しいテストケースが追加されました。これは、issue 1207(go/printer が複数行の return ステートメントを正しく整形しない問題)の将来的な修正に備えるものです。これらのテストケースは、様々な形式の複数行 return ステートメントが go/printer によってどのように整形されるべきかを示しています。

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

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

  • src/pkg/go/printer/nodes.go: exprListMode の定義変更と、exprList 関数の実装およびその呼び出し箇所の修正。
  • src/pkg/go/printer/testdata/statements.golden: 複数行 return ステートメントの整形に関する期待される出力(ゴールデンファイル)。
  • src/pkg/go/printer/testdata/statements.input: 複数行 return ステートメントの整形に関する入力(テスト入力ファイル)。

具体的な変更行数は以下の通りです。

  • src/pkg/go/printer/nodes.go: 77行の追加、47行の削除
  • src/pkg/go/printer/testdata/statements.golden: 76行の追加
  • src/pkg/go/printer/testdata/statements.input: 76行の追加

コアとなるコードの解説

src/pkg/go/printer/nodes.go の変更

  1. exprListMode の定義変更:

    --- a/src/pkg/go/printer/nodes.go
    +++ b/src/pkg/go/printer/nodes.go
    @@ -82,11 +83,8 @@ func (p *printer) setComment(g *ast.CommentGroup) {
     type exprListMode uint
     
     const (
    -	blankStart exprListMode = 1 << iota // print a blank before a non-empty list
    -	blankEnd                            // print a blank after a non-empty list
    -	commaSep                            // elements are separated by commas
    -	commaTerm                           // list is optionally terminated by a comma
    -	noIndent                            // no extra indentation in multi-line lists
    +	commaTerm exprListMode = 1 << iota // list is optionally terminated by a comma
    +	noIndent                           // no extra indentation in multi-line lists
     )
    

    blankStart, blankEnd, commaSep の3つの定数が削除され、exprListMode が簡素化されました。

  2. identList 関数内の exprList 呼び出しの変更:

    --- a/src/pkg/go/printer/nodes.go
    +++ b/src/pkg/go/printer/nodes.go
    @@ -97,9 +95,9 @@ func (p *printer) identList(list []*ast.Ident, indent bool) {
     	for i, x := range list {
     		xlist[i] = x
     	}
    -	mode := commaSep
    +	var mode exprListMode
     	if !indent {
    -		mode |= noIndent
    +		mode = noIndent
     	}
    -	p.exprList(token.NoPos, xlist, 1, mode, token.NoPos)
    +	p.exprList(token.NoPos, xlist, 1, 0, token.NoPos)
     }
    

    identList 関数内で exprList を呼び出す際に、commaSep が削除されたため、mode の初期化が var mode exprListMode となり、exprList への引数も 0 に変更されました。

  3. exprList 関数内の空白挿入ロジックの削除:

    --- a/src/pkg/go/printer/nodes.go
    +++ b/src/pkg/go/printer/nodes.go
    @@ -116,10 +114,6 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
     		return
     	}\n 
    -\tif mode&blankStart != 0 {\n-\t\tp.print(blank)\n-\t}\n-\n     	prev := p.posFor(prev0)\n     	next := p.posFor(next0)\n     	line := p.lineFor(list[0].Pos())\n    ```
    `blankStart` フラグに基づく空白挿入のロジックが削除されました。
    
    
  4. exprList 関数内のカンマ挿入ロジックの簡素化:

    --- a/src/pkg/go/printer/nodes.go
    +++ b/src/pkg/go/printer/nodes.go
    @@ -129,18 +123,12 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
     		// all list entries on a single line
     		for i, x := range list {
     			if i > 0 {
    -				if mode&commaSep != 0 {\n-					// use position of expression following the comma as\n-					// comma position for correct comment placement\n-					p.print(x.Pos(), token.COMMA)\n-				}\n-				p.print(blank)\n    +				// use position of expression following the comma as\n    +				// comma position for correct comment placement\n    +				p.print(x.Pos(), token.COMMA, blank)\n     			}\n     			p.expr0(x, depth)\n     		}\n    -		if mode&blankEnd != 0 {\n    -			p.print(blank)\n    -		}\n     		return
     	}\n    ```
    単一行のリスト整形において、`commaSep` フラグのチェックが削除され、カンマと空白が常に挿入されるようになりました。また、`blankEnd` フラグに基づく空白挿入のロジックも削除されました。
    
    
  5. 複数行リストのカンマ挿入ロジックの簡素化:

    --- a/src/pkg/go/printer/nodes.go
    +++ b/src/pkg/go/printer/nodes.go
    @@ -212,15 +200,13 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
     
     		if i > 0 {
     			needsLinebreak := prevLine < line && prevLine > 0 && line > 0
    -			if mode&commaSep != 0 {\n-				// use position of expression following the comma as\n-				// comma position for correct comment placement, but\n-				// only if the expression is on the same line\n-				if !needsLinebreak {\n-					p.print(x.Pos())\n-				}\n-				p.print(token.COMMA)\n    +			// use position of expression following the comma as\n    +			// comma position for correct comment placement, but\n    +			// only if the expression is on the same line\n    +			if !needsLinebreak {\n    +				p.print(x.Pos())\n     			}\n    +			p.print(token.COMMA)\n     			needsBlank := true
     			if needsLinebreak {
     				// lines are broken using newlines so comments remain aligned
    

    複数行のリスト整形においても、commaSep フラグのチェックが削除され、カンマが常に挿入されるようになりました。

  6. exprList 呼び出し箇所の修正: CallExpr, CompositeLit, AssignStmt, ReturnStmt, CaseClause, ValueSpec, GenDecl など、exprList を呼び出している様々な箇所で、commaSep フラグが削除されたことに伴い、対応する引数が 0 に変更されています。例えば、commaSep|commaTermcommaTerm に、blankStart|commaSep0 に変更されています。

src/pkg/go/printer/testdata/statements.golden および statements.input の変更

これらのファイルには、複数行の return ステートメントに関する新しいテストケースが追加されています。これにより、go/printer がこれらのケースをどのように整形すべきかという期待される動作が定義され、将来的なバグ修正の検証に役立ちます。

例:

// Formatting of multi-line return statements.
func _f() {
	return
	return x, y, z
	return T{}
	return T{1, 2, 3},
		x, y, z
	return T{1, 2, 3},
		x, y,
		z
	return T{1,
		2,
		3}
	return T{1,
		2,
		3,
	}
	// ... 他の複数行returnのテストケース
}

これらのテストケースは、様々な複雑さを持つ複数行の return ステートメント(構造体リテラル、関数リテラル、複数の戻り値など)を網羅しており、go/printer の堅牢性を高めるのに貢献します。

関連リンク

  • Go issue 1207: gofmt doesn't format multi-line return statements well
  • Go CL 5672062: go/printer: simpler exprList code, more tests

参考にした情報源リンク