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. 4 5package oracle 6 7import ( 8 "fmt" 9 "go/token" 10 11 "github.com/visualfc/gotools/oracle/oracle/serial" 12 "golang.org/x/tools/go/callgraph" 13 "golang.org/x/tools/go/loader" 14 "golang.org/x/tools/go/ssa" 15 "golang.org/x/tools/go/ssa/ssautil" 16) 17 18// Callers reports the possible callers of the function 19// immediately enclosing the specified source location. 20// 21func callers(q *Query) error { 22 lconf := loader.Config{Build: q.Build} 23 24 if err := setPTAScope(&lconf, q.Scope); err != nil { 25 return err 26 } 27 28 // Load/parse/type-check the program. 29 lprog, err := lconf.Load() 30 if err != nil { 31 return err 32 } 33 q.Fset = lprog.Fset 34 35 qpos, err := parseQueryPos(lprog, q.Pos, false) 36 if err != nil { 37 return err 38 } 39 40 prog := ssautil.CreateProgram(lprog, 0) 41 42 ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection) 43 if err != nil { 44 return err 45 } 46 47 pkg := prog.Package(qpos.info.Pkg) 48 if pkg == nil { 49 return fmt.Errorf("no SSA package") 50 } 51 if !ssa.HasEnclosingFunction(pkg, qpos.path) { 52 return fmt.Errorf("this position is not inside a function") 53 } 54 55 // Defer SSA construction till after errors are reported. 56 prog.Build() 57 58 target := ssa.EnclosingFunction(pkg, qpos.path) 59 if target == nil { 60 return fmt.Errorf("no SSA function built for this location (dead code?)") 61 } 62 63 // TODO(adonovan): opt: if function is never address-taken, skip 64 // the pointer analysis. Just look for direct calls. This can 65 // be done in a single pass over the SSA. 66 67 // Run the pointer analysis, recording each 68 // call found to originate from target. 69 ptaConfig.BuildCallGraph = true 70 cg := ptrAnalysis(ptaConfig).CallGraph 71 cg.DeleteSyntheticNodes() 72 edges := cg.CreateNode(target).In 73 // TODO(adonovan): sort + dedup calls to ensure test determinism. 74 75 q.result = &callersResult{ 76 target: target, 77 callgraph: cg, 78 edges: edges, 79 } 80 return nil 81} 82 83type callersResult struct { 84 target *ssa.Function 85 callgraph *callgraph.Graph 86 edges []*callgraph.Edge 87} 88 89func (r *callersResult) display(printf printfFunc) { 90 root := r.callgraph.Root 91 if r.edges == nil { 92 printf(r.target, "%s is not reachable in this program.", r.target) 93 } else { 94 printf(r.target, "%s is called from these %d sites:", r.target, len(r.edges)) 95 for _, edge := range r.edges { 96 if edge.Caller == root { 97 printf(r.target, "the root of the call graph") 98 } else { 99 printf(edge, "\t%s from %s", edge.Description(), edge.Caller.Func) 100 } 101 } 102 } 103} 104 105func (r *callersResult) toSerial(res *serial.Result, fset *token.FileSet) { 106 var callers []serial.Caller 107 for _, edge := range r.edges { 108 callers = append(callers, serial.Caller{ 109 Caller: edge.Caller.Func.String(), 110 Pos: fset.Position(edge.Pos()).String(), 111 Desc: edge.Description(), 112 }) 113 } 114 res.Callers = callers 115} 116