[インデックス 19648] ファイルの概要
このコミットは、Go言語の公式フォーマッタであるgofmt
のバグ修正に関するものです。具体的には、gofmt -s
(簡略化オプション)が3インデックススライスを誤って簡略化してしまう問題を修正しています。
コミット
commit dddc8b193fdd548061bb9f77b9395e6417a97cb6
Author: Robert Griesemer <gri@golang.org>
Date: Tue Jul 1 10:40:27 2014 -0700
cmd/gofmt: fix gofmt -s for 3-index slices
3-index slices of the form s[:len(s):len(s)]
cannot be simplified to s[::len(s)].
LGTM=bradfitz
R=golang-codereviews, bradfitz
CC=golang-codereviews
https://golang.org/cl/108330043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/dddc8b193fdd548061bb9f77b9395e6417a97cb6
元コミット内容
cmd/gofmt: fix gofmt -s for 3-index slices
gofmt -s
が3インデックススライス、特にs[:len(s):len(s)]
のような形式のものをs[::len(s)]
に簡略化できない問題を修正します。
変更の背景
Go言語のスライスは、配列の一部を参照するための強力な機能です。通常、スライスはs[low:high]
という2つのインデックスで表現されますが、Go 1.2からs[low:high:capacity]
という3つのインデックスを持つスライス式が導入されました。この3インデックススライスは、スライスの長さ(high - low
)だけでなく、その基盤となる配列の容量(capacity - low
)も指定できるため、より細かなメモリ管理やスライスの再スライス時の挙動を制御するのに役立ちます。
gofmt
はGoコードを整形するための標準ツールであり、-s
オプションを使用すると、コードをより簡潔な形式に自動的に書き換える「簡略化」機能を提供します。例えば、s[0:len(s)]
はs[:]
に簡略化されるといった具合です。
このコミット以前のgofmt -s
は、3インデックススライスの一部を誤って簡略化しようとしていました。具体的には、s[:len(s):len(s)]
という形式のスライス式を、s[::len(s)]
という形式に簡略化しようとしていました。しかし、Go言語の仕様では、3インデックススライスにおいて最初のインデックス(low
)が省略された場合、それは常に0
を意味します。したがって、s[::len(s)]
はs[0:len(s):len(s)]
と同じ意味になります。
問題は、s[:len(s):len(s)]
とs[0:len(s):len(s)]
は同じ意味であるにもかかわらず、gofmt -s
がこれをs[::len(s)]
に簡略化しようとした際に、s[::len(s)]
が常に有効な簡略化ではないケースが存在したことです。特に、s[:len(s):len(s)]
は、スライスの長さと容量が同じであることを明示的に示していますが、s[::len(s)]
は、low
が0
であることを前提としています。この簡略化が常に安全ではないため、gofmt
はこのようなケースで誤った簡略化を行わないように修正する必要がありました。
このバグは、gofmt -s
がコードのセマンティクスを変更してしまう可能性があり、Go開発者にとっては非常に重要な問題でした。そのため、この修正はgofmt
の信頼性と正確性を保つ上で不可欠でした。
前提知識の解説
Go言語のスライス
Go言語のスライスは、同じ型の要素の連続したシーケンスを表すデータ構造です。スライスは配列の上に構築されており、配列の一部を参照します。スライスは、長さ(len
)と容量(cap
)という2つの重要なプロパティを持っています。
- 長さ (Length): スライスに含まれる要素の数。
len(s)
で取得できます。 - 容量 (Capacity): スライスの基盤となる配列の、スライスが参照している開始位置から末尾までの要素の数。
cap(s)
で取得できます。
スライス式
Goのスライス式には、主に以下の2つの形式があります。
-
2インデックススライス:
a[low : high]
low
からhigh-1
までの要素を含む新しいスライスを作成します。low
が省略された場合、デフォルトは0
です。high
が省略された場合、デフォルトは元のスライスの長さ(len(a)
)です。- 新しいスライスの長さは
high - low
、容量はcap(a) - low
です。
-
3インデックススライス:
a[low : high : capacity]
(Go 1.2以降)low
からhigh-1
までの要素を含む新しいスライスを作成します。capacity
は、新しいスライスの容量を明示的に設定します。新しいスライスの容量はcapacity - low
になります。low
が省略された場合、デフォルトは0
です。high
が省略された場合、デフォルトは元のスライスの長さ(len(a)
)です。capacity
はhigh
以上でなければなりません。- この形式は、特にスライスの再スライス時に、基盤となる配列のどの範囲までアクセス可能かを厳密に制御したい場合に有用です。例えば、元のスライスの容量の一部だけを新しいスライスに「公開」したい場合などに使われます。
gofmt
と-s
オプション
gofmt
は、Go言語のソースコードを標準的なスタイルに自動的に整形するツールです。Goコミュニティでは、gofmt
によって整形されたコードが標準とされており、これによりコードの一貫性が保たれ、可読性が向上します。
-s
オプションは、gofmt
にコードの「簡略化」を指示します。これは、冗長な表現をより簡潔な同等の表現に書き換える機能です。例えば、以下のような簡略化が行われます。
x[0:len(x)]
→x[:]
x[low:len(x)]
→x[low:]
for _ = range x
→for range x
if x { return true } else { return false }
→return x
-s
オプションは、コードのセマンティクスを変えることなく、より慣用的なGoのスタイルに近づけることを目的としています。
GoのAST (Abstract Syntax Tree)
Goコンパイラやgofmt
のようなツールは、Goのソースコードを直接操作するのではなく、まずソースコードを抽象構文木(AST: Abstract Syntax Tree)にパースします。ASTは、プログラムの構造を木構造で表現したものです。各ノードは、式、文、宣言などのプログラムの構成要素に対応します。
go/ast
パッケージは、GoのASTを表現するための型と関数を提供します。gofmt
は、このASTを走査(traverse)し、特定のパターンにマッチするノードを見つけて、それを簡略化された形式のノードに置き換えることでコードの整形や簡略化を行います。
ast.Node
: ASTのすべてのノードが実装するインターフェース。ast.Visitor
: ASTを走査するためのインターフェース。Visit
メソッドを持ち、各ノードを訪問する際に呼び出されます。ast.SliceExpr
: スライス式を表すASTノード。X
(スライスされる式)、Low
、High
、Max
(3インデックススライスのcapacity
部分)などのフィールドを持ちます。ast.Ident
: 識別子(変数名、関数名など)を表すASTノード。
技術的詳細
このコミットの核心は、gofmt
の簡略化ロジックが3インデックススライスを扱う際の不正確さを修正することにあります。
Goのスライス式s[low:high:max]
において、low
が省略された場合、それは0
を意味します。つまり、s[:high:max]
はs[0:high:max]
と等価です。
同様に、high
が省略された場合、それはlen(s)
を意味します。つまり、s[low::max]
はs[low:len(s):max]
と等価です。
問題となっていたのは、gofmt -s
がs[:len(s):len(s)]
という形式のスライス式を簡略化しようとした際に、これをs[::len(s)]
に変換しようとしていた点です。
ここで重要なのは、s[:len(s):len(s)]
は、low
が省略されているため0
と解釈され、high
がlen(s)
、max
がlen(s)
であるスライス式です。つまり、s[0:len(s):len(s)]
と等価です。
一方、s[::len(s)]
は、low
が省略されているため0
と解釈され、high
も省略されているためlen(s)
と解釈され、max
がlen(s)
であるスライス式です。つまり、これもs[0:len(s):len(s)]
と等価です。
一見すると、両者は同じ意味に見えます。しかし、gofmt -s
の簡略化の目的は、コードをより簡潔にすることであり、その過程でセマンティクスを変更してはなりません。このコミット以前のgofmt
の簡略化ロジックでは、s[:len(s):len(s)]
のような形式のスライス式を簡略化する際に、Max
フィールド(3番目のインデックス)が存在する場合の考慮が不足していました。
simplify.go
内のVisit
メソッドは、ASTノードを走査し、簡略化可能なスライス式を見つけると変換を行います。以前のコードでは、n.Max != nil
(3番目のインデックスが存在する場合)のチェックが、簡略化をスキップする条件として適切に組み込まれていませんでした。
この修正は、n.Max != nil
の場合、つまり3インデックススライスの場合には、特定の簡略化(特にs[low:len(s)]
をs[low:]
に簡略化するようなケース)を行わないようにすることで、この問題を解決しています。なぜなら、3インデックススライスでは、high
とmax
の値が同じであっても、その明示的な指定がコードの意図を伝える上で重要である場合があるため、安易な簡略化は避けるべきだからです。
また、len()
関数が組み込み関数ではなく、ドットインポートによって導入された別のパッケージの関数である可能性(s.hasDotImport
)も考慮されています。この場合も、len()
が組み込みのlen()
関数であると断定できないため、簡略化は行われません。これは、gofmt
がコードのセマンティクスを正確に理解できない場合に、安全側に倒して簡略化を避けるという原則に基づいています。
この修正により、gofmt -s
は3インデックススライスを正しく扱い、コードのセマンティクスを損なうことなく、安全な簡略化のみを行うようになりました。
コアとなるコードの変更箇所
変更はsrc/cmd/gofmt/simplify.go
ファイルに集中しています。
--- a/src/cmd/gofmt/simplify.go
+++ b/src/cmd/gofmt/simplify.go
@@ -68,9 +68,10 @@ func (s *simplifier) Visit(node ast.Node) ast.Visitor {
// a slice expression of the form: s[a:len(s)]
// can be simplified to: s[a:]
// if s is "simple enough" (for now we only accept identifiers)
- if s.hasDotImport {
- // if dot imports are present, we cannot be certain that an
- // unresolved "len" identifier refers to the predefined len()
+ if n.Max != nil || s.hasDotImport {
+ // - 3-index slices always require the 2nd and 3rd index
+ // - if dot imports are present, we cannot be certain that an
+ // unresolved "len" identifier refers to the predefined len()
break
}
if s, _ := n.X.(*ast.Ident); s != nil && s.Obj != nil {
テストデータも追加されています。
src/cmd/gofmt/testdata/slices1.golden
src/cmd/gofmt/testdata/slices1.input
これらのテストファイルは、3インデックススライスがgofmt -s
によって誤って簡略化されないことを確認するために、新しいテストケースを追加しています。
コアとなるコードの解説
変更されたsimplify.go
のコードは、simplifier
構造体のVisit
メソッド内にあります。このメソッドは、ASTを走査し、各ノードに対して簡略化のロジックを適用します。
変更前のコードでは、スライス式s[a:len(s)]
をs[a:]
に簡略化する際に、s.hasDotImport
(ドットインポートが存在するかどうか)のみをチェックしていました。ドットインポートが存在する場合、len
が組み込み関数ではなく、別のパッケージからインポートされた関数である可能性があるため、簡略化はスキップされていました。
変更後のコードでは、この条件にn.Max != nil
が追加されています。
if n.Max != nil || s.hasDotImport {
// - 3-index slices always require the 2nd and 3rd index
// - if dot imports are present, we cannot be certain that an
// unresolved "len" identifier refers to the predefined len()
break
}
n.Max != nil
: これは、現在処理しているast.SliceExpr
ノードが3インデックススライス(つまり、capacity
部分が指定されているスライス)であることを意味します。- コメントに「3-index slices always require the 2nd and 3rd index」とあるように、3インデックススライスの場合、2番目と3番目のインデックス(
high
とmax
)は常に明示的に指定されていると見なされます。たとえhigh
がlen(s)
であっても、その明示的な存在が重要であるため、s[a:len(s):len(s)]
のような形式をs[a::len(s)]
のように簡略化することは避けるべきです。この条件が追加されたことで、gofmt -s
は3インデックススライスに対して、high
がlen(s)
であるからといって安易にhigh
を省略するような簡略化を行わなくなりました。これにより、コードの意図がより明確に保たれます。
- コメントに「3-index slices always require the 2nd and 3rd index」とあるように、3インデックススライスの場合、2番目と3番目のインデックス(
s.hasDotImport
: これは以前から存在していた条件で、ドットインポートが存在する場合にlen
が組み込み関数であるかどうかの判断が難しくなるため、簡略化をスキップします。
このif
文の条件がtrue
の場合、break
が実行され、現在のスライス式の簡略化処理が中断されます。これにより、gofmt -s
は、3インデックススライスやドットインポートが存在する状況で、誤った、あるいは意図しない簡略化を行うことを防ぎます。
この修正は、gofmt
がGo言語の進化(3インデックススライスの導入)に追従し、その簡略化機能が常に安全で正確であることを保証するための重要なステップでした。
関連リンク
- Go Slices: usage and internals: https://go.dev/blog/slices
- Go 1.2 Release Notes (Slices with a three-index slice expression): https://go.dev/doc/go1.2#three_index
gofmt
command documentation: https://pkg.go.dev/cmd/gofmtgo/ast
package documentation: https://pkg.go.dev/go/ast
参考にした情報源リンク
- https://github.com/golang/go/commit/dddc8b193fdd548061bb9f77b9395e6417a97cb6
- https://golang.org/cl/108330043
- Go言語の公式ドキュメント (Go Slices: usage and internals, Go 1.2 Release Notes)
- Go言語の
go/ast
パッケージのドキュメント - Go言語の
gofmt
コマンドのドキュメント - 一般的なプログラミングにおける抽象構文木(AST)に関する情報I have generated the detailed explanation. I will now output it to standard output.
# [インデックス 19648] ファイルの概要
このコミットは、Go言語の公式フォーマッタである`gofmt`のバグ修正に関するものです。具体的には、`gofmt -s`(簡略化オプション)が3インデックススライスを誤って簡略化してしまう問題を修正しています。
## コミット
commit dddc8b193fdd548061bb9f77b9395e6417a97cb6 Author: Robert Griesemer gri@golang.org Date: Tue Jul 1 10:40:27 2014 -0700
cmd/gofmt: fix gofmt -s for 3-index slices
3-index slices of the form s[:len(s):len(s)]
cannot be simplified to s[::len(s)].
LGTM=bradfitz
R=golang-codereviews, bradfitz
CC=golang-codereviews
https://golang.org/cl/108330043
## GitHub上でのコミットページへのリンク
[https://github.com/golang/go/commit/dddc8b193fdd548061bb9f77b9395e6417a97cb6](https://github.com/golang/go/commit/dddc8b193fdd548061bb9f77b9395e6417a97cb6)
## 元コミット内容
`cmd/gofmt: fix gofmt -s for 3-index slices`
`gofmt -s`が3インデックススライス、特に`s[:len(s):len(s)]`のような形式のものを`s[::len(s)]`に簡略化できない問題を修正します。
## 変更の背景
Go言語のスライスは、配列の一部を参照するための強力な機能です。通常、スライスは`s[low:high]`という2つのインデックスで表現されますが、Go 1.2から`s[low:high:capacity]`という3つのインデックスを持つスライス式が導入されました。この3インデックススライスは、スライスの長さ(`high - low`)だけでなく、その基盤となる配列の容量(`capacity - low`)も指定できるため、より細かなメモリ管理やスライスの再スライス時の挙動を制御するのに役立ちます。
`gofmt`はGoコードを整形するための標準ツールであり、`-s`オプションを使用すると、コードをより簡潔な形式に自動的に書き換える「簡略化」機能を提供します。例えば、`s[0:len(s)]`は`s[:]`に簡略化されるといった具合です。
このコミット以前の`gofmt -s`は、3インデックススライスの一部を誤って簡略化しようとしていました。具体的には、`s[:len(s):len(s)]`という形式のスライス式を、`s[::len(s)]`という形式に簡略化しようとしていました。しかし、Go言語の仕様では、3インデックススライスにおいて最初のインデックス(`low`)が省略された場合、それは常に`0`を意味します。したがって、`s[::len(s)]`は`s[0:len(s):len(s)]`と同じ意味になります。
問題は、`s[:len(s):len(s)]`と`s[0:len(s):len(s)]`は同じ意味であるにもかかわらず、`gofmt -s`がこれを`s[::len(s)]`に簡略化しようとした際に、`s[::len(s)]`が常に有効な簡略化ではないケースが存在したことです。特に、`s[:len(s):len(s)]`は、スライスの長さと容量が同じであることを明示的に示していますが、`s[::len(s)]`は、`low`が`0`であることを前提としています。この簡略化が常に安全ではないため、`gofmt`はこのようなケースで誤った簡略化を行わないように修正する必要がありました。
このバグは、`gofmt -s`がコードのセマンティクスを変更してしまう可能性があり、Go開発者にとっては非常に重要な問題でした。そのため、この修正は`gofmt`の信頼性と正確性を保つ上で不可欠でした。
## 前提知識の解説
### Go言語のスライス
Go言語のスライスは、同じ型の要素の連続したシーケンスを表すデータ構造です。スライスは配列の上に構築されており、配列の一部を参照します。スライスは、長さ(`len`)と容量(`cap`)という2つの重要なプロパティを持っています。
* **長さ (Length)**: スライスに含まれる要素の数。`len(s)`で取得できます。
* **容量 (Capacity)**: スライスの基盤となる配列の、スライスが参照している開始位置から末尾までの要素の数。`cap(s)`で取得できます。
### スライス式
Goのスライス式には、主に以下の2つの形式があります。
1. **2インデックススライス**: `a[low : high]`
* `low`から`high-1`までの要素を含む新しいスライスを作成します。
* `low`が省略された場合、デフォルトは`0`です。
* `high`が省略された場合、デフォルトは元のスライスの長さ(`len(a)`)です。
* 新しいスライスの長さは`high - low`、容量は`cap(a) - low`です。
2. **3インデックススライス**: `a[low : high : capacity]` (Go 1.2以降)
* `low`から`high-1`までの要素を含む新しいスライスを作成します。
* `capacity`は、新しいスライスの容量を明示的に設定します。新しいスライスの容量は`capacity - low`になります。
* `low`が省略された場合、デフォルトは`0`です。
* `high`が省略された場合、デフォルトは元のスライスの長さ(`len(a)`)です。
* `capacity`は`high`以上でなければなりません。
* この形式は、特にスライスの再スライス時に、基盤となる配列のどの範囲までアクセス可能かを厳密に制御したい場合に有用です。例えば、元のスライスの容量の一部だけを新しいスライスに「公開」したい場合などに使われます。
### `gofmt`と`-s`オプション
`gofmt`は、Go言語のソースコードを標準的なスタイルに自動的に整形するツールです。Goコミュニティでは、`gofmt`によって整形されたコードが標準とされており、これによりコードの一貫性が保たれ、可読性が向上します。
`-s`オプションは、`gofmt`にコードの「簡略化」を指示します。これは、冗長な表現をより簡潔な同等の表現に書き換える機能です。例えば、以下のような簡略化が行われます。
* `x[0:len(x)]` → `x[:]`
* `x[low:len(x)]` → `x[low:]`
* `for _ = range x` → `for range x`
* `if x { return true } else { return false }` → `return x`
`-s`オプションは、コードのセマンティクスを変えることなく、より慣用的なGoのスタイルに近づけることを目的としています。
### GoのAST (Abstract Syntax Tree)
Goコンパイラや`gofmt`のようなツールは、Goのソースコードを直接操作するのではなく、まずソースコードを抽象構文木(AST: Abstract Syntax Tree)にパースします。ASTは、プログラムの構造を木構造で表現したものです。各ノードは、式、文、宣言などのプログラムの構成要素に対応します。
`go/ast`パッケージは、GoのASTを表現するための型と関数を提供します。`gofmt`は、このASTを走査(traverse)し、特定のパターンにマッチするノードを見つけて、それを簡略化された形式のノードに置き換えることでコードの整形や簡略化を行います。
* `ast.Node`: ASTのすべてのノードが実装するインターフェース。
* `ast.Visitor`: ASTを走査するためのインターフェース。`Visit`メソッドを持ち、各ノードを訪問する際に呼び出されます。
* `ast.SliceExpr`: スライス式を表すASTノード。`X`(スライスされる式)、`Low`、`High`、`Max`(3インデックススライスの`capacity`部分)などのフィールドを持ちます。
* `ast.Ident`: 識別子(変数名、関数名など)を表すASTノード。
## 技術的詳細
このコミットの核心は、`gofmt`の簡略化ロジックが3インデックススライスを扱う際の不正確さを修正することにあります。
Goのスライス式`s[low:high:max]`において、`low`が省略された場合、それは`0`を意味します。つまり、`s[:high:max]`は`s[0:high:max]`と等価です。
同様に、`high`が省略された場合、それは`len(s)`を意味します。つまり、`s[low::max]`は`s[low:len(s):max]`と等価です。
問題となっていたのは、`gofmt -s`が`s[:len(s):len(s)]`という形式のスライス式を簡略化しようとした際に、これを`s[::len(s)]`に変換しようとしていた点です。
ここで重要なのは、`s[:len(s):len(s)]`は、`low`が省略されているため`0`と解釈され、`high`が`len(s)`、`max`が`len(s)`であるスライス式です。つまり、`s[0:len(s):len(s)]`と等価です。
一方、`s[::len(s)]`は、`low`が省略されているため`0`と解釈され、`high`も省略されているため`len(s)`と解釈され、`max`が`len(s)`であるスライス式です。つまり、これも`s[0:len(s):len(s)]`と等価です。
一見すると、両者は同じ意味に見えます。しかし、`gofmt -s`の簡略化の目的は、コードをより簡潔にすることであり、その過程でセマンティクスを変更してはなりません。このコミット以前の`gofmt`の簡略化ロジックでは、`s[:len(s):len(s)]`のような形式のスライス式を簡略化する際に、`Max`フィールド(3番目のインデックス)が存在する場合の考慮が不足していました。
`simplify.go`内の`Visit`メソッドは、ASTノードを走査し、簡略化可能なスライス式を見つけると変換を行います。以前のコードでは、`n.Max != nil`(3番目のインデックスが存在する場合)のチェックが、簡略化をスキップする条件として適切に組み込まれていませんでした。
この修正は、`n.Max != nil`の場合、つまり3インデックススライスの場合には、特定の簡略化(特に`s[low:len(s)]`を`s[low:]`に簡略化するようなケース)を行わないようにすることで、この問題を解決しています。なぜなら、3インデックススライスでは、`high`と`max`の値が同じであっても、その明示的な指定がコードの意図を伝える上で重要である場合があるため、安易な簡略化は避けるべきだからです。
また、`len()`関数が組み込み関数ではなく、ドットインポートによって導入された別のパッケージの関数である可能性(`s.hasDotImport`)も考慮されています。この場合も、`len()`が組み込みの`len()`関数であると断定できないため、簡略化は行われません。これは、`gofmt`がコードのセマンティクスを正確に理解できない場合に、安全側に倒して簡略化を避けるという原則に基づいています。
この修正により、`gofmt -s`は3インデックススライスを正しく扱い、コードのセマンティクスを損なうことなく、安全な簡略化のみを行うようになりました。
## コアとなるコードの変更箇所
変更は`src/cmd/gofmt/simplify.go`ファイルに集中しています。
```diff
--- a/src/cmd/gofmt/simplify.go
+++ b/src/cmd/gofmt/simplify.go
@@ -68,9 +68,10 @@ func (s *simplifier) Visit(node ast.Node) ast.Visitor {
// a slice expression of the form: s[a:len(s)]
// can be simplified to: s[a:]
// if s is "simple enough" (for now we only accept identifiers)
- if s.hasDotImport {
- // if dot imports are present, we cannot be certain that an
- // unresolved "len" identifier refers to the predefined len()
+ if n.Max != nil || s.hasDotImport {
+ // - 3-index slices always require the 2nd and 3rd index
+ // - if dot imports are present, we cannot be certain that an
+ // unresolved "len" identifier refers to the predefined len()
break
}
if s, _ := n.X.(*ast.Ident); s != nil && s.Obj != nil {
テストデータも追加されています。
src/cmd/gofmt/testdata/slices1.golden
src/cmd/gofmt/testdata/slices1.input
これらのテストファイルは、3インデックススライスがgofmt -s
によって誤って簡略化されないことを確認するために、新しいテストケースを追加しています。
コアとなるコードの解説
変更されたsimplify.go
のコードは、simplifier
構造体のVisit
メソッド内にあります。このメソッドは、ASTを走査し、各ノードに対して簡略化のロジックを適用します。
変更前のコードでは、スライス式s[a:len(s)]
をs[a:]
に簡略化する際に、s.hasDotImport
(ドットインポートが存在するかどうか)のみをチェックしていました。ドットインポートが存在する場合、len
が組み込み関数ではなく、別のパッケージからインポートされた関数である可能性があるため、簡略化はスキップされていました。
変更後のコードでは、この条件にn.Max != nil
が追加されています。
if n.Max != nil || s.hasDotImport {
// - 3-index slices always require the 2nd and 3rd index
// - if dot imports are present, we cannot be certain that an
// unresolved "len" identifier refers to the predefined len()
break
}
n.Max != nil
: これは、現在処理しているast.SliceExpr
ノードが3インデックススライス(つまり、capacity
部分が指定されているスライス)であることを意味します。- コメントに「3-index slices always require the 2nd and 3rd index」とあるように、3インデックススライスの場合、2番目と3番目のインデックス(
high
とmax
)は常に明示的に指定されていると見なされます。たとえhigh
がlen(s)
であっても、その明示的な存在が重要であるため、s[a:len(s):len(s)]
のような形式をs[a::len(s)]
のように簡略化することは避けるべきです。この条件が追加されたことで、gofmt -s
は3インデックススライスに対して、high
がlen(s)
であるからといって安易にhigh
を省略するような簡略化を行わなくなりました。これにより、コードの意図がより明確に保たれます。
- コメントに「3-index slices always require the 2nd and 3rd index」とあるように、3インデックススライスの場合、2番目と3番目のインデックス(
s.hasDotImport
: これは以前から存在していた条件で、ドットインポートが存在する場合にlen
が組み込み関数であるかどうかの判断が難しくなるため、簡略化をスキップします。
このif
文の条件がtrue
の場合、break
が実行され、現在のスライス式の簡略化処理が中断されます。これにより、gofmt -s
は、3インデックススライスやドットインポートが存在する状況で、誤った、あるいは意図しない簡略化を行うことを防ぎます。
この修正は、gofmt
がGo言語の進化(3インデックススライスの導入)に追従し、その簡略化機能が常に安全で正確であることを保証するための重要なステップでした。
関連リンク
- Go Slices: usage and internals: https://go.dev/blog/slices
- Go 1.2 Release Notes (Slices with a three-index slice expression): https://go.dev/doc/go1.2#three_index
gofmt
command documentation: https://pkg.go.dev/cmd/gofmtgo/ast
package documentation: https://pkg.go.dev/go/ast
参考にした情報源リンク
- https://github.com/golang/go/commit/dddc8b193fdd548061bb9f77b9395e6417a97cb6
- https://golang.org/cl/108330043
- Go言語の公式ドキュメント (Go Slices: usage and internals, Go 1.2 Release Notes)
- Go言語の
go/ast
パッケージのドキュメント - Go言語の
gofmt
コマンドのドキュメント - 一般的なプログラミングにおける抽象構文木(AST)に関する情報