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] }