mirror of
https://github.com/golang/go.git
synced 2025-05-06 08:03:03 +00:00
go.tools/oracle: "callees": skip pointer analysis at static call sites.
This improves both performance (most calls are static) and precision (e.g. for static calls in dead code). Also, break callees() function into smaller ones. R=crawshaw CC=golang-dev https://golang.org/cl/38740045
This commit is contained in:
parent
5d70784aca
commit
26d5173f5e
@ -60,27 +60,57 @@ func callees(o *Oracle, qpos *QueryPos) (queryResult, error) {
|
||||
return nil, fmt.Errorf("no SSA function built for this location (dead code?)")
|
||||
}
|
||||
|
||||
// Find the call site.
|
||||
site, err := findCallSite(callerFn, e.Lparen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
funcs, err := findCallees(o, site)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &calleesResult{
|
||||
site: site,
|
||||
funcs: funcs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func findCallSite(fn *ssa.Function, lparen token.Pos) (ssa.CallInstruction, error) {
|
||||
for _, b := range fn.Blocks {
|
||||
for _, instr := range b.Instrs {
|
||||
if site, ok := instr.(ssa.CallInstruction); ok && instr.Pos() == lparen {
|
||||
return site, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("this call site is unreachable in this analysis")
|
||||
}
|
||||
|
||||
func findCallees(o *Oracle, site ssa.CallInstruction) ([]*ssa.Function, error) {
|
||||
// Avoid running the pointer analysis for static calls.
|
||||
if callee := site.Common().StaticCallee(); callee != nil {
|
||||
switch callee.String() {
|
||||
case "runtime.SetFinalizer", "(reflect.Value).Call":
|
||||
// The PTA treats calls to these intrinsics as dynamic.
|
||||
// TODO(adonovan): avoid reliance on PTA internals.
|
||||
|
||||
default:
|
||||
return []*ssa.Function{callee}, nil // singleton
|
||||
}
|
||||
}
|
||||
|
||||
// Dynamic call: use pointer analysis.
|
||||
o.config.BuildCallGraph = true
|
||||
callgraph := ptrAnalysis(o).CallGraph
|
||||
|
||||
// Find the call site and all edges from it.
|
||||
var site ssa.CallInstruction
|
||||
// Find all call edges from the site.
|
||||
calleesMap := make(map[*ssa.Function]bool)
|
||||
var foundCGNode bool
|
||||
for _, n := range callgraph.Nodes() {
|
||||
if n.Func() == callerFn {
|
||||
if site == nil {
|
||||
// First node for callerFn: identify the site.
|
||||
for _, s := range n.Sites() {
|
||||
if s.Pos() == e.Lparen {
|
||||
site = s
|
||||
break
|
||||
}
|
||||
}
|
||||
if site == nil {
|
||||
return nil, fmt.Errorf("this call site is unreachable in this analysis")
|
||||
}
|
||||
}
|
||||
|
||||
if n.Func() == site.Parent() {
|
||||
foundCGNode = true
|
||||
for _, edge := range n.Edges() {
|
||||
if edge.Site == site {
|
||||
calleesMap[edge.Callee.Func()] = true
|
||||
@ -88,8 +118,8 @@ func callees(o *Oracle, qpos *QueryPos) (queryResult, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if site == nil {
|
||||
return nil, fmt.Errorf("this function is unreachable in this analysis")
|
||||
if !foundCGNode {
|
||||
return nil, fmt.Errorf("this call site is unreachable in this analysis")
|
||||
}
|
||||
|
||||
// Discard context, de-duplicate and sort.
|
||||
@ -98,11 +128,7 @@ func callees(o *Oracle, qpos *QueryPos) (queryResult, error) {
|
||||
funcs = append(funcs, f)
|
||||
}
|
||||
sort.Sort(byFuncPos(funcs))
|
||||
|
||||
return &calleesResult{
|
||||
site: site,
|
||||
funcs: funcs,
|
||||
}, nil
|
||||
return funcs, nil
|
||||
}
|
||||
|
||||
type calleesResult struct {
|
||||
|
5
oracle/testdata/src/main/calls.go
vendored
5
oracle/testdata/src/main/calls.go
vendored
@ -69,10 +69,15 @@ func main() {
|
||||
i.f() // @callees callees-err-nil-interface "i.f"
|
||||
}
|
||||
|
||||
var dynamic = func() {}
|
||||
|
||||
// Within dead code, dynamic calls have no callees.
|
||||
func deadcode() {
|
||||
main() // @callees callees-err-deadcode2 "main"
|
||||
// @callers callers-err-deadcode "^"
|
||||
// @callstack callstack-err-deadcode "^"
|
||||
|
||||
dynamic() // @callees callees-err-deadcode3 "dynamic"
|
||||
}
|
||||
|
||||
// This code belongs to init.
|
||||
|
6
oracle/testdata/src/main/calls.golden
vendored
6
oracle/testdata/src/main/calls.golden
vendored
@ -87,11 +87,15 @@ dynamic function call on nil value
|
||||
dynamic method call on nil value
|
||||
|
||||
-------- @callees callees-err-deadcode2 --------
|
||||
this static function call dispatches to:
|
||||
main.main
|
||||
|
||||
Error: this function is unreachable in this analysis
|
||||
-------- @callstack callstack-err-deadcode --------
|
||||
main.deadcode is unreachable in this analysis scope
|
||||
|
||||
-------- @callees callees-err-deadcode3 --------
|
||||
|
||||
Error: this call site is unreachable in this analysis
|
||||
-------- @callers callers-global --------
|
||||
main.init is called from these 1 sites:
|
||||
the root of the call graph
|
||||
|
Loading…
x
Reference in New Issue
Block a user