mirror of
https://github.com/golang/go
synced 2024-11-19 02:54:42 -07:00
go.tools/go/types: missing checks for select statements
Also: Use defer to make sure scopes are always closed even in case of early exits via bailout (and don't cause an imbalanced scope error in debug mode). R=adonovan, gri CC=golang-dev https://golang.org/cl/29300043
This commit is contained in:
parent
849643aaaf
commit
b4286c4c1b
@ -311,11 +311,14 @@ func (check *checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
|||||||
|
|
||||||
case *ast.BlockStmt:
|
case *ast.BlockStmt:
|
||||||
check.openScope(s)
|
check.openScope(s)
|
||||||
|
defer check.closeScope()
|
||||||
|
|
||||||
check.stmtList(inner, s.List)
|
check.stmtList(inner, s.List)
|
||||||
check.closeScope()
|
|
||||||
|
|
||||||
case *ast.IfStmt:
|
case *ast.IfStmt:
|
||||||
check.openScope(s)
|
check.openScope(s)
|
||||||
|
defer check.closeScope()
|
||||||
|
|
||||||
check.initStmt(s.Init)
|
check.initStmt(s.Init)
|
||||||
var x operand
|
var x operand
|
||||||
check.expr(&x, s.Cond)
|
check.expr(&x, s.Cond)
|
||||||
@ -326,11 +329,12 @@ func (check *checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
|||||||
if s.Else != nil {
|
if s.Else != nil {
|
||||||
check.stmt(inner, s.Else)
|
check.stmt(inner, s.Else)
|
||||||
}
|
}
|
||||||
check.closeScope()
|
|
||||||
|
|
||||||
case *ast.SwitchStmt:
|
case *ast.SwitchStmt:
|
||||||
inner |= inBreakable
|
inner |= inBreakable
|
||||||
check.openScope(s)
|
check.openScope(s)
|
||||||
|
defer check.closeScope()
|
||||||
|
|
||||||
check.initStmt(s.Init)
|
check.initStmt(s.Init)
|
||||||
var x operand
|
var x operand
|
||||||
tag := s.Tag
|
tag := s.Tag
|
||||||
@ -361,12 +365,12 @@ func (check *checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
|||||||
check.stmtList(inner, clause.Body)
|
check.stmtList(inner, clause.Body)
|
||||||
check.closeScope()
|
check.closeScope()
|
||||||
}
|
}
|
||||||
check.closeScope()
|
|
||||||
|
|
||||||
case *ast.TypeSwitchStmt:
|
case *ast.TypeSwitchStmt:
|
||||||
inner |= inBreakable
|
inner |= inBreakable
|
||||||
check.openScope(s)
|
check.openScope(s)
|
||||||
defer check.closeScope()
|
defer check.closeScope()
|
||||||
|
|
||||||
check.initStmt(s.Init)
|
check.initStmt(s.Init)
|
||||||
|
|
||||||
// A type switch guard must be of the form:
|
// A type switch guard must be of the form:
|
||||||
@ -473,17 +477,46 @@ func (check *checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
|||||||
if clause == nil {
|
if clause == nil {
|
||||||
continue // error reported before
|
continue // error reported before
|
||||||
}
|
}
|
||||||
check.openScope(clause)
|
|
||||||
if s := clause.Comm; s != nil {
|
// clause.Comm must be a SendStmt, RecvStmt, or default case
|
||||||
check.stmt(inner, s) // TODO(gri) check correctness of c.Comm (must be Send/RecvStmt)
|
valid := false
|
||||||
|
var rhs ast.Expr // rhs of RecvStmt, or nil
|
||||||
|
switch s := clause.Comm.(type) {
|
||||||
|
case nil, *ast.SendStmt:
|
||||||
|
valid = true
|
||||||
|
case *ast.AssignStmt:
|
||||||
|
if len(s.Rhs) == 1 {
|
||||||
|
rhs = s.Rhs[0]
|
||||||
|
}
|
||||||
|
case *ast.ExprStmt:
|
||||||
|
rhs = s.X
|
||||||
|
}
|
||||||
|
|
||||||
|
// if present, rhs must be a receive operation
|
||||||
|
if rhs != nil {
|
||||||
|
if x, _ := unparen(rhs).(*ast.UnaryExpr); x != nil && x.Op == token.ARROW {
|
||||||
|
valid = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !valid {
|
||||||
|
check.errorf(clause.Comm.Pos(), "select case must be send or receive (possibly with assignment)")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
check.openScope(s)
|
||||||
|
defer check.closeScope()
|
||||||
|
if clause.Comm != nil {
|
||||||
|
check.stmt(inner, clause.Comm)
|
||||||
}
|
}
|
||||||
check.stmtList(inner, clause.Body)
|
check.stmtList(inner, clause.Body)
|
||||||
check.closeScope()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case *ast.ForStmt:
|
case *ast.ForStmt:
|
||||||
inner |= inBreakable | inContinuable
|
inner |= inBreakable | inContinuable
|
||||||
check.openScope(s)
|
check.openScope(s)
|
||||||
|
defer check.closeScope()
|
||||||
|
|
||||||
check.initStmt(s.Init)
|
check.initStmt(s.Init)
|
||||||
if s.Cond != nil {
|
if s.Cond != nil {
|
||||||
var x operand
|
var x operand
|
||||||
@ -494,7 +527,6 @@ func (check *checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
|||||||
}
|
}
|
||||||
check.initStmt(s.Post)
|
check.initStmt(s.Post)
|
||||||
check.stmt(inner, s.Body)
|
check.stmt(inner, s.Body)
|
||||||
check.closeScope()
|
|
||||||
|
|
||||||
case *ast.RangeStmt:
|
case *ast.RangeStmt:
|
||||||
inner |= inBreakable | inContinuable
|
inner |= inBreakable | inContinuable
|
||||||
|
18
go/types/testdata/stmt0.src
vendored
18
go/types/testdata/stmt0.src
vendored
@ -186,20 +186,30 @@ func selects() {
|
|||||||
var (
|
var (
|
||||||
ch chan int
|
ch chan int
|
||||||
sc chan <- bool
|
sc chan <- bool
|
||||||
x int
|
|
||||||
)
|
)
|
||||||
select {
|
select {
|
||||||
case <-ch:
|
case <-ch:
|
||||||
ch <- x
|
case (<-ch):
|
||||||
|
case t := <-ch:
|
||||||
|
_ = t
|
||||||
|
case t := (<-ch):
|
||||||
|
_ = t
|
||||||
case t, ok := <-ch:
|
case t, ok := <-ch:
|
||||||
x = t
|
_, _ = t, ok
|
||||||
_ = ok
|
case t, ok := (<-ch):
|
||||||
|
_, _ = t, ok
|
||||||
case <-sc /* ERROR "cannot receive from send-only channel" */ :
|
case <-sc /* ERROR "cannot receive from send-only channel" */ :
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
default:
|
default:
|
||||||
default /* ERROR "multiple defaults" */ :
|
default /* ERROR "multiple defaults" */ :
|
||||||
}
|
}
|
||||||
|
select {
|
||||||
|
case a, b := <-ch:
|
||||||
|
_, b = a, b
|
||||||
|
case x /* ERROR send or receive */ :
|
||||||
|
case a /* ERROR send or receive */ := ch:
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func gos() {
|
func gos() {
|
||||||
|
Loading…
Reference in New Issue
Block a user