github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/oracle/callees.go (about) 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 5 // +build go1.5 6 7 package oracle 8 9 import ( 10 "fmt" 11 "go/ast" 12 "go/token" 13 "go/types" 14 "sort" 15 16 "golang.org/x/tools/go/loader" 17 "golang.org/x/tools/go/pointer" 18 "golang.org/x/tools/go/ssa" 19 "golang.org/x/tools/go/ssa/ssautil" 20 "golang.org/x/tools/oracle/serial" 21 ) 22 23 // Callees reports the possible callees of the function call site 24 // identified by the specified source location. 25 func callees(q *Query) error { 26 lconf := loader.Config{Build: q.Build} 27 28 if err := setPTAScope(&lconf, q.Scope); err != nil { 29 return err 30 } 31 32 // Load/parse/type-check the program. 33 lprog, err := lconf.Load() 34 if err != nil { 35 return err 36 } 37 q.Fset = lprog.Fset 38 39 qpos, err := parseQueryPos(lprog, q.Pos, true) // needs exact pos 40 if err != nil { 41 return err 42 } 43 44 // Determine the enclosing call for the specified position. 45 var e *ast.CallExpr 46 for _, n := range qpos.path { 47 if e, _ = n.(*ast.CallExpr); e != nil { 48 break 49 } 50 } 51 if e == nil { 52 return fmt.Errorf("there is no function call here") 53 } 54 // TODO(adonovan): issue an error if the call is "too far 55 // away" from the current selection, as this most likely is 56 // not what the user intended. 57 58 // Reject type conversions. 59 if qpos.info.Types[e.Fun].IsType() { 60 return fmt.Errorf("this is a type conversion, not a function call") 61 } 62 63 // Deal with obviously static calls before constructing SSA form. 64 // Some static calls may yet require SSA construction, 65 // e.g. f := func(){}; f(). 66 switch funexpr := unparen(e.Fun).(type) { 67 case *ast.Ident: 68 switch obj := qpos.info.Uses[funexpr].(type) { 69 case *types.Builtin: 70 // Reject calls to built-ins. 71 return fmt.Errorf("this is a call to the built-in '%s' operator", obj.Name()) 72 case *types.Func: 73 // This is a static function call 74 q.result = &calleesTypesResult{ 75 site: e, 76 callee: obj, 77 } 78 return nil 79 } 80 case *ast.SelectorExpr: 81 sel := qpos.info.Selections[funexpr] 82 if sel == nil { 83 // qualified identifier. 84 // May refer to top level function variable 85 // or to top level function. 86 callee := qpos.info.Uses[funexpr.Sel] 87 if obj, ok := callee.(*types.Func); ok { 88 q.result = &calleesTypesResult{ 89 site: e, 90 callee: obj, 91 } 92 return nil 93 } 94 } else if sel.Kind() == types.MethodVal { 95 recvtype := sel.Recv() 96 if !types.IsInterface(recvtype) { 97 // static method call 98 q.result = &calleesTypesResult{ 99 site: e, 100 callee: sel.Obj().(*types.Func), 101 } 102 return nil 103 } 104 } 105 } 106 107 prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug) 108 109 ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection) 110 if err != nil { 111 return err 112 } 113 114 pkg := prog.Package(qpos.info.Pkg) 115 if pkg == nil { 116 return fmt.Errorf("no SSA package") 117 } 118 119 // Defer SSA construction till after errors are reported. 120 prog.Build() 121 122 // Ascertain calling function and call site. 123 callerFn := ssa.EnclosingFunction(pkg, qpos.path) 124 if callerFn == nil { 125 return fmt.Errorf("no SSA function built for this location (dead code?)") 126 } 127 128 // Find the call site. 129 site, err := findCallSite(callerFn, e) 130 if err != nil { 131 return err 132 } 133 134 funcs, err := findCallees(ptaConfig, site) 135 if err != nil { 136 return err 137 } 138 139 q.result = &calleesSSAResult{ 140 site: site, 141 funcs: funcs, 142 } 143 return nil 144 } 145 146 func findCallSite(fn *ssa.Function, call *ast.CallExpr) (ssa.CallInstruction, error) { 147 instr, _ := fn.ValueForExpr(call) 148 callInstr, _ := instr.(ssa.CallInstruction) 149 if instr == nil { 150 return nil, fmt.Errorf("this call site is unreachable in this analysis") 151 } 152 return callInstr, nil 153 } 154 155 func findCallees(conf *pointer.Config, site ssa.CallInstruction) ([]*ssa.Function, error) { 156 // Avoid running the pointer analysis for static calls. 157 if callee := site.Common().StaticCallee(); callee != nil { 158 switch callee.String() { 159 case "runtime.SetFinalizer", "(reflect.Value).Call": 160 // The PTA treats calls to these intrinsics as dynamic. 161 // TODO(adonovan): avoid reliance on PTA internals. 162 163 default: 164 return []*ssa.Function{callee}, nil // singleton 165 } 166 } 167 168 // Dynamic call: use pointer analysis. 169 conf.BuildCallGraph = true 170 cg := ptrAnalysis(conf).CallGraph 171 cg.DeleteSyntheticNodes() 172 173 // Find all call edges from the site. 174 n := cg.Nodes[site.Parent()] 175 if n == nil { 176 return nil, fmt.Errorf("this call site is unreachable in this analysis") 177 } 178 calleesMap := make(map[*ssa.Function]bool) 179 for _, edge := range n.Out { 180 if edge.Site == site { 181 calleesMap[edge.Callee.Func] = true 182 } 183 } 184 185 // De-duplicate and sort. 186 funcs := make([]*ssa.Function, 0, len(calleesMap)) 187 for f := range calleesMap { 188 funcs = append(funcs, f) 189 } 190 sort.Sort(byFuncPos(funcs)) 191 return funcs, nil 192 } 193 194 type calleesSSAResult struct { 195 site ssa.CallInstruction 196 funcs []*ssa.Function 197 } 198 199 type calleesTypesResult struct { 200 site *ast.CallExpr 201 callee *types.Func 202 } 203 204 func (r *calleesSSAResult) display(printf printfFunc) { 205 if len(r.funcs) == 0 { 206 // dynamic call on a provably nil func/interface 207 printf(r.site, "%s on nil value", r.site.Common().Description()) 208 } else { 209 printf(r.site, "this %s dispatches to:", r.site.Common().Description()) 210 for _, callee := range r.funcs { 211 printf(callee, "\t%s", callee) 212 } 213 } 214 } 215 216 func (r *calleesSSAResult) toSerial(res *serial.Result, fset *token.FileSet) { 217 j := &serial.Callees{ 218 Pos: fset.Position(r.site.Pos()).String(), 219 Desc: r.site.Common().Description(), 220 } 221 for _, callee := range r.funcs { 222 j.Callees = append(j.Callees, &serial.CalleesItem{ 223 Name: callee.String(), 224 Pos: fset.Position(callee.Pos()).String(), 225 }) 226 } 227 res.Callees = j 228 } 229 230 func (r *calleesTypesResult) display(printf printfFunc) { 231 printf(r.site, "this static function call dispatches to:") 232 printf(r.callee, "\t%s", r.callee.FullName()) 233 } 234 235 func (r *calleesTypesResult) toSerial(res *serial.Result, fset *token.FileSet) { 236 j := &serial.Callees{ 237 Pos: fset.Position(r.site.Pos()).String(), 238 Desc: "static function call", 239 } 240 j.Callees = []*serial.CalleesItem{ 241 &serial.CalleesItem{ 242 Name: r.callee.FullName(), 243 Pos: fset.Position(r.callee.Pos()).String(), 244 }, 245 } 246 res.Callees = j 247 } 248 249 // NB: byFuncPos is not deterministic across packages since it depends on load order. 250 // Use lessPos if the tests need it. 251 type byFuncPos []*ssa.Function 252 253 func (a byFuncPos) Len() int { return len(a) } 254 func (a byFuncPos) Less(i, j int) bool { return a[i].Pos() < a[j].Pos() } 255 func (a byFuncPos) Swap(i, j int) { a[i], a[j] = a[j], a[i] }