.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / honnef.co / go / tools@v0.1.1 / internal / sharedcheck / lint.go
1 package sharedcheck
2
3 import (
4         "go/ast"
5         "go/types"
6
7         "honnef.co/go/tools/go/ast/astutil"
8         "honnef.co/go/tools/go/ir"
9         "honnef.co/go/tools/go/ir/irutil"
10         "honnef.co/go/tools/internal/passes/buildir"
11
12         "golang.org/x/tools/go/analysis"
13 )
14
15 func CheckRangeStringRunes(pass *analysis.Pass) (interface{}, error) {
16         for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
17                 cb := func(node ast.Node) bool {
18                         rng, ok := node.(*ast.RangeStmt)
19                         if !ok || !astutil.IsBlank(rng.Key) {
20                                 return true
21                         }
22
23                         v, _ := fn.ValueForExpr(rng.X)
24
25                         // Check that we're converting from string to []rune
26                         val, _ := v.(*ir.Convert)
27                         if val == nil {
28                                 return true
29                         }
30                         Tsrc, ok := val.X.Type().Underlying().(*types.Basic)
31                         if !ok || Tsrc.Kind() != types.String {
32                                 return true
33                         }
34                         Tdst, ok := val.Type().(*types.Slice)
35                         if !ok {
36                                 return true
37                         }
38                         TdstElem, ok := Tdst.Elem().(*types.Basic)
39                         if !ok || TdstElem.Kind() != types.Int32 {
40                                 return true
41                         }
42
43                         // Check that the result of the conversion is only used to
44                         // range over
45                         refs := val.Referrers()
46                         if refs == nil {
47                                 return true
48                         }
49
50                         // Expect two refs: one for obtaining the length of the slice,
51                         // one for accessing the elements
52                         if len(irutil.FilterDebug(*refs)) != 2 {
53                                 // TODO(dh): right now, we check that only one place
54                                 // refers to our slice. This will miss cases such as
55                                 // ranging over the slice twice. Ideally, we'd ensure that
56                                 // the slice is only used for ranging over (without
57                                 // accessing the key), but that is harder to do because in
58                                 // IR form, ranging over a slice looks like an ordinary
59                                 // loop with index increments and slice accesses. We'd
60                                 // have to look at the associated AST node to check that
61                                 // it's a range statement.
62                                 return true
63                         }
64
65                         pass.Reportf(rng.Pos(), "should range over string, not []rune(string)")
66
67                         return true
68                 }
69                 if source := fn.Source(); source != nil {
70                         ast.Inspect(source, cb)
71                 }
72         }
73         return nil, nil
74 }