Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / godoc / analysis / peers.go
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
5 package analysis
6
7 // This file computes the channel "peers" relation over all pairs of
8 // channel operations in the program.  The peers are displayed in the
9 // lower pane when a channel operation (make, <-, close) is clicked.
10
11 // TODO(adonovan): handle calls to reflect.{Select,Recv,Send,Close} too,
12 // then enable reflection in PTA.
13
14 import (
15         "fmt"
16         "go/token"
17         "go/types"
18
19         "golang.org/x/tools/go/pointer"
20         "golang.org/x/tools/go/ssa"
21 )
22
23 func (a *analysis) doChannelPeers(ptsets map[ssa.Value]pointer.Pointer) {
24         addSendRecv := func(j *commJSON, op chanOp) {
25                 j.Ops = append(j.Ops, commOpJSON{
26                         Op: anchorJSON{
27                                 Text: op.mode,
28                                 Href: a.posURL(op.pos, op.len),
29                         },
30                         Fn: prettyFunc(nil, op.fn),
31                 })
32         }
33
34         // Build an undirected bipartite multigraph (binary relation)
35         // of MakeChan ops and send/recv/close ops.
36         //
37         // TODO(adonovan): opt: use channel element types to partition
38         // the O(n^2) problem into subproblems.
39         aliasedOps := make(map[*ssa.MakeChan][]chanOp)
40         opToMakes := make(map[chanOp][]*ssa.MakeChan)
41         for _, op := range a.ops {
42                 // Combine the PT sets from all contexts.
43                 var makes []*ssa.MakeChan // aliased ops
44                 ptr, ok := ptsets[op.ch]
45                 if !ok {
46                         continue // e.g. channel op in dead code
47                 }
48                 for _, label := range ptr.PointsTo().Labels() {
49                         makechan, ok := label.Value().(*ssa.MakeChan)
50                         if !ok {
51                                 continue // skip intrinsically-created channels for now
52                         }
53                         if makechan.Pos() == token.NoPos {
54                                 continue // not possible?
55                         }
56                         makes = append(makes, makechan)
57                         aliasedOps[makechan] = append(aliasedOps[makechan], op)
58                 }
59                 opToMakes[op] = makes
60         }
61
62         // Now that complete relation is built, build links for ops.
63         for _, op := range a.ops {
64                 v := commJSON{
65                         Ops: []commOpJSON{}, // (JS wants non-nil)
66                 }
67                 ops := make(map[chanOp]bool)
68                 for _, makechan := range opToMakes[op] {
69                         v.Ops = append(v.Ops, commOpJSON{
70                                 Op: anchorJSON{
71                                         Text: "made",
72                                         Href: a.posURL(makechan.Pos()-token.Pos(len("make")),
73                                                 len("make")),
74                                 },
75                                 Fn: makechan.Parent().RelString(op.fn.Package().Pkg),
76                         })
77                         for _, op := range aliasedOps[makechan] {
78                                 ops[op] = true
79                         }
80                 }
81                 for op := range ops {
82                         addSendRecv(&v, op)
83                 }
84
85                 // Add links for each aliased op.
86                 fi, offset := a.fileAndOffset(op.pos)
87                 fi.addLink(aLink{
88                         start:   offset,
89                         end:     offset + op.len,
90                         title:   "show channel ops",
91                         onclick: fmt.Sprintf("onClickComm(%d)", fi.addData(v)),
92                 })
93         }
94         // Add links for makechan ops themselves.
95         for makechan, ops := range aliasedOps {
96                 v := commJSON{
97                         Ops: []commOpJSON{}, // (JS wants non-nil)
98                 }
99                 for _, op := range ops {
100                         addSendRecv(&v, op)
101                 }
102
103                 fi, offset := a.fileAndOffset(makechan.Pos())
104                 fi.addLink(aLink{
105                         start:   offset - len("make"),
106                         end:     offset,
107                         title:   "show channel ops",
108                         onclick: fmt.Sprintf("onClickComm(%d)", fi.addData(v)),
109                 })
110         }
111 }
112
113 // -- utilities --------------------------------------------------------
114
115 // chanOp abstracts an ssa.Send, ssa.Unop(ARROW), close(), or a SelectState.
116 // Derived from cmd/guru/peers.go.
117 type chanOp struct {
118         ch   ssa.Value
119         mode string // sent|received|closed
120         pos  token.Pos
121         len  int
122         fn   *ssa.Function
123 }
124
125 // chanOps returns a slice of all the channel operations in the instruction.
126 // Derived from cmd/guru/peers.go.
127 func chanOps(instr ssa.Instruction) []chanOp {
128         fn := instr.Parent()
129         var ops []chanOp
130         switch instr := instr.(type) {
131         case *ssa.UnOp:
132                 if instr.Op == token.ARROW {
133                         // TODO(adonovan): don't assume <-ch; could be 'range ch'.
134                         ops = append(ops, chanOp{instr.X, "received", instr.Pos(), len("<-"), fn})
135                 }
136         case *ssa.Send:
137                 ops = append(ops, chanOp{instr.Chan, "sent", instr.Pos(), len("<-"), fn})
138         case *ssa.Select:
139                 for _, st := range instr.States {
140                         mode := "received"
141                         if st.Dir == types.SendOnly {
142                                 mode = "sent"
143                         }
144                         ops = append(ops, chanOp{st.Chan, mode, st.Pos, len("<-"), fn})
145                 }
146         case ssa.CallInstruction:
147                 call := instr.Common()
148                 if blt, ok := call.Value.(*ssa.Builtin); ok && blt.Name() == "close" {
149                         pos := instr.Common().Pos()
150                         ops = append(ops, chanOp{call.Args[0], "closed", pos - token.Pos(len("close")), len("close("), fn})
151                 }
152         }
153         return ops
154 }