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 }