.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / honnef.co / go / tools@v0.1.1 / analysis / code / code.go
1 // Package code answers structural and type questions about Go code.
2 package code
3
4 import (
5         "flag"
6         "fmt"
7         "go/ast"
8         "go/constant"
9         "go/token"
10         "go/types"
11         "strings"
12
13         "honnef.co/go/tools/analysis/facts"
14         "honnef.co/go/tools/go/types/typeutil"
15
16         "golang.org/x/tools/go/analysis"
17         "golang.org/x/tools/go/ast/astutil"
18 )
19
20 type Positioner interface {
21         Pos() token.Pos
22 }
23
24 func IsOfType(pass *analysis.Pass, expr ast.Expr, name string) bool {
25         return typeutil.IsType(pass.TypesInfo.TypeOf(expr), name)
26 }
27
28 func IsInTest(pass *analysis.Pass, node Positioner) bool {
29         // FIXME(dh): this doesn't work for global variables with
30         // initializers
31         f := pass.Fset.File(node.Pos())
32         return f != nil && strings.HasSuffix(f.Name(), "_test.go")
33 }
34
35 // IsMain reports whether the package being processed is a package
36 // main.
37 func IsMain(pass *analysis.Pass) bool {
38         return pass.Pkg.Name() == "main"
39 }
40
41 // IsMainLike reports whether the package being processed is a
42 // main-like package. A main-like package is a package that is
43 // package main, or that is intended to be used by a tool framework
44 // such as cobra to implement a command.
45 //
46 // Note that this function errs on the side of false positives; it may
47 // return true for packages that aren't main-like. IsMainLike is
48 // intended for analyses that wish to suppress diagnostics for
49 // main-like packages to avoid false positives.
50 func IsMainLike(pass *analysis.Pass) bool {
51         if pass.Pkg.Name() == "main" {
52                 return true
53         }
54         for _, imp := range pass.Pkg.Imports() {
55                 if imp.Path() == "github.com/spf13/cobra" {
56                         return true
57                 }
58         }
59         return false
60 }
61
62 func SelectorName(pass *analysis.Pass, expr *ast.SelectorExpr) string {
63         info := pass.TypesInfo
64         sel := info.Selections[expr]
65         if sel == nil {
66                 if x, ok := expr.X.(*ast.Ident); ok {
67                         pkg, ok := info.ObjectOf(x).(*types.PkgName)
68                         if !ok {
69                                 // This shouldn't happen
70                                 return fmt.Sprintf("%s.%s", x.Name, expr.Sel.Name)
71                         }
72                         return fmt.Sprintf("%s.%s", pkg.Imported().Path(), expr.Sel.Name)
73                 }
74                 panic(fmt.Sprintf("unsupported selector: %v", expr))
75         }
76         return fmt.Sprintf("(%s).%s", sel.Recv(), sel.Obj().Name())
77 }
78
79 func IsNil(pass *analysis.Pass, expr ast.Expr) bool {
80         return pass.TypesInfo.Types[expr].IsNil()
81 }
82
83 func BoolConst(pass *analysis.Pass, expr ast.Expr) bool {
84         val := pass.TypesInfo.ObjectOf(expr.(*ast.Ident)).(*types.Const).Val()
85         return constant.BoolVal(val)
86 }
87
88 func IsBoolConst(pass *analysis.Pass, expr ast.Expr) bool {
89         // We explicitly don't support typed bools because more often than
90         // not, custom bool types are used as binary enums and the
91         // explicit comparison is desired.
92
93         ident, ok := expr.(*ast.Ident)
94         if !ok {
95                 return false
96         }
97         obj := pass.TypesInfo.ObjectOf(ident)
98         c, ok := obj.(*types.Const)
99         if !ok {
100                 return false
101         }
102         basic, ok := c.Type().(*types.Basic)
103         if !ok {
104                 return false
105         }
106         if basic.Kind() != types.UntypedBool && basic.Kind() != types.Bool {
107                 return false
108         }
109         return true
110 }
111
112 func ExprToInt(pass *analysis.Pass, expr ast.Expr) (int64, bool) {
113         tv := pass.TypesInfo.Types[expr]
114         if tv.Value == nil {
115                 return 0, false
116         }
117         if tv.Value.Kind() != constant.Int {
118                 return 0, false
119         }
120         return constant.Int64Val(tv.Value)
121 }
122
123 func ExprToString(pass *analysis.Pass, expr ast.Expr) (string, bool) {
124         val := pass.TypesInfo.Types[expr].Value
125         if val == nil {
126                 return "", false
127         }
128         if val.Kind() != constant.String {
129                 return "", false
130         }
131         return constant.StringVal(val), true
132 }
133
134 func CallName(pass *analysis.Pass, call *ast.CallExpr) string {
135         switch fun := astutil.Unparen(call.Fun).(type) {
136         case *ast.SelectorExpr:
137                 fn, ok := pass.TypesInfo.ObjectOf(fun.Sel).(*types.Func)
138                 if !ok {
139                         return ""
140                 }
141                 return typeutil.FuncName(fn)
142         case *ast.Ident:
143                 obj := pass.TypesInfo.ObjectOf(fun)
144                 switch obj := obj.(type) {
145                 case *types.Func:
146                         return typeutil.FuncName(obj)
147                 case *types.Builtin:
148                         return obj.Name()
149                 default:
150                         return ""
151                 }
152         default:
153                 return ""
154         }
155 }
156
157 func IsCallTo(pass *analysis.Pass, node ast.Node, name string) bool {
158         call, ok := node.(*ast.CallExpr)
159         if !ok {
160                 return false
161         }
162         return CallName(pass, call) == name
163 }
164
165 func IsCallToAny(pass *analysis.Pass, node ast.Node, names ...string) bool {
166         call, ok := node.(*ast.CallExpr)
167         if !ok {
168                 return false
169         }
170         q := CallName(pass, call)
171         for _, name := range names {
172                 if q == name {
173                         return true
174                 }
175         }
176         return false
177 }
178
179 func File(pass *analysis.Pass, node Positioner) *ast.File {
180         m := pass.ResultOf[facts.TokenFile].(map[*token.File]*ast.File)
181         return m[pass.Fset.File(node.Pos())]
182 }
183
184 // IsGenerated reports whether pos is in a generated file, It ignores
185 // //line directives.
186 func IsGenerated(pass *analysis.Pass, pos token.Pos) bool {
187         _, ok := Generator(pass, pos)
188         return ok
189 }
190
191 // Generator returns the generator that generated the file containing
192 // pos. It ignores //line directives.
193 func Generator(pass *analysis.Pass, pos token.Pos) (facts.Generator, bool) {
194         file := pass.Fset.PositionFor(pos, false).Filename
195         m := pass.ResultOf[facts.Generated].(map[string]facts.Generator)
196         g, ok := m[file]
197         return g, ok
198 }
199
200 // MayHaveSideEffects reports whether expr may have side effects. If
201 // the purity argument is nil, this function implements a purely
202 // syntactic check, meaning that any function call may have side
203 // effects, regardless of the called function's body. Otherwise,
204 // purity will be consulted to determine the purity of function calls.
205 func MayHaveSideEffects(pass *analysis.Pass, expr ast.Expr, purity facts.PurityResult) bool {
206         switch expr := expr.(type) {
207         case *ast.BadExpr:
208                 return true
209         case *ast.Ellipsis:
210                 return MayHaveSideEffects(pass, expr.Elt, purity)
211         case *ast.FuncLit:
212                 // the literal itself cannot have side ffects, only calling it
213                 // might, which is handled by CallExpr.
214                 return false
215         case *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType:
216                 // types cannot have side effects
217                 return false
218         case *ast.BasicLit:
219                 return false
220         case *ast.BinaryExpr:
221                 return MayHaveSideEffects(pass, expr.X, purity) || MayHaveSideEffects(pass, expr.Y, purity)
222         case *ast.CallExpr:
223                 if purity == nil {
224                         return true
225                 }
226                 switch obj := typeutil.Callee(pass.TypesInfo, expr).(type) {
227                 case *types.Func:
228                         if _, ok := purity[obj]; !ok {
229                                 return true
230                         }
231                 case *types.Builtin:
232                         switch obj.Name() {
233                         case "len", "cap":
234                         default:
235                                 return true
236                         }
237                 default:
238                         return true
239                 }
240                 for _, arg := range expr.Args {
241                         if MayHaveSideEffects(pass, arg, purity) {
242                                 return true
243                         }
244                 }
245                 return false
246         case *ast.CompositeLit:
247                 if MayHaveSideEffects(pass, expr.Type, purity) {
248                         return true
249                 }
250                 for _, elt := range expr.Elts {
251                         if MayHaveSideEffects(pass, elt, purity) {
252                                 return true
253                         }
254                 }
255                 return false
256         case *ast.Ident:
257                 return false
258         case *ast.IndexExpr:
259                 return MayHaveSideEffects(pass, expr.X, purity) || MayHaveSideEffects(pass, expr.Index, purity)
260         case *ast.KeyValueExpr:
261                 return MayHaveSideEffects(pass, expr.Key, purity) || MayHaveSideEffects(pass, expr.Value, purity)
262         case *ast.SelectorExpr:
263                 return MayHaveSideEffects(pass, expr.X, purity)
264         case *ast.SliceExpr:
265                 return MayHaveSideEffects(pass, expr.X, purity) ||
266                         MayHaveSideEffects(pass, expr.Low, purity) ||
267                         MayHaveSideEffects(pass, expr.High, purity) ||
268                         MayHaveSideEffects(pass, expr.Max, purity)
269         case *ast.StarExpr:
270                 return MayHaveSideEffects(pass, expr.X, purity)
271         case *ast.TypeAssertExpr:
272                 return MayHaveSideEffects(pass, expr.X, purity)
273         case *ast.UnaryExpr:
274                 if MayHaveSideEffects(pass, expr.X, purity) {
275                         return true
276                 }
277                 return expr.Op == token.ARROW
278         case *ast.ParenExpr:
279                 return MayHaveSideEffects(pass, expr.X, purity)
280         case nil:
281                 return false
282         default:
283                 panic(fmt.Sprintf("internal error: unhandled type %T", expr))
284         }
285 }
286
287 func IsGoVersion(pass *analysis.Pass, minor int) bool {
288         f, ok := pass.Analyzer.Flags.Lookup("go").Value.(flag.Getter)
289         if !ok {
290                 panic("requested Go version, but analyzer has no version flag")
291         }
292         version := f.Get().(int)
293         return version >= minor
294 }