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 (
|
2013-09-03 13:29:02 -06:00
|
|
|
"go/token"
|
|
|
|
|
|
|
|
"code.google.com/p/go.tools/oracle/json"
|
2013-08-27 15:58:26 -06:00
|
|
|
"code.google.com/p/go.tools/pointer"
|
|
|
|
"code.google.com/p/go.tools/ssa"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Callers reports the possible callers of the function
|
|
|
|
// immediately enclosing the specified source location.
|
|
|
|
//
|
|
|
|
// TODO(adonovan): if a caller is a wrapper, show the caller's caller.
|
|
|
|
//
|
|
|
|
func callers(o *oracle) (queryResult, error) {
|
|
|
|
pkg := o.prog.Package(o.queryPkgInfo.Pkg)
|
|
|
|
if pkg == nil {
|
|
|
|
return nil, o.errorf(o.queryPath[0], "no SSA package")
|
|
|
|
}
|
|
|
|
if !ssa.HasEnclosingFunction(pkg, o.queryPath) {
|
|
|
|
return nil, o.errorf(o.queryPath[0], "this position is not inside a function")
|
|
|
|
}
|
|
|
|
|
|
|
|
buildSSA(o)
|
|
|
|
|
|
|
|
target := ssa.EnclosingFunction(pkg, o.queryPath)
|
|
|
|
if target == nil {
|
|
|
|
return nil, o.errorf(o.queryPath[0], "no SSA function built for this location (dead code?)")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run the pointer analysis, recording each
|
|
|
|
// call found to originate from target.
|
2013-09-03 13:29:02 -06:00
|
|
|
var calls []pointer.CallSite
|
|
|
|
o.config.Call = func(site pointer.CallSite, callee pointer.CallGraphNode) {
|
2013-08-27 15:58:26 -06:00
|
|
|
if callee.Func() == target {
|
2013-09-03 13:29:02 -06:00
|
|
|
calls = append(calls, site)
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
}
|
2013-09-03 13:29:02 -06:00
|
|
|
// TODO(adonovan): sort calls, to ensure test determinism.
|
|
|
|
|
2013-08-27 15:58:26 -06:00
|
|
|
root := ptrAnalysis(o)
|
|
|
|
|
|
|
|
return &callersResult{
|
|
|
|
target: target,
|
|
|
|
root: root,
|
|
|
|
calls: calls,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type callersResult struct {
|
|
|
|
target *ssa.Function
|
|
|
|
root pointer.CallGraphNode
|
2013-09-03 13:29:02 -06:00
|
|
|
calls []pointer.CallSite
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
func (r *callersResult) display(printf printfFunc) {
|
2013-08-27 15:58:26 -06:00
|
|
|
if r.calls == nil {
|
2013-09-03 13:29:02 -06:00
|
|
|
printf(r.target, "%s is not reachable in this program.", r.target)
|
2013-08-27 15:58:26 -06:00
|
|
|
} else {
|
2013-09-03 13:29:02 -06:00
|
|
|
printf(r.target, "%s is called from these %d sites:", r.target, len(r.calls))
|
|
|
|
for _, site := range r.calls {
|
|
|
|
if site.Caller() == r.root {
|
|
|
|
printf(r.target, "the root of the call graph")
|
2013-08-27 15:58:26 -06:00
|
|
|
} else {
|
2013-09-03 13:29:02 -06:00
|
|
|
printf(site, "\t%s from %s", site.Description(), site.Caller().Func())
|
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 *callersResult) toJSON(res *json.Result, fset *token.FileSet) {
|
|
|
|
var callers []json.Caller
|
|
|
|
for _, site := range r.calls {
|
|
|
|
var c json.Caller
|
|
|
|
c.Caller = site.Caller().Func().String()
|
|
|
|
if site.Caller() == r.root {
|
|
|
|
c.Desc = "synthetic call"
|
|
|
|
} else {
|
|
|
|
c.Pos = site.Caller().Func().Prog.Fset.Position(site.Pos()).String()
|
|
|
|
c.Desc = site.Description()
|
|
|
|
}
|
|
|
|
callers = append(callers, c)
|
|
|
|
}
|
|
|
|
res.Callers = callers
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|