[インデックス 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
は、式リスト(例えば、関数呼び出しの引数リストや、変数宣言の初期値リストなど)の整形方法を制御するためのフラグの集合でした。しかし、時間の経過とともに、一部のフラグが冗長になったり、常に同じ値に設定されるようになったりしていました。
具体的には、以下の点が問題となっていました。
blankStart
とblankEnd
: これらのフラグは、式リストの開始または終了に空白を挿入するかどうかを制御していましたが、これらの空白は明示的に挿入するか、または後処理のトリマーによって適切に処理されるため、不要になっていました。commaSep
: このフラグは、式リストの要素がカンマで区切られることを示していましたが、exprList
を呼び出すすべての箇所でこのフラグが常に設定されており、実質的に意味をなさなくなっていました。
これらの冗長なフラグを削除することで、exprList
関数の呼び出し側と実装側の両方でコードが簡素化され、理解しやすくなります。また、複数行の return
ステートメントの整形に関するテストケースの追加は、既存のバグ(issue 1207)の修正に備えるものであり、将来的なコードの堅牢性を高める目的があります。
前提知識の解説
このコミットを理解するためには、以下のGo言語および go/printer
パッケージに関する基本的な知識が必要です。
-
Go言語のAST (Abstract Syntax Tree): Go言語のコンパイラやツールは、ソースコードを直接扱うのではなく、その構造を抽象化した「抽象構文木(AST)」に変換して処理します。
go/ast
パッケージは、このASTのノード構造を定義しています。go/printer
はこのASTを受け取り、整形されたコードを出力します。 -
go/printer
パッケージ:go/printer
パッケージは、Goのソースコードを標準的なGoのフォーマット規則に従って整形するためのツールです。gofmt
コマンドの基盤としても使用されています。このパッケージは、ASTを走査し、適切なインデント、空白、改行などを挿入して、読みやすいコードを生成します。 -
token
パッケージ:go/token
パッケージは、Goのソースコードを構成するトークン(キーワード、識別子、演算子、区切り文字など)とその位置情報(ファイル、行、列)を定義しています。go/printer
は、これらのトークン情報を使用して、コードの整形を行います。 -
exprList
とexprListMode
:exprList
はgo/printer
パッケージ内の内部関数で、式(ast.Expr
)のリストを整形する役割を担っています。例えば、関数呼び出しの引数リストf(a, b, c)
や、複合リテラルの要素リスト[]int{1, 2, 3}
などがこれに該当します。exprListMode
は、exprList
関数の動作を制御するためのビットフラグの列挙型でした。各フラグは、リストの整形に関する特定のオプション(例: カンマ区切り、空白の挿入など)を表していました。 -
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
)
このコミットでは、blankStart
、blankEnd
、commaSep
の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
関数の内部ロジックと、その呼び出し箇所が修正されました。
-
blankStart
とblankEnd
に関連するp.print(blank)
の削除:exprList
関数内でmode&blankStart != 0
やmode&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|commaTerm
はcommaTerm
に、commaSep
は0
に変更されています。これにより、呼び出し側のコードも簡素化されています。
テストケースの追加
src/pkg/go/printer/testdata/statements.input
と src/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
の変更
-
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
が簡素化されました。 -
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
に変更されました。 -
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` フラグに基づく空白挿入のロジックが削除されました。
-
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` フラグに基づく空白挿入のロジックも削除されました。
-
複数行リストのカンマ挿入ロジックの簡素化:
--- 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
フラグのチェックが削除され、カンマが常に挿入されるようになりました。 -
exprList
呼び出し箇所の修正:CallExpr
,CompositeLit
,AssignStmt
,ReturnStmt
,CaseClause
,ValueSpec
,GenDecl
など、exprList
を呼び出している様々な箇所で、commaSep
フラグが削除されたことに伴い、対応する引数が0
に変更されています。例えば、commaSep|commaTerm
はcommaTerm
に、blankStart|commaSep
は0
に変更されています。
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のIssueです。
- https://github.com/golang/go/issues/1207
- Go CL 5672062:
go/printer
: simpler exprList code, more tests- このコミットに対応するGerrit Code Reviewのリンクです。
- https://golang.org/cl/5672062
参考にした情報源リンク
- Go言語の公式ドキュメント:
go/printer
パッケージ - Go言語の公式ドキュメント:
go/ast
パッケージ - Go言語の公式ドキュメント:
go/token
パッケージ gofmt
の内部動作に関する記事やドキュメント(一般的な情報源)gofmt
はgo/printer
パッケージを基盤としています。- 例: "Go's gofmt: a tool for formatting Go programs" by Rob Pike (Go Blog)