|
5 | 5 | package simplifyrange
|
6 | 6 |
|
7 | 7 | import (
|
8 |
| - "bytes" |
9 | 8 | _ "embed"
|
10 | 9 | "go/ast"
|
11 |
| - "go/printer" |
12 | 10 | "go/token"
|
13 | 11 |
|
14 | 12 | "golang.org/x/tools/go/analysis"
|
@@ -42,73 +40,48 @@ func run(pass *analysis.Pass) (interface{}, error) {
|
42 | 40 | (*ast.RangeStmt)(nil),
|
43 | 41 | }
|
44 | 42 | 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) |
48 | 44 |
|
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 |
55 | 54 |
|
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 |
66 | 62 | }
|
67 |
| - // Return early if neither if condition is met. |
68 |
| - if old == nil { |
| 63 | + |
| 64 | + if generated[pass.Fset.File(n.Pos())] { |
69 | 65 | return
|
70 | 66 | }
|
| 67 | + |
71 | 68 | 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 | + }}, |
76 | 79 | })
|
77 | 80 | })
|
78 | 81 | return nil, nil
|
79 | 82 | }
|
80 | 83 |
|
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 == "_" |
114 | 87 | }
|
0 commit comments