github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/go/pointer/example_test.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 pointer_test
     6  
     7  import (
     8  	"fmt"
     9  	"sort"
    10  
    11  	"golang.org/x/tools/go/callgraph"
    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  
    18  // This program demonstrates how to use the pointer analysis to
    19  // obtain a conservative call-graph of a Go program.
    20  // It also shows how to compute the points-to set of a variable,
    21  // in this case, (C).f's ch parameter.
    22  func Example() {
    23  	const myprog = `
    24  package main
    25  
    26  import "fmt"
    27  
    28  type I interface {
    29  	f(map[string]int)
    30  }
    31  
    32  type C struct{}
    33  
    34  func (C) f(m map[string]int) {
    35  	fmt.Println("C.f()")
    36  }
    37  
    38  func main() {
    39  	var i I = C{}
    40  	x := map[string]int{"one":1}
    41  	i.f(x) // dynamic method call
    42  }
    43  `
    44  	var conf loader.Config
    45  
    46  	// Parse the input file, a string.
    47  	// (Command-line tools should use conf.FromArgs.)
    48  	file, err := conf.ParseFile("myprog.go", myprog)
    49  	if err != nil {
    50  		fmt.Print(err) // parse error
    51  		return
    52  	}
    53  
    54  	// Create single-file main package and import its dependencies.
    55  	conf.CreateFromFiles("main", file)
    56  
    57  	iprog, err := conf.Load()
    58  	if err != nil {
    59  		fmt.Print(err) // type error in some package
    60  		return
    61  	}
    62  
    63  	// Create SSA-form program representation.
    64  	prog := ssautil.CreateProgram(iprog, ssa.InstantiateGenerics)
    65  	mainPkg := prog.Package(iprog.Created[0].Pkg)
    66  
    67  	// Build SSA code for bodies of all functions in the whole program.
    68  	prog.Build()
    69  
    70  	// Configure the pointer analysis to build a call-graph.
    71  	config := &pointer.Config{
    72  		Mains:          []*ssa.Package{mainPkg},
    73  		BuildCallGraph: true,
    74  	}
    75  
    76  	// Query points-to set of (C).f's parameter m, a map.
    77  	C := mainPkg.Type("C").Type()
    78  	Cfm := prog.LookupMethod(C, mainPkg.Pkg, "f").Params[1]
    79  	config.AddQuery(Cfm)
    80  
    81  	// Run the pointer analysis.
    82  	result, err := pointer.Analyze(config)
    83  	if err != nil {
    84  		panic(err) // internal error in pointer analysis
    85  	}
    86  
    87  	// Find edges originating from the main package.
    88  	// By converting to strings, we de-duplicate nodes
    89  	// representing the same function due to context sensitivity.
    90  	var edges []string
    91  	callgraph.GraphVisitEdges(result.CallGraph, func(edge *callgraph.Edge) error {
    92  		caller := edge.Caller.Func
    93  		if caller.Pkg == mainPkg {
    94  			edges = append(edges, fmt.Sprint(caller, " --> ", edge.Callee.Func))
    95  		}
    96  		return nil
    97  	})
    98  
    99  	// Print the edges in sorted order.
   100  	sort.Strings(edges)
   101  	for _, edge := range edges {
   102  		fmt.Println(edge)
   103  	}
   104  	fmt.Println()
   105  
   106  	// Print the labels of (C).f(m)'s points-to set.
   107  	fmt.Println("m may point to:")
   108  	var labels []string
   109  	for _, l := range result.Queries[Cfm].PointsTo().Labels() {
   110  		label := fmt.Sprintf("  %s: %s", prog.Fset.Position(l.Pos()), l)
   111  		labels = append(labels, label)
   112  	}
   113  	sort.Strings(labels)
   114  	for _, label := range labels {
   115  		fmt.Println(label)
   116  	}
   117  
   118  	// Output:
   119  	// (main.C).f --> fmt.Println
   120  	// main.init --> fmt.init
   121  	// main.main --> (main.C).f
   122  	//
   123  	// m may point to:
   124  	//   myprog.go:18:21: makemap
   125  }