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