[インデックス 1955] ファイルの概要
このコミットは、Go言語の初期開発段階におけるコードベースの整形(pretty-printing)およびドキュメント生成に関連する2つの修正を含んでいます。主な変更点は以下の通りです。
hasPostfix
関数のhasSuffix
へのリネーム: 文字列が特定の接尾辞(suffix)で終わるかを判定する関数の名称が、より一般的で直感的なhasSuffix
に変更されました。- 関数リテラルの出力修正: 抽象構文木(AST)を整形して出力する際に、関数リテラルの後に不要なセミコロンが挿入される問題を修正しました。これは、Go言語のセミコロン自動挿入(ASI: Automatic Semicolon Insertion)ルールと、当時のコンパイラ(
6g
)または言語仕様の解釈に関する潜在的なバグに対応するものです。
コミット
commit 184c623e6b3b92f4a0df25796dbb5ddca9559ded
Author: Robert Griesemer <gri@golang.org>
Date: Thu Apr 2 22:24:52 2009 -0700
- renamed hasPostfix -> hasSuffix
- fixed printing of function literals (require separating ";")
R=rsc
OCL=27055
CL=27055
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/184c623e6b3b92f4a0df25796dbb5ddca9559ded
元コミット内容
- renamed hasPostfix -> hasSuffix
- fixed printing of function literals (require separating ";")
R=rsc
OCL=27055
CL=27055
変更の背景
このコミットは、Go言語の初期段階におけるコードの品質向上とバグ修正の一環として行われました。
-
hasPostfix
のリネーム:hasPostfix
という関数名は、文字列の末尾をチェックするという意味合いでは理解できますが、プログラミング言語の標準的な命名規則や慣習においてはhasSuffix
の方がより一般的で広く認識されています。例えば、JavaのString.endsWith()
やPythonのstr.endswith()
など、多くの言語で「suffix」という用語が使われます。Go言語の標準ライブラリにおいても、将来的にstrings.HasSuffix
のような関数が導入されることを考慮すると、初期段階で命名の一貫性を保つことは重要でした。この変更は、コードの可読性とGo言語全体のAPI設計の一貫性を高めるためのものです。 -
関数リテラルの出力修正: Go言語にはセミコロン自動挿入(ASI)という特徴的なルールがあります。これは、特定の状況下で改行の後に自動的にセミコロンが挿入されるというものです。しかし、この自動挿入が常に意図した通りに機能するとは限りません。特に、関数リテラルのような複雑な構文要素の後に、パーサーや整形ツールが誤ってセミコロンを挿入してしまうと、生成されるコードが不正になったり、意図しない挙動を引き起こしたりする可能性があります。 このコミットが行われた2009年当時、Go言語はまだ開発の初期段階であり、言語仕様やコンパイラの挙動が固まりつつある時期でした。
astprinter.go
は抽象構文木(AST)を読みやすい形式で出力するためのツールであり、その出力がGoのコンパイラ(当時の6g
)や他のツールで正しく解釈されることが不可欠でした。// BUG 6g or spec
というコメントは、当時のコンパイラ6g
または言語仕様自体に、関数リテラル後のセミコロンの扱いに曖昧さやバグが存在したことを示唆しています。この修正は、整形されたコードが常に有効なGoコードとして機能するようにするための重要なバグ修正でした。
前提知識の解説
Go言語の初期開発とツールチェイン
Go言語は2009年にGoogleで開発が始まり、その初期段階では、言語仕様、コンパイラ(6g
、8g
など)、アセンブラ、リンカ、そしてコード整形ツールなどが並行して開発されていました。usr/gri/pretty
ディレクトリは、Go言語のコードを整形(pretty-print)するための初期のツール群が含まれていた場所と考えられます。astprinter.go
はASTを整形して出力する役割を担い、godoc.go
はGoのドキュメント生成ツールの一部として、ファイル名やパスの処理を行っていたと推測されます。
抽象構文木 (AST: Abstract Syntax Tree)
ASTは、ソースコードの構造を木構造で表現したものです。コンパイラやリンタ、コード整形ツールなどは、まずソースコードをASTに変換し、そのASTを解析・操作することで様々な処理を行います。astprinter.go
は、このASTを人間が読めるGoコードの形式に変換して出力する役割を担っていました。
Go言語のセミコロン自動挿入 (ASI: Automatic Semicolon Insertion)
Go言語の構文は、C言語やJavaのような言語とは異なり、ほとんどの文の終わりに明示的なセミコロンを必要としません。これは、Goコンパイラが特定のルールに基づいて自動的にセミコロンを挿入するためです。主なルールは以下の通りです。
- 改行の直前にあるトークンが、識別子、整数・浮動小数点・虚数・ルーン・文字列リテラル、
break
、continue
、fallthrough
、return
、++
、--
、または}
である場合、その改行の直後にセミコロンが挿入されます。 - ただし、改行の直前が
}
で、その直後がelse
である場合など、特定の状況ではセミコロンは挿入されません。
このASIはコードを簡潔に記述できる利点がありますが、そのルールを正確に理解していないと、意図しない構文エラーや挙動を引き起こす可能性があります。特に、関数リテラルのようなブロックを持つ構文の後に、ASIがどのように適用されるかは重要な考慮事項です。
Go言語の関数リテラル (Function Literals)
Go言語では、関数を値として扱うことができ、その場で匿名関数を定義することができます。これを関数リテラルと呼びます。関数リテラルはクロージャとしても機能し、定義されたスコープの変数をキャプチャできます。
func() {
// 関数リテラルの本体
}() // ここで関数を呼び出す
関数リテラルは、その本体がブロック({...}
)で構成されるため、ASIのルールが適用される際に特別な注意が必要です。
技術的詳細
hasPostfix
から hasSuffix
へのリネーム
この変更は、usr/gri/pretty/godoc.go
ファイル内で行われました。
- 変更前:
func hasPostfix(s, postfix string) bool
- 変更後:
func hasSuffix(s, postfix string) bool
このリネームは、機能的な変更を伴わず、単に関数名が変更されただけです。しかし、これはGo言語のコードベース全体における命名規則の統一と、より直感的なAPI設計に向けた初期の取り組みを示しています。Suffix
という用語は、文字列処理の文脈で「接尾辞」を意味する標準的な言葉であり、Postfix
よりも広く認知されています。これにより、コードの意図がより明確になり、将来的にGoの標準ライブラリに同様の機能が追加される際の混乱を防ぐ効果があります。
関数リテラルの出力修正 (astprinter.go
)
この修正は、usr/gri/pretty/astprinter.go
ファイル内のDoFuncLit
メソッドで行われました。
DoFuncLit
メソッドは、抽象構文木(AST)内の関数リテラル(ast.FuncLit
)ノードを処理し、それをGoのソースコード形式で出力する役割を担っています。
変更点:
P.Stmt(x.Body);
の直後に P.opt_semi = false; // BUG 6g or spec
が追加されました。
P.Stmt(x.Body)
: これは関数リテラルの本体(x.Body
)を整形して出力する部分です。関数本体は通常、{ ... }
のブロックで構成されます。P.opt_semi = false;
:P
はPrinter
構造体のインスタンスであり、opt_semi
はそのプリンタがセミコロンを自動挿入すべきかどうかを制御するフラグであると推測されます。false
に設定することで、関数リテラルの本体(}
)の直後に不要なセミコロンが挿入されるのを防ぎます。// BUG 6g or spec
: このコメントは非常に重要です。当時のGoコンパイラ6g
、またはGo言語の仕様自体に、関数リテラルの後にセミコロンが自動挿入されるべきかどうかの解釈に問題があったことを示しています。 GoのASIルールでは、}
の後に改行が続く場合、通常はセミコロンが挿入されます。しかし、関数リテラルが式として使われる場合(例:f := func(){ ... }()
)、関数リテラル全体が一つの式として扱われるため、その直後にセミコロンが挿入されると構文エラーになる可能性があります。 この修正は、astprinter
が関数リテラルを整形する際に、その直後にセミコロンが自動挿入されないように明示的に制御することで、生成されるコードが常に有効なGo構文となるように保証しています。これは、Go言語のパーサーやコンパイラの挙動を深く理解し、それらに合わせてコード整形ツールの出力を調整する必要があったことを示しています。
コアとなるコードの変更箇所
usr/gri/pretty/astprinter.go
--- a/usr/gri/pretty/astprinter.go
+++ b/usr/gri/pretty/astprinter.go
@@ -658,6 +658,7 @@ func (P *Printer) DoFuncLit(x *ast.FuncLit) {
P.DoFuncType(x.Type);
P.separator = blank;
P.Stmt(x.Body);
+ P.opt_semi = false; // BUG 6g or spec
P.newlines = 0;
}
usr/gri/pretty/godoc.go
--- a/usr/gri/pretty/godoc.go
+++ b/usr/gri/pretty/godoc.go
@@ -100,14 +100,14 @@ func hasPrefix(s, prefix string) bool {
}
-func hasPostfix(s, postfix string) bool {
+func hasSuffix(s, postfix string) bool {
pos := len(s) - len(postfix);
return pos >= 0 && s[pos : len(s)] == postfix;
}
func isGoFile(dir *os.Dir) bool {
- return dir.IsRegular() && hasPostfix(dir.Name, ".go");
+ return dir.IsRegular() && hasSuffix(dir.Name, ".go");
}
@@ -414,7 +414,7 @@ var (
func addFile(dirname string, filename string) {
- if hasPostfix(filename, "_test.go") {
+ if hasSuffix(filename, "_test.go") {
// ignore package tests
return;
}
コアとなるコードの解説
usr/gri/pretty/astprinter.go
の変更
DoFuncLit
関数は、Goの抽象構文木(AST)における関数リテラル(ast.FuncLit
)ノードを処理し、整形されたGoコードとして出力する役割を担っています。
追加された行 P.opt_semi = false; // BUG 6g or spec
は、関数リテラルの本体(x.Body
)の出力が完了した直後に実行されます。
P.opt_semi
は、Printer
構造体内部で管理されているフラグであり、次のトークンの前にセミコロンを自動挿入するかどうかを制御していると考えられます。このフラグをfalse
に明示的に設定することで、関数リテラルの閉じ波括弧}
の直後に、Goのセミコロン自動挿入ルールによって意図しないセミコロンが挿入されるのを防ぎます。
当時のGoコンパイラ6g
または言語仕様の曖昧さにより、関数リテラルの後にセミコロンが挿入されるべきかどうかの挙動が不安定であったため、この修正は、astprinter
が常に正しいGo構文を生成するようにするための重要な調整でした。これにより、整形されたコードがコンパイルエラーを引き起こすことなく、期待通りに動作することが保証されます。
usr/gri/pretty/godoc.go
の変更
このファイルでは、hasPostfix
関数がhasSuffix
にリネームされ、それに伴い、この関数を呼び出している箇所もすべてhasSuffix
に修正されました。
func hasPostfix(s, postfix string) bool
がfunc hasSuffix(s, postfix string) bool
に変更。isGoFile
関数内のhasPostfix(dir.Name, ".go")
がhasSuffix(dir.Name, ".go")
に変更。addFile
関数内のhasPostfix(filename, "_test.go")
がhasSuffix(filename, "_test.go")
に変更。
この変更は、コードの機能には影響を与えません。これは、Go言語のコードベース全体における命名規則の標準化と一貫性向上を目的としたものです。hasSuffix
という名称は、文字列が特定の接尾辞で終わるかを判定する機能に対して、より一般的で直感的な表現であり、コードの可読性を高めます。
関連リンク
- Go言語のセミコロン自動挿入に関する公式ドキュメント(Go言語仕様の一部):
- Go言語の関数リテラルに関する公式ドキュメント(Go言語仕様の一部):
参考にした情報源リンク
- Go言語の初期のコミット履歴(GitHub):
- Go言語のセミコロン自動挿入に関する一般的な解説記事(例: Goのブログや技術記事)
- (具体的なURLはコミット内容から直接特定できないため、一般的な情報源として記載)
- Go言語のASTと
go/ast
パッケージに関するドキュメント: - Go言語の
go/printer
パッケージに関するドキュメント(astprinter
の現代版に相当):
[インデックス 1955] ファイルの概要
このコミットは、Go言語の初期開発段階におけるコードベースの整形(pretty-printing)およびドキュメント生成に関連する2つの修正を含んでいます。主な変更点は以下の通りです。
hasPostfix
関数のhasSuffix
へのリネーム: 文字列が特定の接尾辞(suffix)で終わるかを判定する関数の名称が、より一般的で直感的なhasSuffix
に変更されました。- 関数リテラルの出力修正: 抽象構文木(AST)を整形して出力する際に、関数リテラルの後に不要なセミコロンが挿入される問題を修正しました。これは、Go言語のセミコロン自動挿入(ASI: Automatic Semicolon Insertion)ルールと、当時のコンパイラ(
6g
)または言語仕様の解釈に関する潜在的なバグに対応するものです。
コミット
commit 184c623e6b3b92f4a0df25796dbb5ddca9559ded
Author: Robert Griesemer <gri@golang.org>
Date: Thu Apr 2 22:24:52 2009 -0700
- renamed hasPostfix -> hasSuffix
- fixed printing of function literals (require separating ";")
R=rsc
OCL=27055
CL=27055
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/184c623e6b3b92f4a0df25796dbb5ddca9559ded
元コミット内容
- renamed hasPostfix -> hasSuffix
- fixed printing of function literals (require separating ";")
R=rsc
OCL=27055
CL=27055
変更の背景
このコミットは、Go言語の初期段階におけるコードの品質向上とバグ修正の一環として行われました。
-
hasPostfix
のリネーム:hasPostfix
という関数名は、文字列の末尾をチェックするという意味合いでは理解できますが、プログラミング言語の標準的な命名規則や慣習においてはhasSuffix
の方がより一般的で広く認識されています。例えば、JavaのString.endsWith()
やPythonのstr.endswith()
など、多くの言語で「suffix」という用語が使われます。Go言語の標準ライブラリにおいても、将来的にstrings.HasSuffix
のような関数が導入されることを考慮すると、初期段階で命名の一貫性を保つことは重要でした。この変更は、コードの可読性とGo言語全体のAPI設計の一貫性を高めるためのものです。 -
関数リテラルの出力修正: Go言語にはセミコロン自動挿入(ASI)という特徴的なルールがあります。これは、特定の状況下で改行の後に自動的にセミコロンが挿入されるというものです。しかし、この自動挿入が常に意図した通りに機能するとは限りません。特に、関数リテラルのような複雑な構文要素の後に、パーサーや整形ツールが誤ってセミコロンを挿入してしまうと、生成されるコードが不正になったり、意図しない挙動を引き起こしたりする可能性があります。 このコミットが行われた2009年当時、Go言語はまだ開発の初期段階であり、言語仕様やコンパイラの挙動が固まりつつある時期でした。
astprinter.go
は抽象構文木(AST)を読みやすい形式で出力するためのツールであり、その出力がGoのコンパイラ(当時の6g
)や他のツールで正しく解釈されることが不可欠でした。// BUG 6g or spec
というコメントは、当時のコンパイラ6g
または言語仕様自体に、関数リテラル後のセミコロンの扱いに曖昧さやバグが存在したことを示唆しています。この修正は、整形されたコードが常に有効なGoコードとして機能するようにするための重要なバグ修正でした。
前提知識の解説
Go言語の初期開発とツールチェイン
Go言語は2009年にGoogleで開発が始まり、その初期段階では、言語仕様、コンパイラ(6g
、8g
など)、アセンブラ、リンカ、そしてコード整形ツールなどが並行して開発されていました。usr/gri/pretty
ディレクトリは、Go言語のコードを整形(pretty-print)するための初期のツール群が含まれていた場所と考えられます。astprinter.go
はASTを整形して出力する役割を担い、godoc.go
はGoのドキュメント生成ツールの一部として、ファイル名やパスの処理を行っていたと推測されます。
抽象構文木 (AST: Abstract Syntax Tree)
ASTは、ソースコードの構造を木構造で表現したものです。コンパイラやリンタ、コード整形ツールなどは、まずソースコードをASTに変換し、そのASTを解析・操作することで様々な処理を行います。astprinter.go
は、このASTを人間が読めるGoコードの形式に変換して出力する役割を担っていました。
Go言語のセミコロン自動挿入 (ASI: Automatic Semicolon Insertion)
Go言語の構文は、C言語やJavaのような言語とは異なり、ほとんどの文の終わりに明示的なセミコロンを必要としません。これは、Goコンパイラが特定のルールに基づいて自動的にセミコロンを挿入するためです。主なルールは以下の通りです。
- 改行の直前にあるトークンが、識別子、整数・浮動小数点・虚数・ルーン・文字列リテラル、
break
、continue
、fallthrough
、return
、++
、--
、または}
である場合、その改行の直後にセミコロンが挿入されます。 - ただし、改行の直前が
}
で、その直後がelse
である場合など、特定の状況ではセミコロンは挿入されません。
このASIはコードを簡潔に記述できる利点がありますが、そのルールを正確に理解していないと、意図しない構文エラーや挙動を引き起こす可能性があります。特に、関数リテラルのようなブロックを持つ構文の後に、ASIがどのように適用されるかは重要な考慮事項です。
Go言語の関数リテラル (Function Literals)
Go言語では、関数を値として扱うことができ、その場で匿名関数を定義することができます。これを関数リテラルと呼びます。関数リテラルはクロージャとしても機能し、定義されたスコープの変数をキャプチャできます。
func() {
// 関数リテラルの本体
}() // ここで関数を呼び出す
関数リテラルは、その本体がブロック({...}
)で構成されるため、ASIのルールが適用される際に特別な注意が必要です。
技術的詳細
hasPostfix
から hasSuffix
へのリネーム
この変更は、usr/gri/pretty/godoc.go
ファイル内で行われました。
- 変更前:
func hasPostfix(s, postfix string) bool
- 変更後:
func hasSuffix(s, postfix string) bool
このリネームは、機能的な変更を伴わず、単に関数名が変更されただけです。しかし、これはGo言語のコードベース全体における命名規則の統一と、より直感的なAPI設計に向けた初期の取り組みを示しています。Suffix
という用語は、文字列処理の文脈で「接尾辞」を意味する標準的な言葉であり、Postfix
よりも広く認知されています。これにより、コードの意図がより明確になり、将来的にGoの標準ライブラリに同様の機能が追加される際の混乱を防ぐ効果があります。
関数リテラルの出力修正 (astprinter.go
)
この修正は、usr/gri/pretty/astprinter.go
ファイル内のDoFuncLit
メソッドで行われました。
DoFuncLit
メソッドは、抽象構文木(AST)内の関数リテラル(ast.FuncLit
)ノードを処理し、それをGoのソースコード形式で出力する役割を担っています。
変更点:
P.Stmt(x.Body);
の直後に P.opt_semi = false; // BUG 6g or spec
が追加されました。
P.Stmt(x.Body)
: これは関数リテラルの本体(x.Body
)を整形して出力する部分です。関数本体は通常、{ ... }
のブロックで構成されます。P.opt_semi = false;
:P
はPrinter
構造体のインスタンスであり、opt_semi
はそのプリンタがセミコロンを自動挿入すべきかどうかを制御するフラグであると推測されます。false
に設定することで、関数リテラルの本体(}
)の直後に不要なセミコロンが挿入されるのを防ぎます。// BUG 6g or spec
: このコメントは非常に重要です。当時のGoコンパイラ6g
、またはGo言語の仕様自体に、関数リテラルの後にセミコロンが自動挿入されるべきかどうかの解釈に問題があったことを示しています。 GoのASIルールでは、}
の後に改行が続く場合、通常はセミコロンが挿入されます。しかし、関数リテラルが式として使われる場合(例:f := func(){ ... }()
)、関数リテラル全体が一つの式として扱われるため、その直後にセミコロンが挿入されると構文エラーになる可能性があります。 この修正は、astprinter
が関数リテラルを整形する際に、その直後にセミコロンが自動挿入されないように明示的に制御することで、生成されるコードが常に有効なGo構文となるように保証しています。これは、Go言語のパーサーやコンパイラの挙動を深く理解し、それらに合わせてコード整形ツールの出力を調整する必要があったことを示しています。
コアとなるコードの変更箇所
usr/gri/pretty/astprinter.go
--- a/usr/gri/pretty/astprinter.go
+++ b/usr/gri/pretty/astprinter.go
@@ -658,6 +658,7 @@ func (P *Printer) DoFuncLit(x *ast.FuncLit) {
P.DoFuncType(x.Type);
P.separator = blank;
P.Stmt(x.Body);
+ P.opt_semi = false; // BUG 6g or spec
P.newlines = 0;
}
usr/gri/pretty/godoc.go
--- a/usr/gri/pretty/godoc.go
+++ b/usr/gri/pretty/godoc.go
@@ -100,14 +100,14 @@ func hasPrefix(s, prefix string) bool {
}
-func hasPostfix(s, postfix string) bool {
+func hasSuffix(s, postfix string) bool {
pos := len(s) - len(postfix);
return pos >= 0 && s[pos : len(s)] == postfix;
}
func isGoFile(dir *os.Dir) bool {
- return dir.IsRegular() && hasPostfix(dir.Name, ".go");
+ return dir.IsRegular() && hasSuffix(dir.Name, ".go");
}
@@ -414,7 +414,7 @@ var (
func addFile(dirname string, filename string) {
- if hasPostfix(filename, "_test.go") {
+ if hasSuffix(filename, "_test.go") {
// ignore package tests
return;
}
コアとなるコードの解説
usr/gri/pretty/astprinter.go
の変更
DoFuncLit
関数は、Goの抽象構文木(AST)における関数リテラル(ast.FuncLit
)ノードを処理し、整形されたGoコードとして出力する役割を担っています。
追加された行 P.opt_semi = false; // BUG 6g or spec
は、関数リテラルの本体(x.Body
)の出力が完了した直後に実行されます。
P.opt_semi
は、Printer
構造体内部で管理されているフラグであり、次のトークンの前にセミコロンを自動挿入するかどうかを制御していると考えられます。このフラグをfalse
に明示的に設定することで、関数リテラルの閉じ波括弧}
の直後に、Goのセミコロン自動挿入ルールによって意図しないセミコロンが挿入されるのを防ぎます。
当時のGoコンパイラ6g
または言語仕様の曖昧さにより、関数リテラルの後にセミコロンが挿入されるべきかどうかの挙動が不安定であったため、この修正は、astprinter
が常に正しいGo構文を生成するようにするための重要な調整でした。これにより、整形されたコードがコンパイルエラーを引き起こすことなく、期待通りに動作することが保証されます。
usr/gri/pretty/godoc.go
の変更
このファイルでは、hasPostfix
関数がhasSuffix
にリネームされ、それに伴い、この関数を呼び出している箇所もすべてhasSuffix
に修正されました。
func hasPostfix(s, postfix string) bool
がfunc hasSuffix(s, postfix string) bool
に変更。isGoFile
関数内のhasPostfix(dir.Name, ".go")
がhasSuffix(dir.Name, ".go")
に変更。addFile
関数内のhasPostfix(filename, "_test.go")
がhasSuffix(filename, "_test.go")
に変更。
この変更は、コードの機能には影響を与えません。これは、Go言語のコードベース全体における命名規則の標準化と一貫性向上を目的としたものです。hasSuffix
という名称は、文字列が特定の接尾辞で終わるかを判定する機能に対して、より一般的で直感的な表現であり、コードの可読性を高めます。
関連リンク
- Go言語のセミコロン自動挿入に関する公式ドキュメント(Go言語仕様の一部):
- Go言語の関数リテラルに関する公式ドキュメント(Go言語仕様の一部):
参考にした情報源リンク
- Go言語の初期のコミット履歴(GitHub):
- Go言語のセミコロン自動挿入に関する一般的な解説記事(例: Goのブログや技術記事)
- (具体的なURLはコミット内容から直接特定できないため、一般的な情報源として記載)
- Go言語のASTと
go/ast
パッケージに関するドキュメント: - Go言語の
go/printer
パッケージに関するドキュメント(astprinter
の現代版に相当):