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.
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"
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}
26 if err := setPTAScope(&lconf, q.Scope); err != nil {
30 // Load/parse/type-check the program.
31 lprog, err := loadWithSoftErrors(&lconf)
36 qpos, err := parseQueryPos(lprog, q.Pos, true) // needs exact pos
41 // Determine the enclosing call for the specified position.
43 for _, n := range qpos.path {
44 if e, _ = n.(*ast.CallExpr); e != nil {
49 return fmt.Errorf("there is no function call here")
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.
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")
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) {
65 switch obj := qpos.info.Uses[funexpr].(type) {
67 // Reject calls to built-ins.
68 return fmt.Errorf("this is a call to the built-in '%s' operator", obj.Name())
70 // This is a static function call
71 q.Output(lprog.Fset, &calleesTypesResult{
77 case *ast.SelectorExpr:
78 sel := qpos.info.Selections[funexpr]
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{
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) {
100 q.Output(lprog.Fset, &calleesTypesResult{
109 prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug)
111 ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
116 pkg := prog.Package(qpos.info.Pkg)
118 return fmt.Errorf("no SSA package")
121 // Defer SSA construction till after errors are reported.
124 // Ascertain calling function and call site.
125 callerFn := ssa.EnclosingFunction(pkg, qpos.path)
127 return fmt.Errorf("no SSA function built for this location (dead code?)")
130 // Find the call site.
131 site, err := findCallSite(callerFn, e)
136 funcs, err := findCallees(ptaConfig, site)
141 q.Output(lprog.Fset, &calleesSSAResult{
148 func findCallSite(fn *ssa.Function, call *ast.CallExpr) (ssa.CallInstruction, error) {
149 instr, _ := fn.ValueForExpr(call)
150 callInstr, _ := instr.(ssa.CallInstruction)
152 return nil, fmt.Errorf("this call site is unreachable in this analysis")
154 return callInstr, nil
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.
166 return []*ssa.Function{callee}, nil // singleton
170 // Dynamic call: use pointer analysis.
171 conf.BuildCallGraph = true
172 cg := ptrAnalysis(conf).CallGraph
173 cg.DeleteSyntheticNodes()
175 // Find all call edges from the site.
176 n := cg.Nodes[site.Parent()]
178 return nil, fmt.Errorf("this call site is unreachable in this analysis")
180 calleesMap := make(map[*ssa.Function]bool)
181 for _, edge := range n.Out {
182 if edge.Site == site {
183 calleesMap[edge.Callee.Func] = true
187 // De-duplicate and sort.
188 funcs := make([]*ssa.Function, 0, len(calleesMap))
189 for f := range calleesMap {
190 funcs = append(funcs, f)
192 sort.Sort(byFuncPos(funcs))
196 type calleesSSAResult struct {
197 site ssa.CallInstruction
198 funcs []*ssa.Function
201 type calleesTypesResult struct {
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())
211 printf(r.site, "this %s dispatches to:", r.site.Common().Description())
212 for _, callee := range r.funcs {
213 printf(callee, "\t%s", callee)
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(),
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(),
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())
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",
242 j.Callees = []*serial.Callee{
244 Name: r.callee.FullName(),
245 Pos: fset.Position(r.callee.Pos()).String(),
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
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] }