You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

257 lines
6.7 KiB

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