[インデックス 11232] ファイルの概要
このコミットは、Go言語のテストファイル群に対して gofmt
ツールによるフォーマット変更を適用したものです。コードの機能的な変更は含まれておらず、主にGo言語の標準的なコーディングスタイルに準拠させるための整形が行われています。
コミット
commit 6b3462820f5a1c97adfa148df0e1e37c37a3716c
Author: Ian Lance Taylor <iant@golang.org>
Date: Wed Jan 18 13:20:55 2012 -0800
test: gofmt a few tests
I'm planning to change these tests, but the gofmt changes are
fairly extensive, so I'm separating the gofmt changes from the
substantive changes.
R=golang-dev, rsc, r
CC=golang-dev
https://golang.org/cl/5557052
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/6b3462820f5a1c97adfa148df0e1e37c37a3716c
元コミット内容
test: gofmt a few tests
I'm planning to change these tests, but the gofmt changes are
fairly extensive, so I'm separating the gofmt changes from the
substantive changes.
R=golang-dev, rsc, r
CC=golang-dev
https://golang.org/cl/5557052
変更の背景
このコミットの背景には、Go言語開発における gofmt
の重要性と、大規模なコードベース管理におけるベストプラクティスがあります。
Go言語には gofmt
という公式のフォーマッタツールが存在します。このツールは、Goのソースコードを自動的に標準的なスタイルに整形する役割を担っています。gofmt
を使用することで、開発者間でコーディングスタイルに関する議論を不要にし、コードの可読性を均一に保つことができます。これは、特に複数の開発者が関わるプロジェクトやオープンソースプロジェクトにおいて、コードベースの一貫性を維持するために極めて重要です。
コミットメッセージにあるように、この変更は「実質的な変更 (substantive changes)」とは別に gofmt
による整形のみを目的としています。これは、以下の理由から推奨されるGitのコミットプラクティスです。
- 変更内容の明確化: フォーマット変更と機能変更を分離することで、各コミットの目的が明確になります。これにより、コードレビューが容易になり、特定の機能変更やバグ修正がどのような意図で行われたのかを追跡しやすくなります。
- レビューの効率化: フォーマット変更が大量に含まれるコミットは、実際のロジック変更が埋もれてしまい、レビューアが重要な変更を見落とすリスクを高めます。整形のみのコミットは、レビューアがフォーマットの差分を無視して、次のコミットで提示されるであろう機能変更に集中できるようにします。
- Git履歴のクリーンさ: 履歴がクリーンであると、
git blame
やgit bisect
といったツールを使った際に、特定の行がいつ、なぜ変更されたのかを正確に把握しやすくなります。フォーマット変更が機能変更と混ざっていると、これらのツールの出力がノイズで溢れてしまい、デバッグや履歴の調査が困難になることがあります。
このコミットは、将来的にこれらのテストファイルに対して機能的な変更を加える予定があるため、その前に gofmt
を適用してコードベースを整理しておくという意図で行われています。
前提知識の解説
Go言語
Go(Golang)は、Googleによって開発されたオープンソースのプログラミング言語です。静的型付け、コンパイル型、並行処理に強い特徴を持ち、シンプルさと効率性を重視しています。システムプログラミング、Webサービス、マイクロサービス、CLIツールなど、幅広い分野で利用されています。
gofmt
gofmt
は、Go言語のソースコードを自動的に整形するツールです。GoのSDKに標準で含まれており、Goコミュニティでは gofofmt
を使ってコードを整形することが強く推奨されています。
gofmt
の主な特徴と利点は以下の通りです。
- 標準化されたスタイル:
gofmt
は、Go言語の公式なコーディングスタイルガイドライン("Effective Go")に基づいてコードを整形します。これにより、Goのコードはどのプロジェクトでも一貫した見た目になります。 - 開発者の負担軽減: 開発者が手動でインデント、スペース、改行などを調整する手間を省きます。これにより、開発者はコードのロジックに集中できます。
- コードレビューの効率化: スタイルに関する議論が不要になるため、コードレビューではロジックや設計の妥当性といった、より本質的な側面に集中できます。
- ツールとの連携: 多くのGo開発環境(IDEやエディタ)は
gofmt
と統合されており、保存時に自動的にコードを整形する機能を提供しています。
gofmt
は、以下のような整形を行います(このコミットの変更点にも見られます)。
- セミコロンの自動挿入: Go言語では、文の終わりにセミコロンを記述する必要がありません。
gofmt
は必要に応じて自動的にセミコロンを挿入(または削除)します。 - インデントとスペース: 標準的なインデント(タブ)とスペースのルールを適用します。
- 構造体(struct)のフィールドアライメント: 構造体のフィールド宣言を縦に揃えることで、可読性を向上させます。
if
、for
、switch
などのブロックの整形: 標準的な括弧の配置や改行ルールを適用します。- 不要な括弧の削除: 冗長な括弧を削除します。
Gitのコミットプラクティス
Gitを用いたバージョン管理において、コミットはプロジェクトの変更履歴を記録する重要な単位です。良いコミットプラクティスは、プロジェクトの管理、デバッグ、共同作業の効率を大幅に向上させます。
- 単一責任の原則: 一つのコミットは一つの論理的な変更のみを含むべきです。例えば、バグ修正と新機能追加を同じコミットに含めるべきではありません。
- 意味のあるコミットメッセージ: コミットメッセージは、その変更が「なぜ」行われたのか、「何を」変更したのかを簡潔かつ明確に説明すべきです。
- 頻繁なコミット: 小さな変更でも頻繁にコミットすることで、問題が発生した際に原因を特定しやすくなります。
- フォーマット変更の分離: このコミットのように、コードの整形やリファクタリングといった機能に影響しない変更は、機能変更とは別のコミットとして分離することが推奨されます。
技術的詳細
このコミットで行われている技術的な変更は、Go言語の gofmt
ツールによって自動的に適用されるコード整形です。具体的には、以下の種類の変更が複数のテストファイルにわたって行われています。
- セミコロンの削除: Go言語では、文の終わりにはセミコロンを記述する必要がありません。
gofmt
は、開発者が明示的に記述したセミコロンを削除し、Goコンパイラが自動的に挿入するルール(Automatic Semicolon Insertion)に任せます。- 例:
v.nelem = 0;
がv.nelem = 0
に変更されています。
- 例:
- 構造体フィールドのアライメント: 構造体(
struct
)のフィールド宣言において、フィールド名と型の間、または型とコメントの間に適切なスペースを挿入し、縦に揃えることで可読性を向上させます。- 例:
がtype Vector struct { nelem int; elem []Element; }
に変更されています。特にtype Vector struct { nelem int elem []Element }
elem
の前にスペースが追加され、nelem
のint
と縦に揃えられています。
- 例:
if
文やfor
文のブロックの整形: 括弧の配置や改行が標準的なスタイルに調整されます。- 例:
がif k != 0 { panic("k not zero") } // inner loop breaks this one every time
に変更され、if k != 0 { panic("k not zero") } // inner loop breaks this one every time
panic
が新しい行に移動し、インデントされています。
- 例:
- 関数宣言の整形: 関数名と引数リストの間のスペース、および関数本体の開始括弧の配置が調整されます。
- 例:
func main() {
の前の改行が削除されています。
- 例:
- 不要な空行の削除: コードの途中に存在する不要な空行が削除されます。
test/peano.go
などで、関数定義間の空行が削除されています。
switch
文の括弧の削除: Go 1.0以降のswitch
文では、条件式を括弧で囲む必要がありません。gofmt
はこの冗長な括弧を削除します。- 例:
switch(i)
がswitch i
に変更されています。
- 例:
これらの変更は、コードの実行には一切影響を与えません。純粋にコードの見た目を改善し、Goコミュニティ全体で一貫したコーディングスタイルを強制するためのものです。
コアとなるコードの変更箇所
このコミットは複数のファイルにわたるフォーマット変更ですが、特に test/fixedbugs/bug027.go
と test/ken/rob2.go
の変更が gofmt
の典型的な整形パターンをよく示しています。
test/fixedbugs/bug027.go
の変更例
--- a/test/fixedbugs/bug027.go
+++ b/test/fixedbugs/bug027.go
@@ -10,49 +10,55 @@ type Element interface {
}
type Vector struct {
- nelem int;
- elem []Element;
+ nelem int
+ elem []Element
}
func New() *Vector {
- v := new(Vector);
- v.nelem = 0;
- v.elem = make([]Element, 10);
- return v;
+ v := new(Vector)
+ v.nelem = 0
+ v.elem = make([]Element, 10)
+ return v
}
func (v *Vector) At(i int) Element {
- return v.elem[i];
+ return v.elem[i]
}
func (v *Vector) Insert(e Element) {
- v.elem[v.nelem] = e;
- v.nelem++;
+ v.elem[v.nelem] = e
+ v.nelem++
}
func main() {
- type I struct { val int; };
- i0 := new(I); i0.val = 0;
- i1 := new(I); i1.val = 11;
- i2 := new(I); i2.val = 222;
- i3 := new(I); i3.val = 3333;
- i4 := new(I); i4.val = 44444;
- v := New();
- print("hi\n");
- v.Insert(i4);
- v.Insert(i3);
- v.Insert(i2);
- v.Insert(i1);
- v.Insert(i0);
+ type I struct{ val int }
+ i0 := new(I)
+ i0.val = 0
+ i1 := new(I)
+ i1.val = 11
+ i2 := new(I)
+ i2.val = 222
+ i3 := new(I)
+ i3.val = 3333
+ i4 := new(I)
+ i4.val = 44444
+ v := New()
+ print("hi\n")
+ v.Insert(i4)
+ v.Insert(i3)
+ v.Insert(i2)
+ v.Insert(i1)
+ v.Insert(i0)
for i := 0; i < v.nelem; i++ {\n-\t\tvar x *I;\n-\t\tx = v.At(i).(*I);\n-\t\tprint(i, " ", x.val, "\n"); // prints correct list
+\t\tvar x *I
+\t\tx = v.At(i).(*I)
+\t\tprint(i, " ", x.val, "\n") // prints correct list
}\n for i := 0; i < v.nelem; i++ {\n-\t\tprint(i, " ", v.At(i).(*I).val, "\n");
+\t\tprint(i, " ", v.At(i).(*I).val, "\n")
}\n }\n+\n /*\n bug027.go:50: illegal types for operand\n \t(<Element>I{}) CONV (<I>{})\n```
### `test/ken/rob2.go` の変更例
```diff
--- a/test/ken/rob2.go
+++ b/test/ken/rob2.go
@@ -4,269 +4,268 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-
package main
-const nilchar = 0;
+const nilchar = 0
type Atom struct {\n-\tstr\t\tstring;\n-\tinteger\t\tint;\n-\tnext\t\t*Slist;\t/* in hash bucket */\n+\tstr string
+\tinteger int
+\tnext *Slist /* in hash bucket */
}\n
type List struct {\n-\tcar\t\t*Slist;\n-\tcdr*Slist;\n+\tcar *Slist
+\tcdr *Slist
}\n
type Slist struct {\n-\tisatom\t\tbool;\n-\tisstring\tbool;\n+\tisatom bool
+\tisstring bool
\t//union {\n-\tatom\t\tAtom;\n-\tlist\t\tList;\n+\tatom Atom
+\tlist List
\t//} u;\n
}\n
func (this *Slist) Car() *Slist {\n-\treturn this.list.car;\n+\treturn this.list.car
}\n
func (this *Slist) Cdr() *Slist {\n-\treturn this.list.cdr;\n+\treturn this.list.cdr
}\n
func (this *Slist) String() string {\n-\treturn this.atom.str;\n+\treturn this.atom.str
}\n
func (this *Slist) Integer() int {\n-\treturn this.atom.integer;\n+\treturn this.atom.integer
}\n
func (slist *Slist) Free() {\n \tif slist == nil {\n-\t\treturn;\n+\t\treturn
\t}\n \tif slist.isatom {\n-//\t\tfree(slist.String());\n+\t\t//\t\tfree(slist.String());
\t} else {\n-\t\tslist.Car().Free();\n-\t\tslist.Cdr().Free();\n+\t\tslist.Car().Free()
+\t\tslist.Cdr().Free()
\t}\n-//\tfree(slist);\n+\t//\tfree(slist);\
}\n
//Slist* atom(byte *s, int i);\n
-var token int;\n-var peekc int = -1;\n-var lineno int32 = 1;\n+var token int
+var peekc int = -1
+var lineno int32 = 1
-var input string;\n-var inputindex int = 0;\n-var tokenbuf [100]byte;\n-var tokenlen int = 0;\n+var input string
+var inputindex int = 0
+var tokenbuf [100]byte
+var tokenlen int = 0
-const EOF int = -1;\n+const EOF int = -1
func main() {\n-\tvar list *Slist;\n+\tvar list *Slist
-\tOpenFile();\n-\tfor ;; {\n-\t\tlist = Parse();\n+\tOpenFile()
+\tfor {
+\t\tlist = Parse()
\t\tif list == nil {\n-\t\t\tbreak;\n+\t\t\tbreak
\t\t}\n-\t\tlist.Print();\n-\t\tlist.Free();\n-\t\tbreak;\n+\t\tlist.Print()
+\t\tlist.Free()
+\t\tbreak
\t}\n }\n
func (slist *Slist) PrintOne(doparen bool) {\n \tif slist == nil {\n-\t\treturn;\n+\t\treturn
\t}\n \tif slist.isatom {\n \t\tif slist.isstring {\n-\t\t\tprint(slist.String());\n+\t\t\tprint(slist.String())
\t\t} else {\n-\t\t\tprint(slist.Integer());\n+\t\t\tprint(slist.Integer())
\t\t}\n \t} else {\n \t\tif doparen {\n-\t\t\tprint("(" );\n+\t\t\tprint("(")
\t\t}\n-\t\tslist.Car().PrintOne(true);\n+\t\tslist.Car().PrintOne(true)
\t\tif slist.Cdr() != nil {\n-\t\t\tprint(" ");\n-\t\t\tslist.Cdr().PrintOne(false);\n+\t\t\tprint(" ")
+\t\t\tslist.Cdr().PrintOne(false)
\t\t}\n \t\tif doparen {\n-\t\t\tprint(")");\n+\t\t\tprint(")")
\t\t}\n \t}\n }\n
func (slist *Slist) Print() {\n-\tslist.PrintOne(true);\n-\tprint("\n");\n+\tslist.PrintOne(true)
+\tprint("\n")
}\n
func Get() int {\n-\tvar c int;\n+\tvar c int
\tif peekc >= 0 {\n-\t\tc = peekc;\n-\t\tpeekc = -1;\n+\t\tc = peekc
+\t\tpeekc = -1
\t} else {\n-\t\tc = int(input[inputindex]);\n-\t\tinputindex++;\n+\t\tc = int(input[inputindex])
+\t\tinputindex++
\t\tif c == '\n' {\n-\t\t\tlineno = lineno + 1;\n+\t\t\tlineno = lineno + 1
\t\t}\n \t\tif c == nilchar {\n-\t\t\tinputindex = inputindex - 1;\n-\t\t\tc = EOF;\n+\t\t\tinputindex = inputindex - 1
+\t\t\tc = EOF
\t\t}\n \t}\n-\treturn c;\n+\treturn c
}\n
func WhiteSpace(c int) bool {\n-\treturn c == ' ' || c == '\t' || c == '\r' || c == '\n';\n+\treturn c == ' ' || c == '\t' || c == '\r' || c == '\n'
}\n
func NextToken() {\n-\tvar i, c int;\n+\tvar i, c int
-\ttokenbuf[0] = nilchar;\t// clear previous token
-\tc = Get();
+\ttokenbuf[0] = nilchar // clear previous token
+\tc = Get()
\tfor WhiteSpace(c) {\n-\t\tc = Get();
+\t\tc = Get()
\t}\n \tswitch c {\n \tcase EOF:\n-\t\ttoken = EOF;\n+\t\ttoken = EOF
\tcase '(', ')':\n-\t\ttoken = c;\n-\t\tbreak;\n+\t\ttoken = c
+\t\tbreak
\tdefault:\n-\t\tfor i = 0; i < 100 - 1; {\t// sizeof tokenbuf - 1
-\t\t\ttokenbuf[i] = byte(c);\n-\t\t\ti = i + 1;\n-\t\t\tc = Get();
+\t\tfor i = 0; i < 100-1; { // sizeof tokenbuf - 1
+\t\t\ttokenbuf[i] = byte(c)
+\t\t\ti = i + 1
+\t\t\tc = Get()
\t\t\tif c == EOF {\n-\t\t\t\tbreak;\n+\t\t\t\tbreak
\t\t\t}\n \t\t\tif WhiteSpace(c) || c == ')' {\n-\t\t\t\tpeekc = c;\n-\t\t\t\tbreak;\n+\t\t\t\tpeekc = c
+\t\t\t\tbreak
\t\t\t}\n \t\t}\n-\t\tif i >= 100 - 1 {\t// sizeof tokenbuf - 1
-\t\t\tpanic("atom too long\n");
+\t\tif i >= 100-1 { // sizeof tokenbuf - 1
+\t\t\tpanic("atom too long\n")
\t\t}\n-\t\ttokenlen = i;\n-\t\ttokenbuf[i] = nilchar;\n+\t\ttokenlen = i
+\t\ttokenbuf[i] = nilchar
\t\tif '0' <= tokenbuf[0] && tokenbuf[0] <= '9' {\n-\t\t\ttoken = '0';
+\t\t\ttoken = '0'
\t\t} else {\n-\t\t\ttoken = 'A';
+\t\t\ttoken = 'A'
\t\t}\n \t}\n }\n
func Expect(c int) {\n \tif token != c {\n-\t\tprint("parse error: expected ", c, "\n");
-\t\tpanic("parse");
+\t\tprint("parse error: expected ", c, "\n")
+\t\tpanic("parse")
\t}\n-\tNextToken();
+\tNextToken()
}\n
// Parse a non-parenthesized list up to a closing paren or EOF\n func ParseList() *Slist {\n-\tvar slist, retval *Slist;\n-\n-\tslist = new(Slist);\n-\tslist.list.car = nil;\n-\tslist.list.cdr = nil;\n-\tslist.isatom = false;\n-\tslist.isstring = false;\n-\n-\tretval = slist;\n-\tfor ;; {\n-\t\tslist.list.car = Parse();
-\t\tif token == ')' || token == EOF {\t// empty cdr
-\t\t\tbreak;
+\tvar slist, retval *Slist
+\n+\tslist = new(Slist)
+\tslist.list.car = nil
+\tslist.list.cdr = nil
+\tslist.isatom = false
+\tslist.isstring = false
+\n+\tretval = slist
+\tfor {
+\t\tslist.list.car = Parse()
+\t\tif token == ')' || token == EOF { // empty cdr
+\t\t\tbreak
\t\t}\n-\t\tslist.list.cdr = new(Slist);\n-\t\tslist = slist.list.cdr;\n+\t\tslist.list.cdr = new(Slist)
+\t\tslist = slist.list.cdr
\t}\n-\treturn retval;\n+\treturn retval
}\n
-func atom(i int) *Slist\t{ // BUG: uses tokenbuf; should take argument)
-\tvar slist *Slist;\n+func atom(i int) *Slist { // BUG: uses tokenbuf; should take argument)
+\tvar slist *Slist
-\tslist = new(Slist);\n+\tslist = new(Slist)
\tif token == '0' {\n-\t\tslist.atom.integer = i;\n-\t\tslist.isstring = false;\n+\t\tslist.atom.integer = i
+\t\tslist.isstring = false
\t} else {\n-\t\tslist.atom.str = string(tokenbuf[0:tokenlen]);
-\t\tslist.isstring = true;\n+\t\tslist.atom.str = string(tokenbuf[0:tokenlen])
+\t\tslist.isstring = true
\t}\n-\tslist.isatom = true;\n-\treturn slist;\n+\tslist.isatom = true
+\treturn slist
}\n
-func atoi() int\t{ // BUG: uses tokenbuf; should take argument)
-\tvar v int = 0;\n+func atoi() int { // BUG: uses tokenbuf; should take argument)
+\tvar v int = 0
\tfor i := 0; i < tokenlen && '0' <= tokenbuf[i] && tokenbuf[i] <= '9'; i = i + 1 {\n-\t\tv = 10 * v + int(tokenbuf[i] - '0');
+\t\tv = 10*v + int(tokenbuf[i]-'0')
\t}\n-\treturn v;\n+\treturn v
}\n
func Parse() *Slist {\n-\tvar slist *Slist;\n+\tvar slist *Slist
\tif token == EOF || token == ')' {\n-\t\treturn nil;\n+\t\treturn nil
\t}\n \tif token == '(' {\n-\t\tNextToken();
-\t\tslist = ParseList();
-\t\tExpect(')');
-\t\treturn slist;\n+\t\tNextToken()
+\t\tslist = ParseList()
+\t\tExpect(')')
+\t\treturn slist
\t} else {\n \t\t// Atom\n \t\tswitch token {\n \t\tcase EOF:\n-\t\t\treturn nil;\n+\t\t\treturn nil
\t\tcase '0':\n-\t\t\tslist = atom(atoi());
+\t\t\tslist = atom(atoi())
\t\tcase '"', 'A':\n-\t\t\tslist = atom(0);
+\t\t\tslist = atom(0)
\t\tdefault:\n-\t\t\tslist = nil;\n-\t\t\tprint("unknown token: ", token, "\n");
+\t\t\tslist = nil
+\t\t\tprint("unknown token: ", token, "\n")
\t\t}\n-\t\tNextToken();
-\t\treturn slist;\n+\t\tNextToken()
+\t\treturn slist
\t}\n-\treturn nil;\n+\treturn nil
}\n
func OpenFile() {\n-\tinput = "(defn foo (add 12 34))\n\x00";
-\tinputindex = 0;
-\tpeekc = -1;\t\t// BUG
-\tNextToken();
+\tinput = "(defn foo (add 12 34))\n\x00"
+\tinputindex = 0
+\tpeekc = -1 // BUG
+\tNextToken()
}\n```
## コアとなるコードの解説
上記の変更箇所は、`gofmt` がGo言語のコードに対して適用する典型的な整形ルールを示しています。
1. **セミコロンの削除**:
* 変更前: `nelem int;` や `v := new(Vector);` のように、各文の終わりにセミコロンが明示的に記述されています。
* 変更後: `nelem int` や `v := new(Vector)` のように、セミコロンが削除されています。Go言語では、改行によって文の終わりが自動的に認識されるため、セミコロンは通常不要です。`gofmt` はこのルールを強制し、コードをより簡潔にします。
2. **構造体フィールドのアライメント**:
* 変更前: `type Vector struct { nelem int; elem []Element; }` のように、構造体のフィールド宣言が揃っていません。
* 変更後: `type Vector struct { nelem int; elem []Element }` のように、`elem` の前にスペースが追加され、`nelem` の `int` と縦に揃えられています。これにより、構造体の定義が視覚的に整理され、フィールドとその型がより読みやすくなります。これは `gofmt` の特徴的な整形の一つです。
3. **`if` 文や `for` 文のブロックの整形**:
* 変更前: `if k != 0 { panic("k not zero") }` のように、短いブロックが一行にまとめられています。
* 変更後: `if k != 0 { \n\tpanic("k not zero") \n}` のように、ブロック内のステートメントが新しい行に移動し、適切なインデントが適用されています。これにより、コードの構造が明確になり、ネストされたロジックが視覚的に理解しやすくなります。
4. **`switch` 文の括弧の削除**:
* `test/ken/simpswitch.go` の変更で顕著ですが、`switch(i)` が `switch i` に変更されています。Go言語の `switch` 文では、条件式を括弧で囲む必要はありません。これは他のC系の言語とは異なる点であり、`gofmt` はこのGoの慣習に従って冗長な括弧を削除します。
これらの変更は、コードの機能には影響を与えませんが、Go言語の標準的なコーディングスタイルに準拠させることで、コードベース全体の統一性を高め、可読性と保守性を向上させる効果があります。
## 関連リンク
* [Effective Go - Formatting](https://go.dev/doc/effective_go#formatting): Go言語の公式ドキュメントにおける `gofmt` とフォーマットに関する説明。
* [Go Fmt](https://pkg.go.dev/cmd/gofmt): `gofmt` コマンドの公式ドキュメント。
## 参考にした情報源リンク
* 上記の「関連リンク」に記載された公式ドキュメント。
* Go言語の一般的なコーディング規約と `gofmt` の動作に関する知識。
* Gitのコミットプラクティスに関する一般的な知識。