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 / peers.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/ast"
10         "go/token"
11         "go/types"
12         "sort"
13
14         "golang.org/x/tools/cmd/guru/serial"
15         "golang.org/x/tools/go/loader"
16         "golang.org/x/tools/go/ssa"
17         "golang.org/x/tools/go/ssa/ssautil"
18 )
19
20 // peers enumerates, for a given channel send (or receive) operation,
21 // the set of possible receives (or sends) that correspond to it.
22 //
23 // TODO(adonovan): support reflect.{Select,Recv,Send,Close}.
24 // TODO(adonovan): permit the user to query based on a MakeChan (not send/recv),
25 // or the implicit receive in "for v := range ch".
26 func peers(q *Query) error {
27         lconf := loader.Config{Build: q.Build}
28
29         if err := setPTAScope(&lconf, q.Scope); err != nil {
30                 return err
31         }
32
33         // Load/parse/type-check the program.
34         lprog, err := loadWithSoftErrors(&lconf)
35         if err != nil {
36                 return err
37         }
38
39         qpos, err := parseQueryPos(lprog, q.Pos, false)
40         if err != nil {
41                 return err
42         }
43
44         prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug)
45
46         ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
47         if err != nil {
48                 return err
49         }
50
51         opPos := findOp(qpos)
52         if opPos == token.NoPos {
53                 return fmt.Errorf("there is no channel operation here")
54         }
55
56         // Defer SSA construction till after errors are reported.
57         prog.Build()
58
59         var queryOp chanOp // the originating send or receive operation
60         var ops []chanOp   // all sends/receives of opposite direction
61
62         // Look at all channel operations in the whole ssa.Program.
63         // Build a list of those of same type as the query.
64         allFuncs := ssautil.AllFunctions(prog)
65         for fn := range allFuncs {
66                 for _, b := range fn.Blocks {
67                         for _, instr := range b.Instrs {
68                                 for _, op := range chanOps(instr) {
69                                         ops = append(ops, op)
70                                         if op.pos == opPos {
71                                                 queryOp = op // we found the query op
72                                         }
73                                 }
74                         }
75                 }
76         }
77         if queryOp.ch == nil {
78                 return fmt.Errorf("ssa.Instruction for send/receive not found")
79         }
80
81         // Discard operations of wrong channel element type.
82         // Build set of channel ssa.Values as query to pointer analysis.
83         // We compare channels by element types, not channel types, to
84         // ignore both directionality and type names.
85         queryType := queryOp.ch.Type()
86         queryElemType := queryType.Underlying().(*types.Chan).Elem()
87         ptaConfig.AddQuery(queryOp.ch)
88         i := 0
89         for _, op := range ops {
90                 if types.Identical(op.ch.Type().Underlying().(*types.Chan).Elem(), queryElemType) {
91                         ptaConfig.AddQuery(op.ch)
92                         ops[i] = op
93                         i++
94                 }
95         }
96         ops = ops[:i]
97
98         // Run the pointer analysis.
99         ptares := ptrAnalysis(ptaConfig)
100
101         // Find the points-to set.
102         queryChanPtr := ptares.Queries[queryOp.ch]
103
104         // Ascertain which make(chan) labels the query's channel can alias.
105         var makes []token.Pos
106         for _, label := range queryChanPtr.PointsTo().Labels() {
107                 makes = append(makes, label.Pos())
108         }
109         sort.Sort(byPos(makes))
110
111         // Ascertain which channel operations can alias the same make(chan) labels.
112         var sends, receives, closes []token.Pos
113         for _, op := range ops {
114                 if ptr, ok := ptares.Queries[op.ch]; ok && ptr.MayAlias(queryChanPtr) {
115                         switch op.dir {
116                         case types.SendOnly:
117                                 sends = append(sends, op.pos)
118                         case types.RecvOnly:
119                                 receives = append(receives, op.pos)
120                         case types.SendRecv:
121                                 closes = append(closes, op.pos)
122                         }
123                 }
124         }
125         sort.Sort(byPos(sends))
126         sort.Sort(byPos(receives))
127         sort.Sort(byPos(closes))
128
129         q.Output(lprog.Fset, &peersResult{
130                 queryPos:  opPos,
131                 queryType: queryType,
132                 makes:     makes,
133                 sends:     sends,
134                 receives:  receives,
135                 closes:    closes,
136         })
137         return nil
138 }
139
140 // findOp returns the position of the enclosing send/receive/close op.
141 // For send and receive operations, this is the position of the <- token;
142 // for close operations, it's the Lparen of the function call.
143 //
144 // TODO(adonovan): handle implicit receive operations from 'for...range chan' statements.
145 func findOp(qpos *queryPos) token.Pos {
146         for _, n := range qpos.path {
147                 switch n := n.(type) {
148                 case *ast.UnaryExpr:
149                         if n.Op == token.ARROW {
150                                 return n.OpPos
151                         }
152                 case *ast.SendStmt:
153                         return n.Arrow
154                 case *ast.CallExpr:
155                         // close function call can only exist as a direct identifier
156                         if close, ok := unparen(n.Fun).(*ast.Ident); ok {
157                                 if b, ok := qpos.info.Info.Uses[close].(*types.Builtin); ok && b.Name() == "close" {
158                                         return n.Lparen
159                                 }
160                         }
161                 }
162         }
163         return token.NoPos
164 }
165
166 // chanOp abstracts an ssa.Send, ssa.Unop(ARROW), or a SelectState.
167 type chanOp struct {
168         ch  ssa.Value
169         dir types.ChanDir // SendOnly=send, RecvOnly=recv, SendRecv=close
170         pos token.Pos
171 }
172
173 // chanOps returns a slice of all the channel operations in the instruction.
174 func chanOps(instr ssa.Instruction) []chanOp {
175         // TODO(adonovan): handle calls to reflect.{Select,Recv,Send,Close} too.
176         var ops []chanOp
177         switch instr := instr.(type) {
178         case *ssa.UnOp:
179                 if instr.Op == token.ARROW {
180                         ops = append(ops, chanOp{instr.X, types.RecvOnly, instr.Pos()})
181                 }
182         case *ssa.Send:
183                 ops = append(ops, chanOp{instr.Chan, types.SendOnly, instr.Pos()})
184         case *ssa.Select:
185                 for _, st := range instr.States {
186                         ops = append(ops, chanOp{st.Chan, st.Dir, st.Pos})
187                 }
188         case ssa.CallInstruction:
189                 cc := instr.Common()
190                 if b, ok := cc.Value.(*ssa.Builtin); ok && b.Name() == "close" {
191                         ops = append(ops, chanOp{cc.Args[0], types.SendRecv, cc.Pos()})
192                 }
193         }
194         return ops
195 }
196
197 // TODO(adonovan): show the line of text for each pos, like "referrers" does.
198 type peersResult struct {
199         queryPos                       token.Pos   // of queried channel op
200         queryType                      types.Type  // type of queried channel
201         makes, sends, receives, closes []token.Pos // positions of aliased makechan/send/receive/close instrs
202 }
203
204 func (r *peersResult) PrintPlain(printf printfFunc) {
205         if len(r.makes) == 0 {
206                 printf(r.queryPos, "This channel can't point to anything.")
207                 return
208         }
209         printf(r.queryPos, "This channel of type %s may be:", r.queryType)
210         for _, alloc := range r.makes {
211                 printf(alloc, "\tallocated here")
212         }
213         for _, send := range r.sends {
214                 printf(send, "\tsent to, here")
215         }
216         for _, receive := range r.receives {
217                 printf(receive, "\treceived from, here")
218         }
219         for _, clos := range r.closes {
220                 printf(clos, "\tclosed, here")
221         }
222 }
223
224 func (r *peersResult) JSON(fset *token.FileSet) []byte {
225         peers := &serial.Peers{
226                 Pos:  fset.Position(r.queryPos).String(),
227                 Type: r.queryType.String(),
228         }
229         for _, alloc := range r.makes {
230                 peers.Allocs = append(peers.Allocs, fset.Position(alloc).String())
231         }
232         for _, send := range r.sends {
233                 peers.Sends = append(peers.Sends, fset.Position(send).String())
234         }
235         for _, receive := range r.receives {
236                 peers.Receives = append(peers.Receives, fset.Position(receive).String())
237         }
238         for _, clos := range r.closes {
239                 peers.Closes = append(peers.Closes, fset.Position(clos).String())
240         }
241         return toJSON(peers)
242 }
243
244 // -------- utils --------
245
246 // NB: byPos is not deterministic across packages since it depends on load order.
247 // Use lessPos if the tests need it.
248 type byPos []token.Pos
249
250 func (p byPos) Len() int           { return len(p) }
251 func (p byPos) Less(i, j int) bool { return p[i] < p[j] }
252 func (p byPos) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }