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