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