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