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 / 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         "golang.org/x/tools/go/analysis"
14         "golang.org/x/tools/go/analysis/passes/inspect"
15         "golang.org/x/tools/go/ast/astutil"
16         "golang.org/x/tools/go/ast/inspector"
17         "honnef.co/go/tools/facts"
18         "honnef.co/go/tools/go/types/typeutil"
19         "honnef.co/go/tools/ir"
20         "honnef.co/go/tools/lint"
21 )
22
23 type Positioner interface {
24         Pos() token.Pos
25 }
26
27 func CallName(call *ir.CallCommon) string {
28         if call.IsInvoke() {
29                 return ""
30         }
31         switch v := call.Value.(type) {
32         case *ir.Function:
33                 fn, ok := v.Object().(*types.Func)
34                 if !ok {
35                         return ""
36                 }
37                 return lint.FuncName(fn)
38         case *ir.Builtin:
39                 return v.Name()
40         }
41         return ""
42 }
43
44 func IsCallTo(call *ir.CallCommon, name string) bool { return CallName(call) == name }
45
46 func IsCallToAny(call *ir.CallCommon, names ...string) bool {
47         q := CallName(call)
48         for _, name := range names {
49                 if q == name {
50                         return true
51                 }
52         }
53         return false
54 }
55
56 func IsType(T types.Type, name string) bool { return types.TypeString(T, nil) == name }
57
58 func FilterDebug(instr []ir.Instruction) []ir.Instruction {
59         var out []ir.Instruction
60         for _, ins := range instr {
61                 if _, ok := ins.(*ir.DebugRef); !ok {
62                         out = append(out, ins)
63                 }
64         }
65         return out
66 }
67
68 func IsExample(fn *ir.Function) bool {
69         if !strings.HasPrefix(fn.Name(), "Example") {
70                 return false
71         }
72         f := fn.Prog.Fset.File(fn.Pos())
73         if f == nil {
74                 return false
75         }
76         return strings.HasSuffix(f.Name(), "_test.go")
77 }
78
79 func IsPointerLike(T types.Type) bool {
80         switch T := T.Underlying().(type) {
81         case *types.Interface, *types.Chan, *types.Map, *types.Signature, *types.Pointer:
82                 return true
83         case *types.Basic:
84                 return T.Kind() == types.UnsafePointer
85         }
86         return false
87 }
88
89 func IsIdent(expr ast.Expr, ident string) bool {
90         id, ok := expr.(*ast.Ident)
91         return ok && id.Name == ident
92 }
93
94 // isBlank returns whether id is the blank identifier "_".
95 // If id == nil, the answer is false.
96 func IsBlank(id ast.Expr) bool {
97         ident, _ := id.(*ast.Ident)
98         return ident != nil && ident.Name == "_"
99 }
100
101 func IsIntLiteral(expr ast.Expr, literal string) bool {
102         lit, ok := expr.(*ast.BasicLit)
103         return ok && lit.Kind == token.INT && lit.Value == literal
104 }
105
106 // Deprecated: use IsIntLiteral instead
107 func IsZero(expr ast.Expr) bool {
108         return IsIntLiteral(expr, "0")
109 }
110
111 func IsOfType(pass *analysis.Pass, expr ast.Expr, name string) bool {
112         return IsType(pass.TypesInfo.TypeOf(expr), name)
113 }
114
115 func IsInTest(pass *analysis.Pass, node Positioner) bool {
116         // FIXME(dh): this doesn't work for global variables with
117         // initializers
118         f := pass.Fset.File(node.Pos())
119         return f != nil && strings.HasSuffix(f.Name(), "_test.go")
120 }
121
122 // IsMain reports whether the package being processed is a package
123 // main.
124 func IsMain(pass *analysis.Pass) bool {
125         return pass.Pkg.Name() == "main"
126 }
127
128 // IsMainLike reports whether the package being processed is a
129 // main-like package. A main-like package is a package that is
130 // package main, or that is intended to be used by a tool framework
131 // such as cobra to implement a command.
132 //
133 // Note that this function errs on the side of false positives; it may
134 // return true for packages that aren't main-like. IsMainLike is
135 // intended for analyses that wish to suppress diagnostics for
136 // main-like packages to avoid false positives.
137 func IsMainLike(pass *analysis.Pass) bool {
138         if pass.Pkg.Name() == "main" {
139                 return true
140         }
141         for _, imp := range pass.Pkg.Imports() {
142                 if imp.Path() == "github.com/spf13/cobra" {
143                         return true
144                 }
145         }
146         return false
147 }
148
149 func SelectorName(pass *analysis.Pass, expr *ast.SelectorExpr) string {
150         info := pass.TypesInfo
151         sel := info.Selections[expr]
152         if sel == nil {
153                 if x, ok := expr.X.(*ast.Ident); ok {
154                         pkg, ok := info.ObjectOf(x).(*types.PkgName)
155                         if !ok {
156                                 // This shouldn't happen
157                                 return fmt.Sprintf("%s.%s", x.Name, expr.Sel.Name)
158                         }
159                         return fmt.Sprintf("%s.%s", pkg.Imported().Path(), expr.Sel.Name)
160                 }
161                 panic(fmt.Sprintf("unsupported selector: %v", expr))
162         }
163         return fmt.Sprintf("(%s).%s", sel.Recv(), sel.Obj().Name())
164 }
165
166 func IsNil(pass *analysis.Pass, expr ast.Expr) bool {
167         return pass.TypesInfo.Types[expr].IsNil()
168 }
169
170 func BoolConst(pass *analysis.Pass, expr ast.Expr) bool {
171         val := pass.TypesInfo.ObjectOf(expr.(*ast.Ident)).(*types.Const).Val()
172         return constant.BoolVal(val)
173 }
174
175 func IsBoolConst(pass *analysis.Pass, expr ast.Expr) bool {
176         // We explicitly don't support typed bools because more often than
177         // not, custom bool types are used as binary enums and the
178         // explicit comparison is desired.
179
180         ident, ok := expr.(*ast.Ident)
181         if !ok {
182                 return false
183         }
184         obj := pass.TypesInfo.ObjectOf(ident)
185         c, ok := obj.(*types.Const)
186         if !ok {
187                 return false
188         }
189         basic, ok := c.Type().(*types.Basic)
190         if !ok {
191                 return false
192         }
193         if basic.Kind() != types.UntypedBool && basic.Kind() != types.Bool {
194                 return false
195         }
196         return true
197 }
198
199 func ExprToInt(pass *analysis.Pass, expr ast.Expr) (int64, bool) {
200         tv := pass.TypesInfo.Types[expr]
201         if tv.Value == nil {
202                 return 0, false
203         }
204         if tv.Value.Kind() != constant.Int {
205                 return 0, false
206         }
207         return constant.Int64Val(tv.Value)
208 }
209
210 func ExprToString(pass *analysis.Pass, expr ast.Expr) (string, bool) {
211         val := pass.TypesInfo.Types[expr].Value
212         if val == nil {
213                 return "", false
214         }
215         if val.Kind() != constant.String {
216                 return "", false
217         }
218         return constant.StringVal(val), true
219 }
220
221 // Dereference returns a pointer's element type; otherwise it returns
222 // T.
223 func Dereference(T types.Type) types.Type {
224         if p, ok := T.Underlying().(*types.Pointer); ok {
225                 return p.Elem()
226         }
227         return T
228 }
229
230 // DereferenceR returns a pointer's element type; otherwise it returns
231 // T. If the element type is itself a pointer, DereferenceR will be
232 // applied recursively.
233 func DereferenceR(T types.Type) types.Type {
234         if p, ok := T.Underlying().(*types.Pointer); ok {
235                 return DereferenceR(p.Elem())
236         }
237         return T
238 }
239
240 func CallNameAST(pass *analysis.Pass, call *ast.CallExpr) string {
241         switch fun := astutil.Unparen(call.Fun).(type) {
242         case *ast.SelectorExpr:
243                 fn, ok := pass.TypesInfo.ObjectOf(fun.Sel).(*types.Func)
244                 if !ok {
245                         return ""
246                 }
247                 return lint.FuncName(fn)
248         case *ast.Ident:
249                 obj := pass.TypesInfo.ObjectOf(fun)
250                 switch obj := obj.(type) {
251                 case *types.Func:
252                         return lint.FuncName(obj)
253                 case *types.Builtin:
254                         return obj.Name()
255                 default:
256                         return ""
257                 }
258         default:
259                 return ""
260         }
261 }
262
263 func IsCallToAST(pass *analysis.Pass, node ast.Node, name string) bool {
264         call, ok := node.(*ast.CallExpr)
265         if !ok {
266                 return false
267         }
268         return CallNameAST(pass, call) == name
269 }
270
271 func IsCallToAnyAST(pass *analysis.Pass, node ast.Node, names ...string) bool {
272         call, ok := node.(*ast.CallExpr)
273         if !ok {
274                 return false
275         }
276         q := CallNameAST(pass, call)
277         for _, name := range names {
278                 if q == name {
279                         return true
280                 }
281         }
282         return false
283 }
284
285 func Preamble(f *ast.File) string {
286         cutoff := f.Package
287         if f.Doc != nil {
288                 cutoff = f.Doc.Pos()
289         }
290         var out []string
291         for _, cmt := range f.Comments {
292                 if cmt.Pos() >= cutoff {
293                         break
294                 }
295                 out = append(out, cmt.Text())
296         }
297         return strings.Join(out, "\n")
298 }
299
300 func GroupSpecs(fset *token.FileSet, specs []ast.Spec) [][]ast.Spec {
301         if len(specs) == 0 {
302                 return nil
303         }
304         groups := make([][]ast.Spec, 1)
305         groups[0] = append(groups[0], specs[0])
306
307         for _, spec := range specs[1:] {
308                 g := groups[len(groups)-1]
309                 if fset.PositionFor(spec.Pos(), false).Line-1 !=
310                         fset.PositionFor(g[len(g)-1].End(), false).Line {
311
312                         groups = append(groups, nil)
313                 }
314
315                 groups[len(groups)-1] = append(groups[len(groups)-1], spec)
316         }
317
318         return groups
319 }
320
321 func IsObject(obj types.Object, name string) bool {
322         var path string
323         if pkg := obj.Pkg(); pkg != nil {
324                 path = pkg.Path() + "."
325         }
326         return path+obj.Name() == name
327 }
328
329 type Field struct {
330         Var  *types.Var
331         Tag  string
332         Path []int
333 }
334
335 // FlattenFields recursively flattens T and embedded structs,
336 // returning a list of fields. If multiple fields with the same name
337 // exist, all will be returned.
338 func FlattenFields(T *types.Struct) []Field {
339         return flattenFields(T, nil, nil)
340 }
341
342 func flattenFields(T *types.Struct, path []int, seen map[types.Type]bool) []Field {
343         if seen == nil {
344                 seen = map[types.Type]bool{}
345         }
346         if seen[T] {
347                 return nil
348         }
349         seen[T] = true
350         var out []Field
351         for i := 0; i < T.NumFields(); i++ {
352                 field := T.Field(i)
353                 tag := T.Tag(i)
354                 np := append(path[:len(path):len(path)], i)
355                 if field.Anonymous() {
356                         if s, ok := Dereference(field.Type()).Underlying().(*types.Struct); ok {
357                                 out = append(out, flattenFields(s, np, seen)...)
358                         }
359                 } else {
360                         out = append(out, Field{field, tag, np})
361                 }
362         }
363         return out
364 }
365
366 func File(pass *analysis.Pass, node Positioner) *ast.File {
367         m := pass.ResultOf[facts.TokenFile].(map[*token.File]*ast.File)
368         return m[pass.Fset.File(node.Pos())]
369 }
370
371 // IsGenerated reports whether pos is in a generated file, It ignores
372 // //line directives.
373 func IsGenerated(pass *analysis.Pass, pos token.Pos) bool {
374         _, ok := Generator(pass, pos)
375         return ok
376 }
377
378 // Generator returns the generator that generated the file containing
379 // pos. It ignores //line directives.
380 func Generator(pass *analysis.Pass, pos token.Pos) (facts.Generator, bool) {
381         file := pass.Fset.PositionFor(pos, false).Filename
382         m := pass.ResultOf[facts.Generated].(map[string]facts.Generator)
383         g, ok := m[file]
384         return g, ok
385 }
386
387 // MayHaveSideEffects reports whether expr may have side effects. If
388 // the purity argument is nil, this function implements a purely
389 // syntactic check, meaning that any function call may have side
390 // effects, regardless of the called function's body. Otherwise,
391 // purity will be consulted to determine the purity of function calls.
392 func MayHaveSideEffects(pass *analysis.Pass, expr ast.Expr, purity facts.PurityResult) bool {
393         switch expr := expr.(type) {
394         case *ast.BadExpr:
395                 return true
396         case *ast.Ellipsis:
397                 return MayHaveSideEffects(pass, expr.Elt, purity)
398         case *ast.FuncLit:
399                 // the literal itself cannot have side ffects, only calling it
400                 // might, which is handled by CallExpr.
401                 return false
402         case *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType:
403                 // types cannot have side effects
404                 return false
405         case *ast.BasicLit:
406                 return false
407         case *ast.BinaryExpr:
408                 return MayHaveSideEffects(pass, expr.X, purity) || MayHaveSideEffects(pass, expr.Y, purity)
409         case *ast.CallExpr:
410                 if purity == nil {
411                         return true
412                 }
413                 switch obj := typeutil.Callee(pass.TypesInfo, expr).(type) {
414                 case *types.Func:
415                         if _, ok := purity[obj]; !ok {
416                                 return true
417                         }
418                 case *types.Builtin:
419                         switch obj.Name() {
420                         case "len", "cap":
421                         default:
422                                 return true
423                         }
424                 default:
425                         return true
426                 }
427                 for _, arg := range expr.Args {
428                         if MayHaveSideEffects(pass, arg, purity) {
429                                 return true
430                         }
431                 }
432                 return false
433         case *ast.CompositeLit:
434                 if MayHaveSideEffects(pass, expr.Type, purity) {
435                         return true
436                 }
437                 for _, elt := range expr.Elts {
438                         if MayHaveSideEffects(pass, elt, purity) {
439                                 return true
440                         }
441                 }
442                 return false
443         case *ast.Ident:
444                 return false
445         case *ast.IndexExpr:
446                 return MayHaveSideEffects(pass, expr.X, purity) || MayHaveSideEffects(pass, expr.Index, purity)
447         case *ast.KeyValueExpr:
448                 return MayHaveSideEffects(pass, expr.Key, purity) || MayHaveSideEffects(pass, expr.Value, purity)
449         case *ast.SelectorExpr:
450                 return MayHaveSideEffects(pass, expr.X, purity)
451         case *ast.SliceExpr:
452                 return MayHaveSideEffects(pass, expr.X, purity) ||
453                         MayHaveSideEffects(pass, expr.Low, purity) ||
454                         MayHaveSideEffects(pass, expr.High, purity) ||
455                         MayHaveSideEffects(pass, expr.Max, purity)
456         case *ast.StarExpr:
457                 return MayHaveSideEffects(pass, expr.X, purity)
458         case *ast.TypeAssertExpr:
459                 return MayHaveSideEffects(pass, expr.X, purity)
460         case *ast.UnaryExpr:
461                 if MayHaveSideEffects(pass, expr.X, purity) {
462                         return true
463                 }
464                 return expr.Op == token.ARROW
465         case *ast.ParenExpr:
466                 return MayHaveSideEffects(pass, expr.X, purity)
467         case nil:
468                 return false
469         default:
470                 panic(fmt.Sprintf("internal error: unhandled type %T", expr))
471         }
472 }
473
474 func IsGoVersion(pass *analysis.Pass, minor int) bool {
475         version := pass.Analyzer.Flags.Lookup("go").Value.(flag.Getter).Get().(int)
476         return version >= minor
477 }
478
479 func Preorder(pass *analysis.Pass, fn func(ast.Node), types ...ast.Node) {
480         pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder(types, fn)
481 }