Skip to content

Commit 528efda

Browse files
skewb1kgopherbot
authored andcommitted
gopls/internal/analysis/modernize/forvar: provide fix for second loop var
Fixes golang/go#74849 Change-Id: I850109577436d53843c6ef596e267d873416a243 Reviewed-on: https://go-review.googlesource.com/c/tools/+/692615 LUCI-TryBot-Result: Go LUCI <[email protected]> Auto-Submit: Alan Donovan <[email protected]> Reviewed-by: Alan Donovan <[email protected]> Reviewed-by: Robert Findley <[email protected]>
1 parent bdddfd5 commit 528efda

File tree

3 files changed

+96
-58
lines changed

3 files changed

+96
-58
lines changed

gopls/internal/analysis/modernize/forvar.go

Lines changed: 36 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -30,66 +30,50 @@ import (
3030
// is declared implicitly before executing the post statement and initialized to the
3131
// value of the previous iteration's variable at that moment.")
3232
func forvar(pass *analysis.Pass) {
33-
info := pass.TypesInfo
34-
3533
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
36-
for curFile := range filesUsing(inspect, info, "go1.22") {
34+
for curFile := range filesUsing(inspect, pass.TypesInfo, "go1.22") {
35+
astFile := curFile.Node().(*ast.File)
3736
for curLoop := range curFile.Preorder((*ast.RangeStmt)(nil)) {
38-
// in a range loop. Is the first statement var := var?
39-
// if so, is var one of the range vars, and is it defined
40-
// in the for statement?
41-
// If so, decide how much to delete.
4237
loop := curLoop.Node().(*ast.RangeStmt)
4338
if loop.Tok != token.DEFINE {
4439
continue
4540
}
46-
v, stmt := loopVarRedecl(loop.Body)
47-
if v == nil {
48-
continue // index is not redeclared
49-
}
50-
if (loop.Key == nil || !equalSyntax(loop.Key, v)) &&
51-
(loop.Value == nil || !equalSyntax(loop.Value, v)) {
52-
continue
41+
isLoopVarRedecl := func(assign *ast.AssignStmt) bool {
42+
for i, lhs := range assign.Lhs {
43+
if !(equalSyntax(lhs, assign.Rhs[i]) &&
44+
(equalSyntax(lhs, loop.Key) || equalSyntax(lhs, loop.Value))) {
45+
return false
46+
}
47+
}
48+
return true
5349
}
54-
astFile := curFile.Node().(*ast.File)
55-
edits := analysisinternal.DeleteStmt(pass.Fset, astFile, stmt, bug.Reportf)
56-
if len(edits) == 0 {
57-
bug.Reportf("forvar failed to delete statement")
58-
continue
59-
}
60-
remove := edits[0]
61-
diag := analysis.Diagnostic{
62-
Pos: remove.Pos,
63-
End: remove.End,
64-
Category: "forvar",
65-
Message: "copying variable is unneeded",
66-
SuggestedFixes: []analysis.SuggestedFix{{
67-
Message: "Remove unneeded redeclaration",
68-
TextEdits: []analysis.TextEdit{remove},
69-
}},
50+
// Have: for k, v := range x { stmts }
51+
//
52+
// Delete the prefix of stmts that are
53+
// of the form k := k; v := v; k, v := k, v; v, k := v, k.
54+
for _, stmt := range loop.Body.List {
55+
if assign, ok := stmt.(*ast.AssignStmt); ok &&
56+
assign.Tok == token.DEFINE &&
57+
len(assign.Lhs) == len(assign.Rhs) &&
58+
isLoopVarRedecl(assign) {
59+
60+
edits := analysisinternal.DeleteStmt(pass.Fset, astFile, stmt, bug.Reportf)
61+
if len(edits) > 0 {
62+
pass.Report(analysis.Diagnostic{
63+
Pos: stmt.Pos(),
64+
End: stmt.End(),
65+
Category: "forvar",
66+
Message: "copying variable is unneeded",
67+
SuggestedFixes: []analysis.SuggestedFix{{
68+
Message: "Remove unneeded redeclaration",
69+
TextEdits: edits,
70+
}},
71+
})
72+
}
73+
} else {
74+
break // stop at first other statement
75+
}
7076
}
71-
pass.Report(diag)
7277
}
7378
}
7479
}
75-
76-
// if the first statement is var := var, return var and the stmt
77-
func loopVarRedecl(body *ast.BlockStmt) (*ast.Ident, *ast.AssignStmt) {
78-
if len(body.List) < 1 {
79-
return nil, nil
80-
}
81-
stmt, ok := body.List[0].(*ast.AssignStmt)
82-
if !ok || !isSimpleAssign(stmt) || stmt.Tok != token.DEFINE {
83-
return nil, nil
84-
}
85-
if _, ok := stmt.Lhs[0].(*ast.Ident); !ok {
86-
return nil, nil
87-
}
88-
if _, ok := stmt.Rhs[0].(*ast.Ident); !ok {
89-
return nil, nil
90-
}
91-
if stmt.Lhs[0].(*ast.Ident).Name == stmt.Rhs[0].(*ast.Ident).Name {
92-
return stmt.Lhs[0].(*ast.Ident), stmt
93-
}
94-
return nil, nil
95-
}

gopls/internal/analysis/modernize/testdata/src/forvar/forvar.go

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,24 @@ func _(m map[int]int, s []int) {
1212
}
1313
for k, v := range m {
1414
k := k // want "copying variable is unneeded"
15-
v := v // nope: report only the first redeclaration
15+
v := v // want "copying variable is unneeded"
1616
go f(k)
1717
go f(v)
1818
}
19-
for _, v := range m {
19+
for k, v := range m {
2020
v := v // want "copying variable is unneeded"
21+
k := k // want "copying variable is unneeded"
22+
go f(k)
23+
go f(v)
24+
}
25+
for k, v := range m {
26+
k, v := k, v // want "copying variable is unneeded"
27+
go f(k)
28+
go f(v)
29+
}
30+
for k, v := range m {
31+
v, k := v, k // want "copying variable is unneeded"
32+
go f(k)
2133
go f(v)
2234
}
2335
for i := range s {
@@ -53,10 +65,25 @@ func _(m map[int]int, s []int) {
5365
v := i
5466
go f(v)
5567
}
56-
for i := range s {
68+
for k, v := range m { // nope, LHS and RHS differ
69+
v, k := k, v
70+
go f(k)
71+
go f(v)
72+
}
73+
for k, v := range m { // nope, not a simple redecl
74+
k, v, x := k, v, 1
75+
go f(k)
76+
go f(v)
77+
go f(x)
78+
}
79+
for i := range s { // nope, not a simple redecl
5780
i := (i)
5881
go f(i)
5982
}
83+
for i := range s { // nope, not a simple redecl
84+
i := i + 1
85+
go f(i)
86+
}
6087
}
6188

6289
func f(n int) {}

gopls/internal/analysis/modernize/testdata/src/forvar/forvar.go.golden

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,24 @@ func _(m map[int]int, s []int) {
1212
}
1313
for k, v := range m {
1414
// want "copying variable is unneeded"
15-
v := v // nope: report only the first redeclaration
15+
// want "copying variable is unneeded"
16+
go f(k)
17+
go f(v)
18+
}
19+
for k, v := range m {
20+
// want "copying variable is unneeded"
21+
// want "copying variable is unneeded"
1622
go f(k)
1723
go f(v)
1824
}
19-
for _, v := range m {
25+
for k, v := range m {
2026
// want "copying variable is unneeded"
27+
go f(k)
28+
go f(v)
29+
}
30+
for k, v := range m {
31+
// want "copying variable is unneeded"
32+
go f(k)
2133
go f(v)
2234
}
2335
for i := range s {
@@ -53,10 +65,25 @@ func _(m map[int]int, s []int) {
5365
v := i
5466
go f(v)
5567
}
56-
for i := range s {
68+
for k, v := range m { // nope, LHS and RHS differ
69+
v, k := k, v
70+
go f(k)
71+
go f(v)
72+
}
73+
for k, v := range m { // nope, not a simple redecl
74+
k, v, x := k, v, 1
75+
go f(k)
76+
go f(v)
77+
go f(x)
78+
}
79+
for i := range s { // nope, not a simple redecl
5780
i := (i)
5881
go f(i)
5982
}
83+
for i := range s { // nope, not a simple redecl
84+
i := i + 1
85+
go f(i)
86+
}
6087
}
6188

6289
func f(n int) {}

0 commit comments

Comments
 (0)