func checkCallsExpectation(prog *ssa.Program, e *expectation, callgraph call.Graph) bool {
	found := make(map[string]int)
	err := call.GraphVisitEdges(callgraph, func(edge call.Edge) error {
		// Name-based matching is inefficient but it allows us to
		// match functions whose names that would not appear in an
		// index ("<root>") or which are not unique ("[email protected]").
		if edge.Caller.Func().String() == e.args[0] {
			calleeStr := edge.Callee.Func().String()
			if calleeStr == e.args[1] {
				return errOK // expectation satisified; stop the search
			}
			found[calleeStr]++
		}
		return nil
	})
	if err == errOK {
		return true
	}
	if len(found) == 0 {
		e.errorf("didn't find any calls from %s", e.args[0])
	}
	e.errorf("found no call from %s to %s, but only to %s",
		e.args[0], e.args[1], join(found))
	return false
}
Example #2
0
// Callers reports the possible callers of the function
// immediately enclosing the specified source location.
//
// TODO(adonovan): if a caller is a wrapper, show the caller's caller.
//
func callers(o *Oracle, qpos *QueryPos) (queryResult, error) {
	pkg := o.prog.Package(qpos.info.Pkg)
	if pkg == nil {
		return nil, fmt.Errorf("no SSA package")
	}
	if !ssa.HasEnclosingFunction(pkg, qpos.path) {
		return nil, fmt.Errorf("this position is not inside a function")
	}

	buildSSA(o)

	target := ssa.EnclosingFunction(pkg, qpos.path)
	if target == nil {
		return nil, fmt.Errorf("no SSA function built for this location (dead code?)")
	}

	// Run the pointer analysis, recording each
	// call found to originate from target.
	o.config.BuildCallGraph = true
	callgraph := ptrAnalysis(o).CallGraph
	var edges []call.Edge
	call.GraphVisitEdges(callgraph, func(edge call.Edge) error {
		if edge.Callee.Func() == target {
			edges = append(edges, edge)
		}
		return nil
	})
	// TODO(adonovan): sort + dedup calls to ensure test determinism.

	return &callersResult{
		target:    target,
		callgraph: callgraph,
		edges:     edges,
	}, nil
}
Example #3
0
// This program demonstrates how to use the pointer analysis to
// obtain a conservative call-graph of a Go program.
//
func Example() {
	const myprog = `
package main

import "fmt"

type I interface {
	f()
}

type C struct{}

func (C) f() {
	fmt.Println("C.f()")
}

func main() {
	var i I = C{}
	i.f() // dynamic method call
}
`
	// Construct an importer.
	// Imports will be loaded as if by 'go build'.
	imp := importer.New(&importer.Config{Build: &build.Default})

	// Parse the input file.
	file, err := parser.ParseFile(imp.Fset, "myprog.go", myprog, parser.DeclarationErrors)
	if err != nil {
		fmt.Print(err) // parse error
		return
	}

	// Create a "main" package containing one file.
	mainInfo := imp.LoadMainPackage(file)

	// Create SSA-form program representation.
	var mode ssa.BuilderMode
	prog := ssa.NewProgram(imp.Fset, mode)
	if err := prog.CreatePackages(imp); err != nil {
		fmt.Print(err) // type error in some package
		return
	}
	mainPkg := prog.Package(mainInfo.Pkg)

	// Build SSA code for bodies of all functions in the whole program.
	prog.BuildAll()

	// Run the pointer analysis and build the complete callgraph.
	config := &pointer.Config{
		Mains:          []*ssa.Package{mainPkg},
		BuildCallGraph: true,
	}
	result := pointer.Analyze(config)

	// Find edges originating from the main package.
	// By converting to strings, we de-duplicate nodes
	// representing the same function due to context sensitivity.
	var edges []string
	call.GraphVisitEdges(result.CallGraph, func(edge call.Edge) error {
		caller := edge.Caller.Func()
		if caller.Pkg == mainPkg {
			edges = append(edges, fmt.Sprint(caller, " --> ", edge.Callee.Func()))
		}
		return nil
	})

	// Print the edges in sorted order.
	sort.Strings(edges)
	for _, edge := range edges {
		fmt.Println(edge)
	}

	// Output:
	// (main.C).f --> fmt.Println
	// main.init --> fmt.init
	// main.main --> (main.C).f
}