Skip to content

Commit df7baa0

Browse files
committed
gopls/internal/analysis/simplifyrange: more precise fix
This CL reduces the size of the fix offered by simplifyrange, which makes the cursor jump less. It's also simpler, and handles a missing case. Change-Id: I8dbff96158e442b2073e86694b61ea1f0b1ea704 Reviewed-on: https://go-review.googlesource.com/c/tools/+/649355 Reviewed-by: Robert Findley <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent ad5dd98 commit df7baa0

File tree

4 files changed

+51
-67
lines changed

4 files changed

+51
-67
lines changed

gopls/internal/analysis/simplifyrange/simplifyrange.go

Lines changed: 33 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,8 @@
55
package simplifyrange
66

77
import (
8-
"bytes"
98
_ "embed"
109
"go/ast"
11-
"go/printer"
1210
"go/token"
1311

1412
"golang.org/x/tools/go/analysis"
@@ -42,73 +40,48 @@ func run(pass *analysis.Pass) (interface{}, error) {
4240
(*ast.RangeStmt)(nil),
4341
}
4442
inspect.Preorder(nodeFilter, func(n ast.Node) {
45-
if _, ok := generated[pass.Fset.File(n.Pos())]; ok {
46-
return // skip checking if it's generated code
47-
}
43+
rng := n.(*ast.RangeStmt)
4844

49-
var copy *ast.RangeStmt // shallow-copy the AST before modifying
50-
{
51-
x := *n.(*ast.RangeStmt)
52-
copy = &x
53-
}
54-
end := newlineIndex(pass.Fset, copy)
45+
kblank := isBlank(rng.Key)
46+
vblank := isBlank(rng.Value)
47+
var start, end token.Pos
48+
switch {
49+
case kblank && (rng.Value == nil || vblank):
50+
// for _ = range x {}
51+
// for _, _ = range x {}
52+
// ^^^^^^^
53+
start, end = rng.Key.Pos(), rng.Range
5554

56-
// Range statements of the form: for i, _ := range x {}
57-
var old ast.Expr
58-
if isBlank(copy.Value) {
59-
old = copy.Value
60-
copy.Value = nil
61-
}
62-
// Range statements of the form: for _ := range x {}
63-
if isBlank(copy.Key) && copy.Value == nil {
64-
old = copy.Key
65-
copy.Key = nil
55+
case vblank:
56+
// for k, _ := range x {}
57+
// ^^^
58+
start, end = rng.Key.End(), rng.Value.End()
59+
60+
default:
61+
return
6662
}
67-
// Return early if neither if condition is met.
68-
if old == nil {
63+
64+
if generated[pass.Fset.File(n.Pos())] {
6965
return
7066
}
67+
7168
pass.Report(analysis.Diagnostic{
72-
Pos: old.Pos(),
73-
End: old.End(),
74-
Message: "simplify range expression",
75-
SuggestedFixes: suggestedFixes(pass.Fset, copy, end),
69+
Pos: start,
70+
End: end,
71+
Message: "simplify range expression",
72+
SuggestedFixes: []analysis.SuggestedFix{{
73+
Message: "Remove empty value",
74+
TextEdits: []analysis.TextEdit{{
75+
Pos: start,
76+
End: end,
77+
}},
78+
}},
7679
})
7780
})
7881
return nil, nil
7982
}
8083

81-
func suggestedFixes(fset *token.FileSet, rng *ast.RangeStmt, end token.Pos) []analysis.SuggestedFix {
82-
var b bytes.Buffer
83-
printer.Fprint(&b, fset, rng)
84-
stmt := b.Bytes()
85-
index := bytes.Index(stmt, []byte("\n"))
86-
// If there is a new line character, then don't replace the body.
87-
if index != -1 {
88-
stmt = stmt[:index]
89-
}
90-
return []analysis.SuggestedFix{{
91-
Message: "Remove empty value",
92-
TextEdits: []analysis.TextEdit{{
93-
Pos: rng.Pos(),
94-
End: end,
95-
NewText: stmt[:index],
96-
}},
97-
}}
98-
}
99-
100-
func newlineIndex(fset *token.FileSet, rng *ast.RangeStmt) token.Pos {
101-
var b bytes.Buffer
102-
printer.Fprint(&b, fset, rng)
103-
contents := b.Bytes()
104-
index := bytes.Index(contents, []byte("\n"))
105-
if index == -1 {
106-
return rng.End()
107-
}
108-
return rng.Pos() + token.Pos(index)
109-
}
110-
111-
func isBlank(x ast.Expr) bool {
112-
ident, ok := x.(*ast.Ident)
113-
return ok && ident.Name == "_"
84+
func isBlank(e ast.Expr) bool {
85+
id, ok := e.(*ast.Ident)
86+
return ok && id.Name == "_"
11487
}

gopls/internal/analysis/simplifyrange/simplifyrange_test.go

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,15 @@
55
package simplifyrange_test
66

77
import (
8-
"go/build"
9-
"slices"
108
"testing"
119

1210
"golang.org/x/tools/go/analysis/analysistest"
1311
"golang.org/x/tools/gopls/internal/analysis/simplifyrange"
1412
)
1513

1614
func Test(t *testing.T) {
17-
testdata := analysistest.TestData()
18-
analysistest.RunWithSuggestedFixes(t, testdata, simplifyrange.Analyzer, "a", "generatedcode")
19-
if slices.Contains(build.Default.ReleaseTags, "go1.23") { // uses iter.Seq
20-
analysistest.RunWithSuggestedFixes(t, testdata, simplifyrange.Analyzer, "rangeoverfunc")
21-
}
15+
analysistest.RunWithSuggestedFixes(t, analysistest.TestData(), simplifyrange.Analyzer,
16+
"a",
17+
"generatedcode",
18+
"rangeoverfunc")
2219
}

gopls/internal/analysis/simplifyrange/testdata/src/a/a.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,11 @@ func m() {
1313
}
1414
for _ = range maps { // want "simplify range expression"
1515
}
16+
for _, _ = range maps { // want "simplify range expression"
17+
}
18+
for _, v := range maps { // nope
19+
println(v)
20+
}
21+
for range maps { // nope
22+
}
1623
}

gopls/internal/analysis/simplifyrange/testdata/src/a/a.go.golden

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,11 @@ func m() {
1313
}
1414
for range maps { // want "simplify range expression"
1515
}
16+
for range maps { // want "simplify range expression"
17+
}
18+
for _, v := range maps { // nope
19+
println(v)
20+
}
21+
for range maps { // nope
22+
}
1623
}

0 commit comments

Comments
 (0)