go/oracle/callers.go
Alan Donovan 3a4c0462d8 go.tools/oracle: change -mode argument into subcommand.
e.g. "oracle callgraph <package>"

Also: simplified error handling.
Eliminated oracle.errorf because it prepends "file:line:col: "
to the error message so the main function can't safely prepend "Error: ".
The position wasn't interesting though: it was just -pos, more or less.

R=crawshaw, dominik.honnef, r
CC=golang-dev
https://golang.org/cl/13864044
2013-09-25 14:34:39 -04:00

92 lines
2.3 KiB
Go

// 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.
package oracle
import (
"fmt"
"go/token"
"code.google.com/p/go.tools/oracle/serial"
"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, qpos *QueryPos) (queryResult, error) {
pkg := o.prog.Package(qpos.info.Pkg)
if pkg == nil {
return nil, fmt.Errorf("no SSA package")
}
if !ssa.HasEnclosingFunction(pkg, qpos.path) {
return nil, fmt.Errorf("this position is not inside a function")
}
buildSSA(o)
target := ssa.EnclosingFunction(pkg, qpos.path)
if target == nil {
return nil, fmt.Errorf("no SSA function built for this location (dead code?)")
}
// Run the pointer analysis, recording each
// call found to originate from target.
var calls []pointer.CallSite
o.config.Call = func(site pointer.CallSite, callee pointer.CallGraphNode) {
if callee.Func() == target {
calls = append(calls, site)
}
}
// TODO(adonovan): sort calls, to ensure test determinism.
root := ptrAnalysis(o)
return &callersResult{
target: target,
root: root,
calls: calls,
}, nil
}
type callersResult struct {
target *ssa.Function
root pointer.CallGraphNode
calls []pointer.CallSite
}
func (r *callersResult) display(printf printfFunc) {
if r.calls == nil {
printf(r.target, "%s is not reachable in this program.", r.target)
} else {
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")
} else {
printf(site, "\t%s from %s", site.Description(), site.Caller().Func())
}
}
}
}
func (r *callersResult) toSerial(res *serial.Result, fset *token.FileSet) {
var callers []serial.Caller
for _, site := range r.calls {
var c serial.Caller
c.Caller = site.Caller().Func().String()
if site.Caller() == r.root {
c.Desc = "synthetic call"
} else {
c.Pos = fset.Position(site.Pos()).String()
c.Desc = site.Description()
}
callers = append(callers, c)
}
res.Callers = callers
}