1 | // Copyright 2018 The Go Authors. All rights reserved. |
---|---|
2 | // Use of this source code is governed by a BSD-style |
3 | // license that can be found in the LICENSE file. |
4 | |
5 | package typeutil |
6 | |
7 | import ( |
8 | "go/ast" |
9 | "go/types" |
10 | |
11 | "golang.org/x/tools/go/ast/astutil" |
12 | "golang.org/x/tools/internal/typeparams" |
13 | ) |
14 | |
15 | // Callee returns the named target of a function call, if any: |
16 | // a function, method, builtin, or variable. |
17 | // |
18 | // Functions and methods may potentially have type parameters. |
19 | func Callee(info *types.Info, call *ast.CallExpr) types.Object { |
20 | fun := astutil.Unparen(call.Fun) |
21 | |
22 | // Look through type instantiation if necessary. |
23 | isInstance := false |
24 | switch fun.(type) { |
25 | case *ast.IndexExpr, *typeparams.IndexListExpr: |
26 | // When extracting the callee from an *IndexExpr, we need to check that |
27 | // it is a *types.Func and not a *types.Var. |
28 | // Example: Don't match a slice m within the expression `m[0]()`. |
29 | isInstance = true |
30 | fun, _, _, _ = typeparams.UnpackIndexExpr(fun) |
31 | } |
32 | |
33 | var obj types.Object |
34 | switch fun := fun.(type) { |
35 | case *ast.Ident: |
36 | obj = info.Uses[fun] // type, var, builtin, or declared func |
37 | case *ast.SelectorExpr: |
38 | if sel, ok := info.Selections[fun]; ok { |
39 | obj = sel.Obj() // method or field |
40 | } else { |
41 | obj = info.Uses[fun.Sel] // qualified identifier? |
42 | } |
43 | } |
44 | if _, ok := obj.(*types.TypeName); ok { |
45 | return nil // T(x) is a conversion, not a call |
46 | } |
47 | // A Func is required to match instantiations. |
48 | if _, ok := obj.(*types.Func); isInstance && !ok { |
49 | return nil // Was not a Func. |
50 | } |
51 | return obj |
52 | } |
53 | |
54 | // StaticCallee returns the target (function or method) of a static function |
55 | // call, if any. It returns nil for calls to builtins. |
56 | // |
57 | // Note: for calls of instantiated functions and methods, StaticCallee returns |
58 | // the corresponding generic function or method on the generic type. |
59 | func StaticCallee(info *types.Info, call *ast.CallExpr) *types.Func { |
60 | if f, ok := Callee(info, call).(*types.Func); ok && !interfaceMethod(f) { |
61 | return f |
62 | } |
63 | return nil |
64 | } |
65 | |
66 | func interfaceMethod(f *types.Func) bool { |
67 | recv := f.Type().(*types.Signature).Recv() |
68 | return recv != nil && types.IsInterface(recv.Type()) |
69 | } |
70 |
Members