diff options
author | Daniel Morsing <daniel.morsing@gmail.com> | 2014-10-12 15:08:28 +0100 |
---|---|---|
committer | Daniel Morsing <daniel.morsing@gmail.com> | 2014-10-12 15:08:28 +0100 |
commit | 70f0e2472d3be5eb3834f4ede6749ab746b87ea4 (patch) | |
tree | 34cb880e01148f5b616bfd9b43a98439568725a3 /oracle | |
parent | cced8d21ddff0072338da11d7de4f37958662dba (diff) | |
download | tools-70f0e2472d3be5eb3834f4ede6749ab746b87ea4.tar.gz |
go.tools/oracle: consider calls to close a peer operation.
This makes it possible to find corresponding closes to receives and sends.
LGTM=adonovan
R=adonovan
CC=golang-codereviews
https://golang.org/cl/155260043
Diffstat (limited to 'oracle')
-rw-r--r-- | oracle/TODO | 2 | ||||
-rw-r--r-- | oracle/peers.go | 63 | ||||
-rw-r--r-- | oracle/serial/serial.go | 1 | ||||
-rw-r--r-- | oracle/testdata/src/main/peers.go | 12 | ||||
-rw-r--r-- | oracle/testdata/src/main/peers.golden | 37 |
5 files changed, 94 insertions, 21 deletions
diff --git a/oracle/TODO b/oracle/TODO index 1373ab9..bbf0577 100644 --- a/oracle/TODO +++ b/oracle/TODO @@ -70,7 +70,7 @@ describe peers - Permit querying from a makechan, close(), for...range, or reflective op. + Permit querying from a makechan, for...range, or reflective op. Report aliasing reflect.{Send,Recv,Close} and close() operations. diff --git a/oracle/peers.go b/oracle/peers.go index 5a41073..90baf65 100644 --- a/oracle/peers.go +++ b/oracle/peers.go @@ -22,12 +22,10 @@ import ( // TODO(adonovan): support reflect.{Select,Recv,Send,Close}. // TODO(adonovan): permit the user to query based on a MakeChan (not send/recv), // or the implicit receive in "for v := range ch". -// TODO(adonovan): support "close" as a channel op. -// func peers(o *Oracle, qpos *QueryPos) (queryResult, error) { - arrowPos := findArrow(qpos) - if arrowPos == token.NoPos { - return nil, fmt.Errorf("there is no send/receive here") + opPos := findOp(qpos) + if opPos == token.NoPos { + return nil, fmt.Errorf("there is no channel operation here") } buildSSA(o) @@ -35,15 +33,15 @@ func peers(o *Oracle, qpos *QueryPos) (queryResult, error) { var queryOp chanOp // the originating send or receive operation var ops []chanOp // all sends/receives of opposite direction - // Look at all send/receive instructions in the whole ssa.Program. - // Build a list of those of same type to query. + // Look at all channel operations in the whole ssa.Program. + // Build a list of those of same type as the query. allFuncs := ssautil.AllFunctions(o.prog) for fn := range allFuncs { for _, b := range fn.Blocks { for _, instr := range b.Instrs { for _, op := range chanOps(instr) { ops = append(ops, op) - if op.pos == arrowPos { + if op.pos == opPos { queryOp = op // we found the query op } } @@ -84,33 +82,40 @@ func peers(o *Oracle, qpos *QueryPos) (queryResult, error) { } sort.Sort(byPos(makes)) - // Ascertain which send/receive operations can alias the same make(chan) labels. - var sends, receives []token.Pos + // Ascertain which channel operations can alias the same make(chan) labels. + var sends, receives, closes []token.Pos for _, op := range ops { if ptr, ok := ptares.Queries[op.ch]; ok && ptr.MayAlias(queryChanPtr) { - if op.dir == types.SendOnly { + switch op.dir { + case types.SendOnly: sends = append(sends, op.pos) - } else { + case types.RecvOnly: receives = append(receives, op.pos) + case types.SendRecv: + closes = append(closes, op.pos) } } } sort.Sort(byPos(sends)) sort.Sort(byPos(receives)) + sort.Sort(byPos(closes)) return &peersResult{ - queryPos: arrowPos, + queryPos: opPos, queryType: queryType, makes: makes, sends: sends, receives: receives, + closes: closes, }, nil } -// findArrow returns the position of the enclosing send/receive op -// (<-) for the query position, or token.NoPos if not found. +// findOp returns the position of the enclosing send/receive/close op. +// For send and receive operations, this is the position of the <- token; +// for close operations, it's the Lparen of the function call. // -func findArrow(qpos *QueryPos) token.Pos { +// TODO(adonovan): handle implicit receive operations from 'for...range chan' statements. +func findOp(qpos *QueryPos) token.Pos { for _, n := range qpos.path { switch n := n.(type) { case *ast.UnaryExpr: @@ -119,6 +124,13 @@ func findArrow(qpos *QueryPos) token.Pos { } case *ast.SendStmt: return n.Arrow + case *ast.CallExpr: + // close function call can only exist as a direct identifier + if close, ok := unparen(n.Fun).(*ast.Ident); ok { + if b, ok := qpos.info.Info.Uses[close].(*types.Builtin); ok && b.Name() == "close" { + return n.Lparen + } + } } } return token.NoPos @@ -127,7 +139,7 @@ func findArrow(qpos *QueryPos) token.Pos { // chanOp abstracts an ssa.Send, ssa.Unop(ARROW), or a SelectState. type chanOp struct { ch ssa.Value - dir types.ChanDir // SendOnly or RecvOnly + dir types.ChanDir // SendOnly=send, RecvOnly=recv, SendRecv=close pos token.Pos } @@ -146,14 +158,19 @@ func chanOps(instr ssa.Instruction) []chanOp { for _, st := range instr.States { ops = append(ops, chanOp{st.Chan, st.Dir, st.Pos}) } + case ssa.CallInstruction: + cc := instr.Common() + if b, ok := cc.Value.(*ssa.Builtin); ok && b.Name() == "close" { + ops = append(ops, chanOp{cc.Args[0], types.SendRecv, cc.Pos()}) + } } return ops } type peersResult struct { - queryPos token.Pos // of queried '<-' token - queryType types.Type // type of queried channel - makes, sends, receives []token.Pos // positions of aliased makechan/send/receive instrs + queryPos token.Pos // of queried channel op + queryType types.Type // type of queried channel + makes, sends, receives, closes []token.Pos // positions of aliased makechan/send/receive/close instrs } func (r *peersResult) display(printf printfFunc) { @@ -171,6 +188,9 @@ func (r *peersResult) display(printf printfFunc) { for _, receive := range r.receives { printf(receive, "\treceived from, here") } + for _, clos := range r.closes { + printf(clos, "\tclosed, here") + } } func (r *peersResult) toSerial(res *serial.Result, fset *token.FileSet) { @@ -187,6 +207,9 @@ func (r *peersResult) toSerial(res *serial.Result, fset *token.FileSet) { for _, receive := range r.receives { peers.Receives = append(peers.Receives, fset.Position(receive).String()) } + for _, clos := range r.closes { + peers.Closes = append(peers.Closes, fset.Position(clos).String()) + } res.Peers = peers } diff --git a/oracle/serial/serial.go b/oracle/serial/serial.go index 316ecff..631d9bc 100644 --- a/oracle/serial/serial.go +++ b/oracle/serial/serial.go @@ -22,6 +22,7 @@ type Peers struct { Allocs []string `json:"allocs,omitempty"` // locations of aliased make(chan) ops Sends []string `json:"sends,omitempty"` // locations of aliased ch<-x ops Receives []string `json:"receives,omitempty"` // locations of aliased <-ch ops + Closes []string `json:"closes,omitempty"` // locations of aliased close(ch) ops } // A Referrers is the result of a 'referrers' query. diff --git a/oracle/testdata/src/main/peers.go b/oracle/testdata/src/main/peers.go index 65ec907..8370f64 100644 --- a/oracle/testdata/src/main/peers.go +++ b/oracle/testdata/src/main/peers.go @@ -37,4 +37,16 @@ func main() { for _ = range chA { } + + close(chA) // @peers peer-close-chA "chA" + + chC := make(chan *int) + (close)(chC) // @peers peer-close-chC "chC" + + close := func(ch chan *int) chan *int { + return ch + } + + close(chC) <- &b // @peers peer-send-chC "chC" + <-close(chC) // @peers peer-recv-chC "chC" } diff --git a/oracle/testdata/src/main/peers.golden b/oracle/testdata/src/main/peers.golden index e6b8a65..f97e672 100644 --- a/oracle/testdata/src/main/peers.golden +++ b/oracle/testdata/src/main/peers.golden @@ -22,6 +22,7 @@ This channel of type chan *int may be: received from, here received from, here received from, here + closed, here -------- @pointsto pointsto-rA -------- this *int may point to these objects: @@ -50,6 +51,7 @@ This channel of type chan *int may be: received from, here received from, here received from, here + closed, here -------- @peers peer-send-chA' -------- This channel of type chan *int may be: @@ -60,4 +62,39 @@ This channel of type chan *int may be: received from, here received from, here received from, here + closed, here + +-------- @peers peer-close-chA -------- +This channel of type chan *int may be: + allocated here + allocated here + sent to, here + sent to, here + received from, here + received from, here + received from, here + received from, here + received from, here + closed, here + +-------- @peers peer-close-chC -------- +This channel of type chan *int may be: + allocated here + sent to, here + received from, here + closed, here + +-------- @peers peer-send-chC -------- +This channel of type chan *int may be: + allocated here + sent to, here + received from, here + closed, here + +-------- @peers peer-recv-chC -------- +This channel of type chan *int may be: + allocated here + sent to, here + received from, here + closed, here |