github.com/pankona/gometalinter@v2.0.11+incompatible/_linters/src/honnef.co/go/tools/internal/sharedcheck/lint.go (about)

     1  package sharedcheck
     2  
     3  import (
     4  	"go/ast"
     5  	"go/types"
     6  
     7  	"honnef.co/go/tools/lint"
     8  	"honnef.co/go/tools/ssa"
     9  )
    10  
    11  func CheckRangeStringRunes(nodeFns map[ast.Node]*ssa.Function, j *lint.Job) {
    12  	fn := func(node ast.Node) bool {
    13  		rng, ok := node.(*ast.RangeStmt)
    14  		if !ok || !lint.IsBlank(rng.Key) {
    15  			return true
    16  		}
    17  		ssafn := nodeFns[rng]
    18  		if ssafn == nil {
    19  			return true
    20  		}
    21  		v, _ := ssafn.ValueForExpr(rng.X)
    22  
    23  		// Check that we're converting from string to []rune
    24  		val, _ := v.(*ssa.Convert)
    25  		if val == nil {
    26  			return true
    27  		}
    28  		Tsrc, ok := val.X.Type().(*types.Basic)
    29  		if !ok || Tsrc.Kind() != types.String {
    30  			return true
    31  		}
    32  		Tdst, ok := val.Type().(*types.Slice)
    33  		if !ok {
    34  			return true
    35  		}
    36  		TdstElem, ok := Tdst.Elem().(*types.Basic)
    37  		if !ok || TdstElem.Kind() != types.Int32 {
    38  			return true
    39  		}
    40  
    41  		// Check that the result of the conversion is only used to
    42  		// range over
    43  		refs := val.Referrers()
    44  		if refs == nil {
    45  			return true
    46  		}
    47  
    48  		// Expect two refs: one for obtaining the length of the slice,
    49  		// one for accessing the elements
    50  		if len(lint.FilterDebug(*refs)) != 2 {
    51  			// TODO(dh): right now, we check that only one place
    52  			// refers to our slice. This will miss cases such as
    53  			// ranging over the slice twice. Ideally, we'd ensure that
    54  			// the slice is only used for ranging over (without
    55  			// accessing the key), but that is harder to do because in
    56  			// SSA form, ranging over a slice looks like an ordinary
    57  			// loop with index increments and slice accesses. We'd
    58  			// have to look at the associated AST node to check that
    59  			// it's a range statement.
    60  			return true
    61  		}
    62  
    63  		j.Errorf(rng, "should range over string, not []rune(string)")
    64  
    65  		return true
    66  	}
    67  	for _, f := range j.Program.Files {
    68  		ast.Inspect(f, fn)
    69  	}
    70  }