Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / cmd / guru / callstack.go
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 main
6
7 import (
8         "fmt"
9         "go/token"
10
11         "golang.org/x/tools/cmd/guru/serial"
12         "golang.org/x/tools/go/callgraph"
13         "golang.org/x/tools/go/callgraph/static"
14         "golang.org/x/tools/go/loader"
15         "golang.org/x/tools/go/ssa"
16         "golang.org/x/tools/go/ssa/ssautil"
17 )
18
19 // The callstack function displays an arbitrary path from a root of the callgraph
20 // to the function at the current position.
21 //
22 // The information may be misleading in a context-insensitive
23 // analysis. e.g. the call path X->Y->Z might be infeasible if Y never
24 // calls Z when it is called from X.  TODO(adonovan): think about UI.
25 //
26 // TODO(adonovan): permit user to specify a starting point other than
27 // the analysis root.
28 //
29 func callstack(q *Query) error {
30         fset := token.NewFileSet()
31         lconf := loader.Config{Fset: fset, Build: q.Build}
32
33         if err := setPTAScope(&lconf, q.Scope); err != nil {
34                 return err
35         }
36
37         // Load/parse/type-check the program.
38         lprog, err := loadWithSoftErrors(&lconf)
39         if err != nil {
40                 return err
41         }
42
43         qpos, err := parseQueryPos(lprog, q.Pos, false)
44         if err != nil {
45                 return err
46         }
47
48         prog := ssautil.CreateProgram(lprog, 0)
49
50         ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
51         if err != nil {
52                 return err
53         }
54
55         pkg := prog.Package(qpos.info.Pkg)
56         if pkg == nil {
57                 return fmt.Errorf("no SSA package")
58         }
59
60         if !ssa.HasEnclosingFunction(pkg, qpos.path) {
61                 return fmt.Errorf("this position is not inside a function")
62         }
63
64         // Defer SSA construction till after errors are reported.
65         prog.Build()
66
67         target := ssa.EnclosingFunction(pkg, qpos.path)
68         if target == nil {
69                 return fmt.Errorf("no SSA function built for this location (dead code?)")
70         }
71
72         var callpath []*callgraph.Edge
73         isEnd := func(n *callgraph.Node) bool { return n.Func == target }
74
75         // First, build a callgraph containing only static call edges,
76         // and search for an arbitrary path from a root to the target function.
77         // This is quick, and the user wants a static path if one exists.
78         cg := static.CallGraph(prog)
79         cg.DeleteSyntheticNodes()
80         for _, ep := range entryPoints(ptaConfig.Mains) {
81                 callpath = callgraph.PathSearch(cg.CreateNode(ep), isEnd)
82                 if callpath != nil {
83                         break
84                 }
85         }
86
87         // No fully static path found.
88         // Run the pointer analysis and build a complete call graph.
89         if callpath == nil {
90                 ptaConfig.BuildCallGraph = true
91                 cg := ptrAnalysis(ptaConfig).CallGraph
92                 cg.DeleteSyntheticNodes()
93                 callpath = callgraph.PathSearch(cg.Root, isEnd)
94                 if callpath != nil {
95                         callpath = callpath[1:] // remove synthetic edge from <root>
96                 }
97         }
98
99         q.Output(fset, &callstackResult{
100                 qpos:     qpos,
101                 target:   target,
102                 callpath: callpath,
103         })
104         return nil
105 }
106
107 type callstackResult struct {
108         qpos     *queryPos
109         target   *ssa.Function
110         callpath []*callgraph.Edge
111 }
112
113 func (r *callstackResult) PrintPlain(printf printfFunc) {
114         if r.callpath != nil {
115                 printf(r.qpos, "Found a call path from root to %s", r.target)
116                 printf(r.target, "%s", r.target)
117                 for i := len(r.callpath) - 1; i >= 0; i-- {
118                         edge := r.callpath[i]
119                         printf(edge, "%s from %s", edge.Description(), edge.Caller.Func)
120                 }
121         } else {
122                 printf(r.target, "%s is unreachable in this analysis scope", r.target)
123         }
124 }
125
126 func (r *callstackResult) JSON(fset *token.FileSet) []byte {
127         var callers []serial.Caller
128         for i := len(r.callpath) - 1; i >= 0; i-- { // (innermost first)
129                 edge := r.callpath[i]
130                 callers = append(callers, serial.Caller{
131                         Pos:    fset.Position(edge.Pos()).String(),
132                         Caller: edge.Caller.Func.String(),
133                         Desc:   edge.Description(),
134                 })
135         }
136         return toJSON(&serial.CallStack{
137                 Pos:     fset.Position(r.target.Pos()).String(),
138                 Target:  r.target.String(),
139                 Callers: callers,
140         })
141 }