2013-08-27 16:49:13 -06:00
|
|
|
// Copyright 2013 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
2013-08-27 15:58:26 -06:00
|
|
|
package oracle
|
|
|
|
|
|
|
|
import (
|
|
|
|
"go/ast"
|
2013-09-03 13:29:02 -06:00
|
|
|
"go/token"
|
|
|
|
"sort"
|
2013-08-27 15:58:26 -06:00
|
|
|
|
|
|
|
"code.google.com/p/go.tools/go/types"
|
2013-09-03 13:29:02 -06:00
|
|
|
"code.google.com/p/go.tools/oracle/json"
|
2013-08-27 15:58:26 -06:00
|
|
|
"code.google.com/p/go.tools/pointer"
|
2013-09-03 13:29:02 -06:00
|
|
|
"code.google.com/p/go.tools/ssa"
|
2013-08-27 15:58:26 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
// Callees reports the possible callees of the function call site
|
|
|
|
// identified by the specified source location.
|
|
|
|
//
|
|
|
|
// TODO(adonovan): if a callee is a wrapper, show the callee's callee.
|
|
|
|
//
|
|
|
|
func callees(o *oracle) (queryResult, error) {
|
|
|
|
// Determine the enclosing call for the specified position.
|
|
|
|
var call *ast.CallExpr
|
|
|
|
for _, n := range o.queryPath {
|
|
|
|
if call, _ = n.(*ast.CallExpr); call != nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if call == nil {
|
|
|
|
return nil, o.errorf(o.queryPath[0], "there is no function call here")
|
|
|
|
}
|
|
|
|
// TODO(adonovan): issue an error if the call is "too far
|
|
|
|
// away" from the current selection, as this most likely is
|
|
|
|
// not what the user intended.
|
|
|
|
|
|
|
|
// Reject type conversions.
|
|
|
|
if o.queryPkgInfo.IsType(call.Fun) {
|
|
|
|
return nil, o.errorf(call, "this is a type conversion, not a function call")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reject calls to built-ins.
|
|
|
|
if b, ok := o.queryPkgInfo.TypeOf(call.Fun).(*types.Builtin); ok {
|
|
|
|
return nil, o.errorf(call, "this is a call to the built-in '%s' operator", b.Name())
|
|
|
|
}
|
|
|
|
|
|
|
|
buildSSA(o)
|
|
|
|
|
|
|
|
// Compute the subgraph of the callgraph for callsite(s)
|
|
|
|
// arising from 'call'. There may be more than one if its
|
|
|
|
// enclosing function was treated context-sensitively.
|
|
|
|
// (Or zero if it was in dead code.)
|
|
|
|
//
|
|
|
|
// The presence of a key indicates this call site is
|
|
|
|
// interesting even if the value is nil.
|
|
|
|
querySites := make(map[pointer.CallSite][]pointer.CallGraphNode)
|
2013-09-03 13:29:02 -06:00
|
|
|
var arbitrarySite pointer.CallSite
|
2013-08-27 15:58:26 -06:00
|
|
|
o.config.CallSite = func(site pointer.CallSite) {
|
|
|
|
if site.Pos() == call.Lparen {
|
|
|
|
// Not a no-op! Ensures key is
|
|
|
|
// present even if value is nil:
|
|
|
|
querySites[site] = querySites[site]
|
2013-09-03 13:29:02 -06:00
|
|
|
arbitrarySite = site
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
}
|
2013-09-03 13:29:02 -06:00
|
|
|
o.config.Call = func(site pointer.CallSite, callee pointer.CallGraphNode) {
|
2013-08-27 15:58:26 -06:00
|
|
|
if targets, ok := querySites[site]; ok {
|
|
|
|
querySites[site] = append(targets, callee)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ptrAnalysis(o)
|
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
if arbitrarySite == nil {
|
|
|
|
return nil, o.errorf(call.Lparen, "this call site is unreachable in this analysis")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compute union of callees across all contexts.
|
|
|
|
funcsMap := make(map[*ssa.Function]bool)
|
|
|
|
for _, callees := range querySites {
|
|
|
|
for _, callee := range callees {
|
|
|
|
funcsMap[callee.Func()] = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
funcs := make([]*ssa.Function, 0, len(funcsMap))
|
|
|
|
for f := range funcsMap {
|
|
|
|
funcs = append(funcs, f)
|
|
|
|
}
|
|
|
|
sort.Sort(byFuncPos(funcs))
|
|
|
|
|
2013-08-27 15:58:26 -06:00
|
|
|
return &calleesResult{
|
2013-09-03 13:29:02 -06:00
|
|
|
site: arbitrarySite,
|
|
|
|
funcs: funcs,
|
2013-08-27 15:58:26 -06:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type calleesResult struct {
|
2013-09-03 13:29:02 -06:00
|
|
|
site pointer.CallSite
|
|
|
|
funcs []*ssa.Function
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
func (r *calleesResult) display(printf printfFunc) {
|
|
|
|
if len(r.funcs) == 0 {
|
|
|
|
// dynamic call on a provably nil func/interface
|
|
|
|
printf(r.site, "%s on nil value", r.site.Description())
|
|
|
|
} else {
|
|
|
|
printf(r.site, "this %s dispatches to:", r.site.Description())
|
|
|
|
for _, callee := range r.funcs {
|
|
|
|
printf(callee, "\t%s", callee)
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
2013-09-03 13:29:02 -06:00
|
|
|
}
|
|
|
|
}
|
2013-08-27 15:58:26 -06:00
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
func (r *calleesResult) toJSON(res *json.Result, fset *token.FileSet) {
|
|
|
|
j := &json.Callees{
|
|
|
|
Pos: r.site.Caller().Func().Prog.Fset.Position(r.site.Pos()).String(),
|
|
|
|
Desc: r.site.Description(),
|
|
|
|
}
|
|
|
|
for _, callee := range r.funcs {
|
|
|
|
j.Callees = append(j.Callees, &json.CalleesItem{
|
|
|
|
Name: callee.String(),
|
|
|
|
Pos: callee.Prog.Fset.Position(callee.Pos()).String(),
|
|
|
|
})
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
2013-09-03 13:29:02 -06:00
|
|
|
res.Callees = j
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
2013-09-03 13:29:02 -06:00
|
|
|
|
|
|
|
type byFuncPos []*ssa.Function
|
|
|
|
|
|
|
|
func (a byFuncPos) Len() int { return len(a) }
|
|
|
|
func (a byFuncPos) Less(i, j int) bool { return a[i].Pos() < a[j].Pos() }
|
|
|
|
func (a byFuncPos) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|