.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.1.0 / godoc / analysis / peers.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.0/godoc/analysis/peers.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.0/godoc/analysis/peers.go
new file mode 100644 (file)
index 0000000..a742f06
--- /dev/null
@@ -0,0 +1,154 @@
+// 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 channel "peers" relation over all pairs of
+// channel operations in the program.  The peers are displayed in the
+// lower pane when a channel operation (make, <-, close) is clicked.
+
+// TODO(adonovan): handle calls to reflect.{Select,Recv,Send,Close} too,
+// then enable reflection in PTA.
+
+import (
+       "fmt"
+       "go/token"
+       "go/types"
+
+       "golang.org/x/tools/go/pointer"
+       "golang.org/x/tools/go/ssa"
+)
+
+func (a *analysis) doChannelPeers(ptsets map[ssa.Value]pointer.Pointer) {
+       addSendRecv := func(j *commJSON, op chanOp) {
+               j.Ops = append(j.Ops, commOpJSON{
+                       Op: anchorJSON{
+                               Text: op.mode,
+                               Href: a.posURL(op.pos, op.len),
+                       },
+                       Fn: prettyFunc(nil, op.fn),
+               })
+       }
+
+       // Build an undirected bipartite multigraph (binary relation)
+       // of MakeChan ops and send/recv/close ops.
+       //
+       // TODO(adonovan): opt: use channel element types to partition
+       // the O(n^2) problem into subproblems.
+       aliasedOps := make(map[*ssa.MakeChan][]chanOp)
+       opToMakes := make(map[chanOp][]*ssa.MakeChan)
+       for _, op := range a.ops {
+               // Combine the PT sets from all contexts.
+               var makes []*ssa.MakeChan // aliased ops
+               ptr, ok := ptsets[op.ch]
+               if !ok {
+                       continue // e.g. channel op in dead code
+               }
+               for _, label := range ptr.PointsTo().Labels() {
+                       makechan, ok := label.Value().(*ssa.MakeChan)
+                       if !ok {
+                               continue // skip intrinsically-created channels for now
+                       }
+                       if makechan.Pos() == token.NoPos {
+                               continue // not possible?
+                       }
+                       makes = append(makes, makechan)
+                       aliasedOps[makechan] = append(aliasedOps[makechan], op)
+               }
+               opToMakes[op] = makes
+       }
+
+       // Now that complete relation is built, build links for ops.
+       for _, op := range a.ops {
+               v := commJSON{
+                       Ops: []commOpJSON{}, // (JS wants non-nil)
+               }
+               ops := make(map[chanOp]bool)
+               for _, makechan := range opToMakes[op] {
+                       v.Ops = append(v.Ops, commOpJSON{
+                               Op: anchorJSON{
+                                       Text: "made",
+                                       Href: a.posURL(makechan.Pos()-token.Pos(len("make")),
+                                               len("make")),
+                               },
+                               Fn: makechan.Parent().RelString(op.fn.Package().Pkg),
+                       })
+                       for _, op := range aliasedOps[makechan] {
+                               ops[op] = true
+                       }
+               }
+               for op := range ops {
+                       addSendRecv(&v, op)
+               }
+
+               // Add links for each aliased op.
+               fi, offset := a.fileAndOffset(op.pos)
+               fi.addLink(aLink{
+                       start:   offset,
+                       end:     offset + op.len,
+                       title:   "show channel ops",
+                       onclick: fmt.Sprintf("onClickComm(%d)", fi.addData(v)),
+               })
+       }
+       // Add links for makechan ops themselves.
+       for makechan, ops := range aliasedOps {
+               v := commJSON{
+                       Ops: []commOpJSON{}, // (JS wants non-nil)
+               }
+               for _, op := range ops {
+                       addSendRecv(&v, op)
+               }
+
+               fi, offset := a.fileAndOffset(makechan.Pos())
+               fi.addLink(aLink{
+                       start:   offset - len("make"),
+                       end:     offset,
+                       title:   "show channel ops",
+                       onclick: fmt.Sprintf("onClickComm(%d)", fi.addData(v)),
+               })
+       }
+}
+
+// -- utilities --------------------------------------------------------
+
+// chanOp abstracts an ssa.Send, ssa.Unop(ARROW), close(), or a SelectState.
+// Derived from cmd/guru/peers.go.
+type chanOp struct {
+       ch   ssa.Value
+       mode string // sent|received|closed
+       pos  token.Pos
+       len  int
+       fn   *ssa.Function
+}
+
+// chanOps returns a slice of all the channel operations in the instruction.
+// Derived from cmd/guru/peers.go.
+func chanOps(instr ssa.Instruction) []chanOp {
+       fn := instr.Parent()
+       var ops []chanOp
+       switch instr := instr.(type) {
+       case *ssa.UnOp:
+               if instr.Op == token.ARROW {
+                       // TODO(adonovan): don't assume <-ch; could be 'range ch'.
+                       ops = append(ops, chanOp{instr.X, "received", instr.Pos(), len("<-"), fn})
+               }
+       case *ssa.Send:
+               ops = append(ops, chanOp{instr.Chan, "sent", instr.Pos(), len("<-"), fn})
+       case *ssa.Select:
+               for _, st := range instr.States {
+                       mode := "received"
+                       if st.Dir == types.SendOnly {
+                               mode = "sent"
+                       }
+                       ops = append(ops, chanOp{st.Chan, mode, st.Pos, len("<-"), fn})
+               }
+       case ssa.CallInstruction:
+               call := instr.Common()
+               if blt, ok := call.Value.(*ssa.Builtin); ok && blt.Name() == "close" {
+                       pos := instr.Common().Pos()
+                       ops = append(ops, chanOp{call.Args[0], "closed", pos - token.Pos(len("close")), len("close("), fn})
+               }
+       }
+       return ops
+}