aboutsummaryrefslogtreecommitdiff
path: root/oracle
diff options
context:
space:
mode:
authorDaniel Morsing <daniel.morsing@gmail.com>2014-10-12 15:08:28 +0100
committerDaniel Morsing <daniel.morsing@gmail.com>2014-10-12 15:08:28 +0100
commit70f0e2472d3be5eb3834f4ede6749ab746b87ea4 (patch)
tree34cb880e01148f5b616bfd9b43a98439568725a3 /oracle
parentcced8d21ddff0072338da11d7de4f37958662dba (diff)
downloadtools-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/TODO2
-rw-r--r--oracle/peers.go63
-rw-r--r--oracle/serial/serial.go1
-rw-r--r--oracle/testdata/src/main/peers.go12
-rw-r--r--oracle/testdata/src/main/peers.golden37
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