github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/oracle/callers.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  package oracle
     6  
     7  import (
     8  	"fmt"
     9  	"go/token"
    10  
    11  	"golang.org/x/tools/go/callgraph"
    12  	"golang.org/x/tools/go/loader"
    13  	"golang.org/x/tools/go/ssa"
    14  	"golang.org/x/tools/go/ssa/ssautil"
    15  	"golang.org/x/tools/oracle/serial"
    16  )
    17  
    18  // Callers reports the possible callers of the function
    19  // immediately enclosing the specified source location.
    20  //
    21  func 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  
    83  type callersResult struct {
    84  	target    *ssa.Function
    85  	callgraph *callgraph.Graph
    86  	edges     []*callgraph.Edge
    87  }
    88  
    89  func (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  
   105  func (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  }