// Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package pointer_test import ( "fmt" "go/build" "go/parser" "sort" "code.google.com/p/go.tools/call" "code.google.com/p/go.tools/importer" "code.google.com/p/go.tools/pointer" "code.google.com/p/go.tools/ssa" ) // 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, 0) if err != nil { fmt.Print(err) // parse error return } // Create single-file main package and import its dependencies. mainInfo := imp.CreatePackage("main", 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 }