1// Copyright 2014 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 analysis
6
7// This file computes the CALLERS and CALLEES relations from the call
8// graph.  CALLERS/CALLEES information is displayed in the lower pane
9// when a "func" token or ast.CallExpr.Lparen is clicked, respectively.
10
11import (
12	"fmt"
13	"go/ast"
14	"go/token"
15	"go/types"
16	"log"
17	"math/big"
18	"sort"
19
20	"golang.org/x/tools/go/callgraph"
21	"golang.org/x/tools/go/ssa"
22)
23
24// doCallgraph computes the CALLEES and CALLERS relations.
25func (a *analysis) doCallgraph(cg *callgraph.Graph) {
26	log.Print("Deleting synthetic nodes...")
27	// TODO(adonovan): opt: DeleteSyntheticNodes is asymptotically
28	// inefficient and can be (unpredictably) slow.
29	cg.DeleteSyntheticNodes()
30	log.Print("Synthetic nodes deleted")
31
32	// Populate nodes of package call graphs (PCGs).
33	for _, n := range cg.Nodes {
34		a.pcgAddNode(n.Func)
35	}
36	// Within each PCG, sort funcs by name.
37	for _, pcg := range a.pcgs {
38		pcg.sortNodes()
39	}
40
41	calledFuncs := make(map[ssa.CallInstruction]map[*ssa.Function]bool)
42	callingSites := make(map[*ssa.Function]map[ssa.CallInstruction]bool)
43	for _, n := range cg.Nodes {
44		for _, e := range n.Out {
45			if e.Site == nil {
46				continue // a call from a synthetic node such as <root>
47			}
48
49			// Add (site pos, callee) to calledFuncs.
50			// (Dynamic calls only.)
51			callee := e.Callee.Func
52
53			a.pcgAddEdge(n.Func, callee)
54
55			if callee.Synthetic != "" {
56				continue // call of a package initializer
57			}
58
59			if e.Site.Common().StaticCallee() == nil {
60				// dynamic call
61				// (CALLEES information for static calls
62				// is computed using SSA information.)
63				lparen := e.Site.Common().Pos()
64				if lparen != token.NoPos {
65					fns := calledFuncs[e.Site]
66					if fns == nil {
67						fns = make(map[*ssa.Function]bool)
68						calledFuncs[e.Site] = fns
69					}
70					fns[callee] = true
71				}
72			}
73
74			// Add (callee, site) to callingSites.
75			fns := callingSites[callee]
76			if fns == nil {
77				fns = make(map[ssa.CallInstruction]bool)
78				callingSites[callee] = fns
79			}
80			fns[e.Site] = true
81		}
82	}
83
84	// CALLEES.
85	log.Print("Callees...")
86	for site, fns := range calledFuncs {
87		var funcs funcsByPos
88		for fn := range fns {
89			funcs = append(funcs, fn)
90		}
91		sort.Sort(funcs)
92
93		a.addCallees(site, funcs)
94	}
95
96	// CALLERS
97	log.Print("Callers...")
98	for callee, sites := range callingSites {
99		pos := funcToken(callee)
100		if pos == token.NoPos {
101			log.Printf("CALLERS: skipping %s: no pos", callee)
102			continue
103		}
104
105		var this *types.Package // for relativizing names
106		if callee.Pkg != nil {
107			this = callee.Pkg.Pkg
108		}
109
110		// Compute sites grouped by parent, with text and URLs.
111		sitesByParent := make(map[*ssa.Function]sitesByPos)
112		for site := range sites {
113			fn := site.Parent()
114			sitesByParent[fn] = append(sitesByParent[fn], site)
115		}
116		var funcs funcsByPos
117		for fn := range sitesByParent {
118			funcs = append(funcs, fn)
119		}
120		sort.Sort(funcs)
121
122		v := callersJSON{
123			Callee:  callee.String(),
124			Callers: []callerJSON{}, // (JS wants non-nil)
125		}
126		for _, fn := range funcs {
127			caller := callerJSON{
128				Func:  prettyFunc(this, fn),
129				Sites: []anchorJSON{}, // (JS wants non-nil)
130			}
131			sites := sitesByParent[fn]
132			sort.Sort(sites)
133			for _, site := range sites {
134				pos := site.Common().Pos()
135				if pos != token.NoPos {
136					caller.Sites = append(caller.Sites, anchorJSON{
137						Text: fmt.Sprintf("%d", a.prog.Fset.Position(pos).Line),
138						Href: a.posURL(pos, len("(")),
139					})
140				}
141			}
142			v.Callers = append(v.Callers, caller)
143		}
144
145		fi, offset := a.fileAndOffset(pos)
146		fi.addLink(aLink{
147			start:   offset,
148			end:     offset + len("func"),
149			title:   fmt.Sprintf("%d callers", len(sites)),
150			onclick: fmt.Sprintf("onClickCallers(%d)", fi.addData(v)),
151		})
152	}
153
154	// PACKAGE CALLGRAPH
155	log.Print("Package call graph...")
156	for pkg, pcg := range a.pcgs {
157		// Maps (*ssa.Function).RelString() to index in JSON CALLGRAPH array.
158		index := make(map[string]int)
159
160		// Treat exported functions (and exported methods of
161		// exported named types) as roots even if they aren't
162		// actually called from outside the package.
163		for i, n := range pcg.nodes {
164			if i == 0 || n.fn.Object() == nil || !n.fn.Object().Exported() {
165				continue
166			}
167			recv := n.fn.Signature.Recv()
168			if recv == nil || deref(recv.Type()).(*types.Named).Obj().Exported() {
169				roots := &pcg.nodes[0].edges
170				roots.SetBit(roots, i, 1)
171			}
172			index[n.fn.RelString(pkg.Pkg)] = i
173		}
174
175		json := a.pcgJSON(pcg)
176
177		// TODO(adonovan): pkg.Path() is not unique!
178		// It is possible to declare a non-test package called x_test.
179		a.result.pkgInfo(pkg.Pkg.Path()).setCallGraph(json, index)
180	}
181}
182
183// addCallees adds client data and links for the facts that site calls fns.
184func (a *analysis) addCallees(site ssa.CallInstruction, fns []*ssa.Function) {
185	v := calleesJSON{
186		Descr:   site.Common().Description(),
187		Callees: []anchorJSON{}, // (JS wants non-nil)
188	}
189	var this *types.Package // for relativizing names
190	if p := site.Parent().Package(); p != nil {
191		this = p.Pkg
192	}
193
194	for _, fn := range fns {
195		v.Callees = append(v.Callees, anchorJSON{
196			Text: prettyFunc(this, fn),
197			Href: a.posURL(funcToken(fn), len("func")),
198		})
199	}
200
201	fi, offset := a.fileAndOffset(site.Common().Pos())
202	fi.addLink(aLink{
203		start:   offset,
204		end:     offset + len("("),
205		title:   fmt.Sprintf("%d callees", len(v.Callees)),
206		onclick: fmt.Sprintf("onClickCallees(%d)", fi.addData(v)),
207	})
208}
209
210// -- utilities --------------------------------------------------------
211
212// stable order within packages but undefined across packages.
213type funcsByPos []*ssa.Function
214
215func (a funcsByPos) Less(i, j int) bool { return a[i].Pos() < a[j].Pos() }
216func (a funcsByPos) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
217func (a funcsByPos) Len() int           { return len(a) }
218
219type sitesByPos []ssa.CallInstruction
220
221func (a sitesByPos) Less(i, j int) bool { return a[i].Common().Pos() < a[j].Common().Pos() }
222func (a sitesByPos) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
223func (a sitesByPos) Len() int           { return len(a) }
224
225func funcToken(fn *ssa.Function) token.Pos {
226	switch syntax := fn.Syntax().(type) {
227	case *ast.FuncLit:
228		return syntax.Type.Func
229	case *ast.FuncDecl:
230		return syntax.Type.Func
231	}
232	return token.NoPos
233}
234
235// prettyFunc pretty-prints fn for the user interface.
236// TODO(adonovan): return HTML so we have more markup freedom.
237func prettyFunc(this *types.Package, fn *ssa.Function) string {
238	if fn.Parent() != nil {
239		return fmt.Sprintf("%s in %s",
240			types.TypeString(fn.Signature, types.RelativeTo(this)),
241			prettyFunc(this, fn.Parent()))
242	}
243	if fn.Synthetic != "" && fn.Name() == "init" {
244		// (This is the actual initializer, not a declared 'func init').
245		if fn.Pkg.Pkg == this {
246			return "package initializer"
247		}
248		return fmt.Sprintf("%q package initializer", fn.Pkg.Pkg.Path())
249	}
250	return fn.RelString(this)
251}
252
253// -- intra-package callgraph ------------------------------------------
254
255// pcgNode represents a node in the package call graph (PCG).
256type pcgNode struct {
257	fn     *ssa.Function
258	pretty string  // cache of prettyFunc(fn)
259	edges  big.Int // set of callee func indices
260}
261
262// A packageCallGraph represents the intra-package edges of the global call graph.
263// The zeroth node indicates "all external functions".
264type packageCallGraph struct {
265	nodeIndex map[*ssa.Function]int // maps func to node index (a small int)
266	nodes     []*pcgNode            // maps node index to node
267}
268
269// sortNodes populates pcg.nodes in name order and updates the nodeIndex.
270func (pcg *packageCallGraph) sortNodes() {
271	nodes := make([]*pcgNode, 0, len(pcg.nodeIndex))
272	nodes = append(nodes, &pcgNode{fn: nil, pretty: "<external>"})
273	for fn := range pcg.nodeIndex {
274		nodes = append(nodes, &pcgNode{
275			fn:     fn,
276			pretty: prettyFunc(fn.Pkg.Pkg, fn),
277		})
278	}
279	sort.Sort(pcgNodesByPretty(nodes[1:]))
280	for i, n := range nodes {
281		pcg.nodeIndex[n.fn] = i
282	}
283	pcg.nodes = nodes
284}
285
286func (pcg *packageCallGraph) addEdge(caller, callee *ssa.Function) {
287	var callerIndex int
288	if caller.Pkg == callee.Pkg {
289		// intra-package edge
290		callerIndex = pcg.nodeIndex[caller]
291		if callerIndex < 1 {
292			panic(caller)
293		}
294	}
295	edges := &pcg.nodes[callerIndex].edges
296	edges.SetBit(edges, pcg.nodeIndex[callee], 1)
297}
298
299func (a *analysis) pcgAddNode(fn *ssa.Function) {
300	if fn.Pkg == nil {
301		return
302	}
303	pcg, ok := a.pcgs[fn.Pkg]
304	if !ok {
305		pcg = &packageCallGraph{nodeIndex: make(map[*ssa.Function]int)}
306		a.pcgs[fn.Pkg] = pcg
307	}
308	pcg.nodeIndex[fn] = -1
309}
310
311func (a *analysis) pcgAddEdge(caller, callee *ssa.Function) {
312	if callee.Pkg != nil {
313		a.pcgs[callee.Pkg].addEdge(caller, callee)
314	}
315}
316
317// pcgJSON returns a new slice of callgraph JSON values.
318func (a *analysis) pcgJSON(pcg *packageCallGraph) []*PCGNodeJSON {
319	var nodes []*PCGNodeJSON
320	for _, n := range pcg.nodes {
321
322		// TODO(adonovan): why is there no good way to iterate
323		// over the set bits of a big.Int?
324		var callees []int
325		nbits := n.edges.BitLen()
326		for j := 0; j < nbits; j++ {
327			if n.edges.Bit(j) == 1 {
328				callees = append(callees, j)
329			}
330		}
331
332		var pos token.Pos
333		if n.fn != nil {
334			pos = funcToken(n.fn)
335		}
336		nodes = append(nodes, &PCGNodeJSON{
337			Func: anchorJSON{
338				Text: n.pretty,
339				Href: a.posURL(pos, len("func")),
340			},
341			Callees: callees,
342		})
343	}
344	return nodes
345}
346
347type pcgNodesByPretty []*pcgNode
348
349func (a pcgNodesByPretty) Less(i, j int) bool { return a[i].pretty < a[j].pretty }
350func (a pcgNodesByPretty) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
351func (a pcgNodesByPretty) Len() int           { return len(a) }
352