github.com/Johnny2210/revive@v1.0.8-0.20210625134200-febf37ccd0f5/rule/range.go (about) 1 package rule 2 3 import ( 4 "fmt" 5 "go/ast" 6 "strings" 7 8 "github.com/mgechev/revive/lint" 9 ) 10 11 // RangeRule lints given else constructs. 12 type RangeRule struct{} 13 14 // Apply applies the rule to given file. 15 func (r *RangeRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { 16 var failures []lint.Failure 17 18 onFailure := func(failure lint.Failure) { 19 failures = append(failures, failure) 20 } 21 22 w := &lintRanges{file, onFailure} 23 ast.Walk(w, file.AST) 24 return failures 25 } 26 27 // Name returns the rule name. 28 func (r *RangeRule) Name() string { 29 return "range" 30 } 31 32 type lintRanges struct { 33 file *lint.File 34 onFailure func(lint.Failure) 35 } 36 37 func (w *lintRanges) Visit(node ast.Node) ast.Visitor { 38 rs, ok := node.(*ast.RangeStmt) 39 if !ok { 40 return w 41 } 42 if rs.Value == nil { 43 // for x = range m { ... } 44 return w // single var form 45 } 46 if !isIdent(rs.Value, "_") { 47 // for ?, y = range m { ... } 48 return w 49 } 50 51 newRS := *rs // shallow copy 52 newRS.Value = nil 53 54 w.onFailure(lint.Failure{ 55 Failure: fmt.Sprintf("should omit 2nd value from range; this loop is equivalent to `for %s %s range ...`", w.file.Render(rs.Key), rs.Tok), 56 Confidence: 1, 57 Node: rs.Value, 58 ReplacementLine: firstLineOf(w.file, &newRS, rs), 59 }) 60 61 return w 62 } 63 64 func firstLineOf(f *lint.File, node, match ast.Node) string { 65 line := f.Render(node) 66 if i := strings.Index(line, "\n"); i >= 0 { 67 line = line[:i] 68 } 69 return indentOf(f, match) + line 70 } 71 72 func indentOf(f *lint.File, node ast.Node) string { 73 line := srcLine(f.Content(), f.ToPosition(node.Pos())) 74 for i, r := range line { 75 switch r { 76 case ' ', '\t': 77 default: 78 return line[:i] 79 } 80 } 81 return line // unusual or empty line 82 }