Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / cmd / guru / callees.go
1 // Copyright 2013 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 main
6
7 import (
8         "fmt"
9         "go/ast"
10         "go/token"
11         "go/types"
12         "sort"
13
14         "golang.org/x/tools/cmd/guru/serial"
15         "golang.org/x/tools/go/loader"
16         "golang.org/x/tools/go/pointer"
17         "golang.org/x/tools/go/ssa"
18         "golang.org/x/tools/go/ssa/ssautil"
19 )
20
21 // The callees function reports the possible callees of the function call site
22 // identified by the specified source location.
23 func callees(q *Query) error {
24         lconf := loader.Config{Build: q.Build}
25
26         if err := setPTAScope(&lconf, q.Scope); err != nil {
27                 return err
28         }
29
30         // Load/parse/type-check the program.
31         lprog, err := loadWithSoftErrors(&lconf)
32         if err != nil {
33                 return err
34         }
35
36         qpos, err := parseQueryPos(lprog, q.Pos, true) // needs exact pos
37         if err != nil {
38                 return err
39         }
40
41         // Determine the enclosing call for the specified position.
42         var e *ast.CallExpr
43         for _, n := range qpos.path {
44                 if e, _ = n.(*ast.CallExpr); e != nil {
45                         break
46                 }
47         }
48         if e == nil {
49                 return fmt.Errorf("there is no function call here")
50         }
51         // TODO(adonovan): issue an error if the call is "too far
52         // away" from the current selection, as this most likely is
53         // not what the user intended.
54
55         // Reject type conversions.
56         if qpos.info.Types[e.Fun].IsType() {
57                 return fmt.Errorf("this is a type conversion, not a function call")
58         }
59
60         // Deal with obviously static calls before constructing SSA form.
61         // Some static calls may yet require SSA construction,
62         // e.g.  f := func(){}; f().
63         switch funexpr := unparen(e.Fun).(type) {
64         case *ast.Ident:
65                 switch obj := qpos.info.Uses[funexpr].(type) {
66                 case *types.Builtin:
67                         // Reject calls to built-ins.
68                         return fmt.Errorf("this is a call to the built-in '%s' operator", obj.Name())
69                 case *types.Func:
70                         // This is a static function call
71                         q.Output(lprog.Fset, &calleesTypesResult{
72                                 site:   e,
73                                 callee: obj,
74                         })
75                         return nil
76                 }
77         case *ast.SelectorExpr:
78                 sel := qpos.info.Selections[funexpr]
79                 if sel == nil {
80                         // qualified identifier.
81                         // May refer to top level function variable
82                         // or to top level function.
83                         callee := qpos.info.Uses[funexpr.Sel]
84                         if obj, ok := callee.(*types.Func); ok {
85                                 q.Output(lprog.Fset, &calleesTypesResult{
86                                         site:   e,
87                                         callee: obj,
88                                 })
89                                 return nil
90                         }
91                 } else if sel.Kind() == types.MethodVal {
92                         // Inspect the receiver type of the selected method.
93                         // If it is concrete, the call is statically dispatched.
94                         // (Due to implicit field selections, it is not enough to look
95                         // at sel.Recv(), the type of the actual receiver expression.)
96                         method := sel.Obj().(*types.Func)
97                         recvtype := method.Type().(*types.Signature).Recv().Type()
98                         if !types.IsInterface(recvtype) {
99                                 // static method call
100                                 q.Output(lprog.Fset, &calleesTypesResult{
101                                         site:   e,
102                                         callee: method,
103                                 })
104                                 return nil
105                         }
106                 }
107         }
108
109         prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug)
110
111         ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
112         if err != nil {
113                 return err
114         }
115
116         pkg := prog.Package(qpos.info.Pkg)
117         if pkg == nil {
118                 return fmt.Errorf("no SSA package")
119         }
120
121         // Defer SSA construction till after errors are reported.
122         prog.Build()
123
124         // Ascertain calling function and call site.
125         callerFn := ssa.EnclosingFunction(pkg, qpos.path)
126         if callerFn == nil {
127                 return fmt.Errorf("no SSA function built for this location (dead code?)")
128         }
129
130         // Find the call site.
131         site, err := findCallSite(callerFn, e)
132         if err != nil {
133                 return err
134         }
135
136         funcs, err := findCallees(ptaConfig, site)
137         if err != nil {
138                 return err
139         }
140
141         q.Output(lprog.Fset, &calleesSSAResult{
142                 site:  site,
143                 funcs: funcs,
144         })
145         return nil
146 }
147
148 func findCallSite(fn *ssa.Function, call *ast.CallExpr) (ssa.CallInstruction, error) {
149         instr, _ := fn.ValueForExpr(call)
150         callInstr, _ := instr.(ssa.CallInstruction)
151         if instr == nil {
152                 return nil, fmt.Errorf("this call site is unreachable in this analysis")
153         }
154         return callInstr, nil
155 }
156
157 func findCallees(conf *pointer.Config, site ssa.CallInstruction) ([]*ssa.Function, error) {
158         // Avoid running the pointer analysis for static calls.
159         if callee := site.Common().StaticCallee(); callee != nil {
160                 switch callee.String() {
161                 case "runtime.SetFinalizer", "(reflect.Value).Call":
162                         // The PTA treats calls to these intrinsics as dynamic.
163                         // TODO(adonovan): avoid reliance on PTA internals.
164
165                 default:
166                         return []*ssa.Function{callee}, nil // singleton
167                 }
168         }
169
170         // Dynamic call: use pointer analysis.
171         conf.BuildCallGraph = true
172         cg := ptrAnalysis(conf).CallGraph
173         cg.DeleteSyntheticNodes()
174
175         // Find all call edges from the site.
176         n := cg.Nodes[site.Parent()]
177         if n == nil {
178                 return nil, fmt.Errorf("this call site is unreachable in this analysis")
179         }
180         calleesMap := make(map[*ssa.Function]bool)
181         for _, edge := range n.Out {
182                 if edge.Site == site {
183                         calleesMap[edge.Callee.Func] = true
184                 }
185         }
186
187         // De-duplicate and sort.
188         funcs := make([]*ssa.Function, 0, len(calleesMap))
189         for f := range calleesMap {
190                 funcs = append(funcs, f)
191         }
192         sort.Sort(byFuncPos(funcs))
193         return funcs, nil
194 }
195
196 type calleesSSAResult struct {
197         site  ssa.CallInstruction
198         funcs []*ssa.Function
199 }
200
201 type calleesTypesResult struct {
202         site   *ast.CallExpr
203         callee *types.Func
204 }
205
206 func (r *calleesSSAResult) PrintPlain(printf printfFunc) {
207         if len(r.funcs) == 0 {
208                 // dynamic call on a provably nil func/interface
209                 printf(r.site, "%s on nil value", r.site.Common().Description())
210         } else {
211                 printf(r.site, "this %s dispatches to:", r.site.Common().Description())
212                 for _, callee := range r.funcs {
213                         printf(callee, "\t%s", callee)
214                 }
215         }
216 }
217
218 func (r *calleesSSAResult) JSON(fset *token.FileSet) []byte {
219         j := &serial.Callees{
220                 Pos:  fset.Position(r.site.Pos()).String(),
221                 Desc: r.site.Common().Description(),
222         }
223         for _, callee := range r.funcs {
224                 j.Callees = append(j.Callees, &serial.Callee{
225                         Name: callee.String(),
226                         Pos:  fset.Position(callee.Pos()).String(),
227                 })
228         }
229         return toJSON(j)
230 }
231
232 func (r *calleesTypesResult) PrintPlain(printf printfFunc) {
233         printf(r.site, "this static function call dispatches to:")
234         printf(r.callee, "\t%s", r.callee.FullName())
235 }
236
237 func (r *calleesTypesResult) JSON(fset *token.FileSet) []byte {
238         j := &serial.Callees{
239                 Pos:  fset.Position(r.site.Pos()).String(),
240                 Desc: "static function call",
241         }
242         j.Callees = []*serial.Callee{
243                 {
244                         Name: r.callee.FullName(),
245                         Pos:  fset.Position(r.callee.Pos()).String(),
246                 },
247         }
248         return toJSON(j)
249 }
250
251 // NB: byFuncPos is not deterministic across packages since it depends on load order.
252 // Use lessPos if the tests need it.
253 type byFuncPos []*ssa.Function
254
255 func (a byFuncPos) Len() int           { return len(a) }
256 func (a byFuncPos) Less(i, j int) bool { return a[i].Pos() < a[j].Pos() }
257 func (a byFuncPos) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }