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 / godoc / analysis / callgraph.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201028153306-37f0764111ff/godoc/analysis/callgraph.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201028153306-37f0764111ff/godoc/analysis/callgraph.go
new file mode 100644 (file)
index 0000000..492022d
--- /dev/null
@@ -0,0 +1,351 @@
+// Copyright 2014 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 analysis
+
+// This file computes the CALLERS and CALLEES relations from the call
+// graph.  CALLERS/CALLEES information is displayed in the lower pane
+// when a "func" token or ast.CallExpr.Lparen is clicked, respectively.
+
+import (
+       "fmt"
+       "go/ast"
+       "go/token"
+       "go/types"
+       "log"
+       "math/big"
+       "sort"
+
+       "golang.org/x/tools/go/callgraph"
+       "golang.org/x/tools/go/ssa"
+)
+
+// doCallgraph computes the CALLEES and CALLERS relations.
+func (a *analysis) doCallgraph(cg *callgraph.Graph) {
+       log.Print("Deleting synthetic nodes...")
+       // TODO(adonovan): opt: DeleteSyntheticNodes is asymptotically
+       // inefficient and can be (unpredictably) slow.
+       cg.DeleteSyntheticNodes()
+       log.Print("Synthetic nodes deleted")
+
+       // Populate nodes of package call graphs (PCGs).
+       for _, n := range cg.Nodes {
+               a.pcgAddNode(n.Func)
+       }
+       // Within each PCG, sort funcs by name.
+       for _, pcg := range a.pcgs {
+               pcg.sortNodes()
+       }
+
+       calledFuncs := make(map[ssa.CallInstruction]map[*ssa.Function]bool)
+       callingSites := make(map[*ssa.Function]map[ssa.CallInstruction]bool)
+       for _, n := range cg.Nodes {
+               for _, e := range n.Out {
+                       if e.Site == nil {
+                               continue // a call from a synthetic node such as <root>
+                       }
+
+                       // Add (site pos, callee) to calledFuncs.
+                       // (Dynamic calls only.)
+                       callee := e.Callee.Func
+
+                       a.pcgAddEdge(n.Func, callee)
+
+                       if callee.Synthetic != "" {
+                               continue // call of a package initializer
+                       }
+
+                       if e.Site.Common().StaticCallee() == nil {
+                               // dynamic call
+                               // (CALLEES information for static calls
+                               // is computed using SSA information.)
+                               lparen := e.Site.Common().Pos()
+                               if lparen != token.NoPos {
+                                       fns := calledFuncs[e.Site]
+                                       if fns == nil {
+                                               fns = make(map[*ssa.Function]bool)
+                                               calledFuncs[e.Site] = fns
+                                       }
+                                       fns[callee] = true
+                               }
+                       }
+
+                       // Add (callee, site) to callingSites.
+                       fns := callingSites[callee]
+                       if fns == nil {
+                               fns = make(map[ssa.CallInstruction]bool)
+                               callingSites[callee] = fns
+                       }
+                       fns[e.Site] = true
+               }
+       }
+
+       // CALLEES.
+       log.Print("Callees...")
+       for site, fns := range calledFuncs {
+               var funcs funcsByPos
+               for fn := range fns {
+                       funcs = append(funcs, fn)
+               }
+               sort.Sort(funcs)
+
+               a.addCallees(site, funcs)
+       }
+
+       // CALLERS
+       log.Print("Callers...")
+       for callee, sites := range callingSites {
+               pos := funcToken(callee)
+               if pos == token.NoPos {
+                       log.Printf("CALLERS: skipping %s: no pos", callee)
+                       continue
+               }
+
+               var this *types.Package // for relativizing names
+               if callee.Pkg != nil {
+                       this = callee.Pkg.Pkg
+               }
+
+               // Compute sites grouped by parent, with text and URLs.
+               sitesByParent := make(map[*ssa.Function]sitesByPos)
+               for site := range sites {
+                       fn := site.Parent()
+                       sitesByParent[fn] = append(sitesByParent[fn], site)
+               }
+               var funcs funcsByPos
+               for fn := range sitesByParent {
+                       funcs = append(funcs, fn)
+               }
+               sort.Sort(funcs)
+
+               v := callersJSON{
+                       Callee:  callee.String(),
+                       Callers: []callerJSON{}, // (JS wants non-nil)
+               }
+               for _, fn := range funcs {
+                       caller := callerJSON{
+                               Func:  prettyFunc(this, fn),
+                               Sites: []anchorJSON{}, // (JS wants non-nil)
+                       }
+                       sites := sitesByParent[fn]
+                       sort.Sort(sites)
+                       for _, site := range sites {
+                               pos := site.Common().Pos()
+                               if pos != token.NoPos {
+                                       caller.Sites = append(caller.Sites, anchorJSON{
+                                               Text: fmt.Sprintf("%d", a.prog.Fset.Position(pos).Line),
+                                               Href: a.posURL(pos, len("(")),
+                                       })
+                               }
+                       }
+                       v.Callers = append(v.Callers, caller)
+               }
+
+               fi, offset := a.fileAndOffset(pos)
+               fi.addLink(aLink{
+                       start:   offset,
+                       end:     offset + len("func"),
+                       title:   fmt.Sprintf("%d callers", len(sites)),
+                       onclick: fmt.Sprintf("onClickCallers(%d)", fi.addData(v)),
+               })
+       }
+
+       // PACKAGE CALLGRAPH
+       log.Print("Package call graph...")
+       for pkg, pcg := range a.pcgs {
+               // Maps (*ssa.Function).RelString() to index in JSON CALLGRAPH array.
+               index := make(map[string]int)
+
+               // Treat exported functions (and exported methods of
+               // exported named types) as roots even if they aren't
+               // actually called from outside the package.
+               for i, n := range pcg.nodes {
+                       if i == 0 || n.fn.Object() == nil || !n.fn.Object().Exported() {
+                               continue
+                       }
+                       recv := n.fn.Signature.Recv()
+                       if recv == nil || deref(recv.Type()).(*types.Named).Obj().Exported() {
+                               roots := &pcg.nodes[0].edges
+                               roots.SetBit(roots, i, 1)
+                       }
+                       index[n.fn.RelString(pkg.Pkg)] = i
+               }
+
+               json := a.pcgJSON(pcg)
+
+               // TODO(adonovan): pkg.Path() is not unique!
+               // It is possible to declare a non-test package called x_test.
+               a.result.pkgInfo(pkg.Pkg.Path()).setCallGraph(json, index)
+       }
+}
+
+// addCallees adds client data and links for the facts that site calls fns.
+func (a *analysis) addCallees(site ssa.CallInstruction, fns []*ssa.Function) {
+       v := calleesJSON{
+               Descr:   site.Common().Description(),
+               Callees: []anchorJSON{}, // (JS wants non-nil)
+       }
+       var this *types.Package // for relativizing names
+       if p := site.Parent().Package(); p != nil {
+               this = p.Pkg
+       }
+
+       for _, fn := range fns {
+               v.Callees = append(v.Callees, anchorJSON{
+                       Text: prettyFunc(this, fn),
+                       Href: a.posURL(funcToken(fn), len("func")),
+               })
+       }
+
+       fi, offset := a.fileAndOffset(site.Common().Pos())
+       fi.addLink(aLink{
+               start:   offset,
+               end:     offset + len("("),
+               title:   fmt.Sprintf("%d callees", len(v.Callees)),
+               onclick: fmt.Sprintf("onClickCallees(%d)", fi.addData(v)),
+       })
+}
+
+// -- utilities --------------------------------------------------------
+
+// stable order within packages but undefined across packages.
+type funcsByPos []*ssa.Function
+
+func (a funcsByPos) Less(i, j int) bool { return a[i].Pos() < a[j].Pos() }
+func (a funcsByPos) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
+func (a funcsByPos) Len() int           { return len(a) }
+
+type sitesByPos []ssa.CallInstruction
+
+func (a sitesByPos) Less(i, j int) bool { return a[i].Common().Pos() < a[j].Common().Pos() }
+func (a sitesByPos) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
+func (a sitesByPos) Len() int           { return len(a) }
+
+func funcToken(fn *ssa.Function) token.Pos {
+       switch syntax := fn.Syntax().(type) {
+       case *ast.FuncLit:
+               return syntax.Type.Func
+       case *ast.FuncDecl:
+               return syntax.Type.Func
+       }
+       return token.NoPos
+}
+
+// prettyFunc pretty-prints fn for the user interface.
+// TODO(adonovan): return HTML so we have more markup freedom.
+func prettyFunc(this *types.Package, fn *ssa.Function) string {
+       if fn.Parent() != nil {
+               return fmt.Sprintf("%s in %s",
+                       types.TypeString(fn.Signature, types.RelativeTo(this)),
+                       prettyFunc(this, fn.Parent()))
+       }
+       if fn.Synthetic != "" && fn.Name() == "init" {
+               // (This is the actual initializer, not a declared 'func init').
+               if fn.Pkg.Pkg == this {
+                       return "package initializer"
+               }
+               return fmt.Sprintf("%q package initializer", fn.Pkg.Pkg.Path())
+       }
+       return fn.RelString(this)
+}
+
+// -- intra-package callgraph ------------------------------------------
+
+// pcgNode represents a node in the package call graph (PCG).
+type pcgNode struct {
+       fn     *ssa.Function
+       pretty string  // cache of prettyFunc(fn)
+       edges  big.Int // set of callee func indices
+}
+
+// A packageCallGraph represents the intra-package edges of the global call graph.
+// The zeroth node indicates "all external functions".
+type packageCallGraph struct {
+       nodeIndex map[*ssa.Function]int // maps func to node index (a small int)
+       nodes     []*pcgNode            // maps node index to node
+}
+
+// sortNodes populates pcg.nodes in name order and updates the nodeIndex.
+func (pcg *packageCallGraph) sortNodes() {
+       nodes := make([]*pcgNode, 0, len(pcg.nodeIndex))
+       nodes = append(nodes, &pcgNode{fn: nil, pretty: "<external>"})
+       for fn := range pcg.nodeIndex {
+               nodes = append(nodes, &pcgNode{
+                       fn:     fn,
+                       pretty: prettyFunc(fn.Pkg.Pkg, fn),
+               })
+       }
+       sort.Sort(pcgNodesByPretty(nodes[1:]))
+       for i, n := range nodes {
+               pcg.nodeIndex[n.fn] = i
+       }
+       pcg.nodes = nodes
+}
+
+func (pcg *packageCallGraph) addEdge(caller, callee *ssa.Function) {
+       var callerIndex int
+       if caller.Pkg == callee.Pkg {
+               // intra-package edge
+               callerIndex = pcg.nodeIndex[caller]
+               if callerIndex < 1 {
+                       panic(caller)
+               }
+       }
+       edges := &pcg.nodes[callerIndex].edges
+       edges.SetBit(edges, pcg.nodeIndex[callee], 1)
+}
+
+func (a *analysis) pcgAddNode(fn *ssa.Function) {
+       if fn.Pkg == nil {
+               return
+       }
+       pcg, ok := a.pcgs[fn.Pkg]
+       if !ok {
+               pcg = &packageCallGraph{nodeIndex: make(map[*ssa.Function]int)}
+               a.pcgs[fn.Pkg] = pcg
+       }
+       pcg.nodeIndex[fn] = -1
+}
+
+func (a *analysis) pcgAddEdge(caller, callee *ssa.Function) {
+       if callee.Pkg != nil {
+               a.pcgs[callee.Pkg].addEdge(caller, callee)
+       }
+}
+
+// pcgJSON returns a new slice of callgraph JSON values.
+func (a *analysis) pcgJSON(pcg *packageCallGraph) []*PCGNodeJSON {
+       var nodes []*PCGNodeJSON
+       for _, n := range pcg.nodes {
+
+               // TODO(adonovan): why is there no good way to iterate
+               // over the set bits of a big.Int?
+               var callees []int
+               nbits := n.edges.BitLen()
+               for j := 0; j < nbits; j++ {
+                       if n.edges.Bit(j) == 1 {
+                               callees = append(callees, j)
+                       }
+               }
+
+               var pos token.Pos
+               if n.fn != nil {
+                       pos = funcToken(n.fn)
+               }
+               nodes = append(nodes, &PCGNodeJSON{
+                       Func: anchorJSON{
+                               Text: n.pretty,
+                               Href: a.posURL(pos, len("func")),
+                       },
+                       Callees: callees,
+               })
+       }
+       return nodes
+}
+
+type pcgNodesByPretty []*pcgNode
+
+func (a pcgNodesByPretty) Less(i, j int) bool { return a[i].pretty < a[j].pretty }
+func (a pcgNodesByPretty) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
+func (a pcgNodesByPretty) Len() int           { return len(a) }