Skip to content

Commit 133beb8

Browse files
adonovangopherbot
authored andcommitted
go/analysis/passes/modernize: modernizer for iterator APIs
This CL adds the stditerators modernizer, which replaces loops of the form for i := 0; i < x.Len(); i++ { use(x.At(i)) } or their "range x.Len()" equivalent by for elem := range x.All() { use(x.At(i) } for various well-known std types. Publishing each new Analyzer symbol requires going through the proposal process, so we plan to do them in one batch per Go release. In the interim, we cannot make such symbols public, so we use a backdoor mechanism to export them to gopls. + test, doc, relnote Updates golang/go#75693 Change-Id: I3accd1925ad990b84500a9e77c3c589aeaeabfb0 Reviewed-on: https://go-review.googlesource.com/c/tools/+/708775 Reviewed-by: Robert Findley <rfindley@google.com> Auto-Submit: Alan Donovan <adonovan@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
1 parent 077f385 commit 133beb8

File tree

12 files changed

+580
-9
lines changed

12 files changed

+580
-9
lines changed

go/analysis/passes/modernize/bloop.go

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -117,15 +117,8 @@ func bloop(pass *analysis.Pass) (any, error) {
117117
// ...no references to i...
118118
// }
119119
body, _ := curLoop.LastChild()
120-
if assign, ok := n.Init.(*ast.AssignStmt); ok &&
121-
assign.Tok == token.DEFINE &&
122-
len(assign.Rhs) == 1 &&
123-
isZeroIntLiteral(info, assign.Rhs[0]) &&
124-
is[*ast.IncDecStmt](n.Post) &&
125-
n.Post.(*ast.IncDecStmt).Tok == token.INC &&
126-
equalSyntax(n.Post.(*ast.IncDecStmt).X, assign.Lhs[0]) &&
127-
!uses(index, body, info.Defs[assign.Lhs[0].(*ast.Ident)]) {
128-
120+
if v := isIncrementLoop(info, n); v != nil &&
121+
!uses(index, body, v) {
129122
delStart, delEnd = n.Init.Pos(), n.Post.End()
130123
}
131124

@@ -238,3 +231,18 @@ func isBenchmarkFunc(f *ast.FuncDecl) bool {
238231
f.Type.Params != nil &&
239232
len(f.Type.Params.List) == 1
240233
}
234+
235+
// isIncrementLoop reports whether loop has the form "for i := 0; ...; i++ { ... }",
236+
// and if so, it returns the symbol for the index variable.
237+
func isIncrementLoop(info *types.Info, loop *ast.ForStmt) *types.Var {
238+
if assign, ok := loop.Init.(*ast.AssignStmt); ok &&
239+
assign.Tok == token.DEFINE &&
240+
len(assign.Rhs) == 1 &&
241+
isZeroIntLiteral(info, assign.Rhs[0]) &&
242+
is[*ast.IncDecStmt](loop.Post) &&
243+
loop.Post.(*ast.IncDecStmt).Tok == token.INC &&
244+
equalSyntax(loop.Post.(*ast.IncDecStmt).X, assign.Lhs[0]) {
245+
return info.Defs[assign.Lhs[0].(*ast.Ident)].(*types.Var)
246+
}
247+
return nil
248+
}

go/analysis/passes/modernize/doc.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,25 @@ types. It replaces
270270
271271
with the simpler `slices.Sort(s)`, which was added in Go 1.21.
272272
273+
# Analyzer stditerators
274+
275+
stditerators: use iterators instead of Len/At-style APIs
276+
277+
This analyzer suggests a fix to replace each loop of the form:
278+
279+
for i := 0; i < x.Len(); i++ {
280+
use(x.At(i))
281+
}
282+
283+
or its "for elem := range x.Len()" equivalent by a range loop over an
284+
iterator offered by the same data type:
285+
286+
for elem := range x.All() {
287+
use(x.At(i)
288+
}
289+
290+
where x is one of various well-known types in the standard library.
291+
273292
# Analyzer stringscutprefix
274293
275294
stringscutprefix: replace HasPrefix/TrimPrefix with CutPrefix

go/analysis/passes/modernize/modernize.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ var Suite = []*analysis.Analyzer{
4545
SlicesContainsAnalyzer,
4646
// SlicesDeleteAnalyzer, // not nil-preserving!
4747
SlicesSortAnalyzer,
48+
stditeratorsAnalyzer,
4849
StringsCutPrefixAnalyzer,
4950
StringsSeqAnalyzer,
5051
StringsBuilderAnalyzer,

go/analysis/passes/modernize/modernize_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
. "golang.org/x/tools/go/analysis/analysistest"
1111
"golang.org/x/tools/go/analysis/passes/modernize"
12+
"golang.org/x/tools/internal/goplsexport"
1213
)
1314

1415
func TestAppendClipped(t *testing.T) {
@@ -31,6 +32,10 @@ func TestForVar(t *testing.T) {
3132
RunWithSuggestedFixes(t, TestData(), modernize.ForVarAnalyzer, "forvar")
3233
}
3334

35+
func TestStdIterators(t *testing.T) {
36+
RunWithSuggestedFixes(t, TestData(), goplsexport.StdIteratorsModernizer, "stditerators")
37+
}
38+
3439
func TestMapsLoop(t *testing.T) {
3540
RunWithSuggestedFixes(t, TestData(), modernize.MapsLoopAnalyzer, "mapsloop")
3641
}

0 commit comments

Comments
 (0)