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  }