[インデックス 14837] ファイルの概要
このコミットは、Go言語の公式フォーマッタであるgofmt
およびその基盤となるgo/printer
パッケージにおける、パラメータ型を囲む不要な括弧の出力を抑制するための変更です。これにより、生成されるGoコードの可読性とGoの慣用的なスタイルへの準拠が向上します。
コミット
commit 0141c92a5331c9aa7c1ac35f54bb3082a38520a3
Author: Robert Griesemer <gri@golang.org>
Date: Wed Jan 9 11:32:16 2013 -0800
go/printer, gofmt: don't print unneeded parentheses around parameter types
Fixes #4624.
R=rsc
CC=golang-dev
https://golang.org/cl/7058052
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/0141c92a5331c9aa7c1ac35f54bb3082a38520a3
元コミット内容
go/printer, gofmt: don't print unneeded parentheses around parameter types
このコミットは、Goのプリンタとフォーマッタが、関数やメソッドのパラメータ型、および戻り値の型宣言において、不必要に付加される括弧を出力しないように修正します。これにより、生成されるコードがより簡潔で、Goの標準的なコーディングスタイルに沿ったものになります。
変更の背景
Go言語には、コードのフォーマットを自動的に行うgofmt
というツールがあります。このツールは、Goのコードベース全体で一貫したスタイルを強制し、可読性を高めることを目的としています。gofmt
は内部的にgo/printer
パッケージを利用して、抽象構文木(AST)からGoコードを再構築します。
このコミット以前は、特定の状況下で、go/printer
がパラメータ型や戻り値の型に不要な括弧を付加してしまう問題がありました。例えば、(int)
のように単一の型を括弧で囲むことは、Goの文法上は許容されますが、慣用的ではありません。特に、((((((int))))))
のように多重に括弧がネストされた場合、コードの可読性が著しく損なわれます。
この問題は、Goのコードベース全体で一貫したフォーマットを維持するというgofmt
の目標に反していました。そのため、不要な括弧を自動的に除去し、よりクリーンなコードを生成することが求められていました。コミットメッセージにあるFixes #4624
は、この問題が報告されたイシュートラッカーのIDを示していますが、コミットが2013年のものであるため、現在のGitHubのイシュートラッカーとは異なる古いシステムでのIDである可能性が高いです。
前提知識の解説
go/printer
パッケージ: Go言語のソースコードを整形し、出力するためのパッケージです。Goの抽象構文木(AST)を受け取り、それを整形されたGoコードとしてバイトストリームに書き出します。gofmt
ツールはこのパッケージを内部的に利用しています。gofmt
ツール: Go言語のソースコードを自動的にフォーマットするコマンドラインツールです。Goの標準的なコーディングスタイルに準拠させることで、コードの可読性と一貫性を高めます。- 抽象構文木(AST: Abstract Syntax Tree): プログラミング言語のソースコードの抽象的な構文構造を木構造で表現したものです。コンパイラやリンタ、フォーマッタなどのツールがコードを解析・操作する際に利用します。Go言語では
go/ast
パッケージでASTを扱います。 ast.Expr
: GoのASTにおける「式」を表すインターフェースです。変数、リテラル、関数呼び出し、型など、様々なものが式として表現されます。ast.ParenExpr
:ast.Expr
の一種で、括弧で囲まれた式を表します。例えば、(a + b)
という式はast.ParenExpr
としてASTに表現されます。- 慣用的なGoのスタイル: Go言語には、公式ドキュメントやコミュニティによって推奨される特定のコーディングスタイルがあります。
gofmt
はこのスタイルを自動的に適用することで、Goコードの一貫性を保ちます。不要な括弧を避けることは、この慣用的なスタイルの一部です。
技術的詳細
このコミットの主要な変更点は、go/printer
パッケージがASTを走査し、コードを整形して出力する際に、パラメータ型や戻り値の型から不要なast.ParenExpr
(括弧で囲まれた式)を再帰的に取り除く新しいヘルパー関数stripParensAlways
を導入したことです。
以前のgo/printer
は、ast.FieldList
(関数のパラメータや戻り値のリスト)を処理する際に、各フィールドの型(par.Type
やresult.List[0].Type
)を直接p.expr()
メソッドに渡していました。p.expr()
はASTノードを整形して出力する役割を担いますが、この際にast.ParenExpr
がネストされていると、そのまま括弧が出力されてしまっていました。
新しいstripParensAlways
関数は、引数としてast.Expr
を受け取り、それがast.ParenExpr
である限り、その内部の式(x.X
)を再帰的に辿っていきます。最終的にast.ParenExpr
ではない最も内側の式を返します。これにより、((((((int))))))
のような多重にネストされた括弧も、int
という本来の型のみが抽出されるようになります。
このstripParensAlways
関数が、nodes.go
内のparameters
関数とsignature
関数で、パラメータ型や戻り値の型を処理する直前に適用されるようになりました。これにより、プリンタが実際に型を出力する前に、不要な括弧が取り除かれるため、整形されたコードには簡潔な型宣言のみが含まれるようになります。
また、printer.go
の変更は、tabwriter.Writer
の型アサーションに関するもので、これは直接的な括弧の除去とは関係なく、Goの型アサーションの構文の改善(tw, _ := (output).(*tabwriter.Writer)
からtw, _ := output.(*tabwriter.Writer)
への変更)であり、このコミットの主要な目的とは異なりますが、同時に行われたクリーンアップの一部と考えられます。
テストデータ(declarations.input
とdeclarations.golden
)の追加は、この変更が意図通りに機能し、様々なケースで不要な括弧が除去されることを検証するために行われました。特に、単一の型、複数の型、チャネル型など、括弧が問題となりうる多様なシナリオがカバーされています。
コアとなるコードの変更箇所
src/pkg/go/printer/nodes.go
--- a/src/pkg/go/printer/nodes.go
+++ b/src/pkg/go/printer/nodes.go
@@ -307,7 +307,7 @@ func (p *printer) parameters(fields *ast.FieldList) {
p.print(blank)
}
// parameter type
- p.expr(par.Type)
+ p.expr(stripParensAlways(par.Type))
prevLine = parLineEnd
}
// if the closing ")" is on a separate line from the last parameter,
@@ -336,7 +336,7 @@ func (p *printer) signature(params, result *ast.FieldList) {
p.print(blank)
if n == 1 && result.List[0].Names == nil {
// single anonymous result; no ()'s
- p.expr(result.List[0].Type)
+ p.expr(stripParensAlways(result.List[0].Type))
return
}
p.parameters(result)
@@ -959,6 +959,13 @@ func stripParens(x ast.Expr) ast.Expr {
return x
}
+func stripParensAlways(x ast.Expr) ast.Expr {
+ if x, ok := x.(*ast.ParenExpr); ok {
+ return stripParensAlways(x.X)
+ }
+ return x
+}
+
func (p *printer) controlClause(isForStmt bool, init ast.Stmt, expr ast.Expr, post ast.Stmt) {
p.print(blank)
needsBlank := false
src/pkg/go/printer/printer.go
--- a/src/pkg/go/printer/printer.go
+++ b/src/pkg/go/printer/printer.go
@@ -1230,7 +1230,7 @@ func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node interface{
}
// flush tabwriter, if any
- if tw, _ := (output).(*tabwriter.Writer); tw != nil {
+ if tw, _ := output.(*tabwriter.Writer); tw != nil {
err = tw.Flush()
}
テストデータ (src/pkg/go/printer/testdata/declarations.input
と declarations.golden
)
新しいテストケースが追加され、入力ファイルで不要な括弧を含む型宣言が定義され、ゴールデンファイルでそれらの括弧が除去された正しい出力が期待されるようになっています。
コアとなるコードの解説
-
stripParensAlways
関数の追加:src/pkg/go/printer/nodes.go
に新しくstripParensAlways
関数が追加されました。func stripParensAlways(x ast.Expr) ast.Expr { if x, ok := x.(*ast.ParenExpr); ok { return stripParensAlways(x.X) } return x }
この関数は、与えられた
ast.Expr
がast.ParenExpr
(括弧で囲まれた式)であるかどうかをチェックします。もしそうであれば、その内部の式(x.X
)に対して再帰的にstripParensAlways
を呼び出します。これにより、((int))
のような多重にネストされた括弧も、最終的に最も内側のint
という型(ast.Ident
などの非ParenExpr
)が返されるようになります。ok
変数は型アサーションが成功したかどうかを示し、成功した場合にのみ再帰処理が続行されます。 -
parameters
関数でのstripParensAlways
の利用:src/pkg/go/printer/nodes.go
のparameters
関数は、関数のパラメータリストを処理します。この関数内で、各パラメータの型を出力する際に、以前はp.expr(par.Type)
と直接型を渡していましたが、変更後はp.expr(stripParensAlways(par.Type))
となりました。これにより、パラメータ型がast.ParenExpr
で囲まれていても、出力時には不要な括弧が除去されます。 -
signature
関数でのstripParensAlways
の利用:src/pkg/go/printer/nodes.go
のsignature
関数は、関数のシグネチャ(パラメータと戻り値)を処理します。特に、単一の匿名戻り値の型を処理する部分で、以前はp.expr(result.List[0].Type)
でしたが、これもp.expr(stripParensAlways(result.List[0].Type))
に変更されました。これにより、単一の匿名戻り値の型に不要な括弧が付いている場合も、適切に除去されます。 -
printer.go
の変更:src/pkg/go/printer/printer.go
の変更は、tabwriter.Writer
への型アサーションの構文をtw, _ := (output).(*tabwriter.Writer)
からtw, _ := output.(*tabwriter.Writer)
へと修正したものです。これは、Goの言語仕様の進化に伴う構文の簡略化であり、このコミットの主要な目的である括弧の除去とは直接関係ありませんが、コードベース全体のクリーンアップの一環として行われたと考えられます。
これらの変更により、go/printer
はよりGoの慣用的なスタイルに沿ったコードを生成するようになり、gofmt
を通じて開発者が意識することなく、整形されたコードの品質が向上しました。
関連リンク
- Gerrit Change-ID:
https://golang.org/cl/7058052
(Goプロジェクトの旧コードレビューシステムであるGerritのリンク) - Go言語公式ドキュメント: https://go.dev/
gofmt
に関するドキュメント: https://go.dev/blog/gofmt
参考にした情報源リンク
- Go言語のソースコード(特に
go/printer
パッケージ) - Go言語のASTに関するドキュメント
- Go言語の型アサーションに関する情報
gofmt
の動作原理に関する一般的な知識- GitHubのコミット履歴
- Go言語の古いイシュートラッカーに関する一般的な情報(
Fixes #4624
の背景を理解するため)- 注:
Fixes #4624
の具体的なイシューページは、コミットが古い(2013年)ため、現在のGitHubイシュートラッカーでは直接見つけることができませんでした。これは、Goプロジェクトがイシュートラッカーシステムを移行したためと考えられます。
- 注: