go/types: typecheck loop body even if range clause is broken
Be more tolerant in the presence of incorrect range clauses if for loops and type-check loop body with minimal assumptions about iteration variables. (Before, in some cases we would simply ignore the loop body in such cases). Fixes #10148. Change-Id: I0b66f81875348088c1a7fa04ccdcbfe768f2eb6c Reviewed-on: https://go-review.googlesource.com/7525 Reviewed-by: Alan Donovan <adonovan@google.com>
This commit is contained in:
parent
868c429971
commit
e4a1c78f0f
100
go/types/stmt.go
100
go/types/stmt.go
|
@ -611,60 +611,49 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
|||
defer check.closeScope()
|
||||
|
||||
// check expression to iterate over
|
||||
decl := s.Tok == token.DEFINE
|
||||
var x operand
|
||||
check.expr(&x, s.X)
|
||||
if x.mode == invalid {
|
||||
// if we don't have a declaration, we can still check the loop's body
|
||||
// (otherwise we can't because we are missing the declared variables)
|
||||
if !decl {
|
||||
check.stmt(inner, s.Body)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// determine key/value types
|
||||
var key, val Type
|
||||
switch typ := x.typ.Underlying().(type) {
|
||||
case *Basic:
|
||||
if isString(typ) {
|
||||
key = Typ[Int]
|
||||
val = UniverseRune // use 'rune' name
|
||||
}
|
||||
case *Array:
|
||||
key = Typ[Int]
|
||||
val = typ.elem
|
||||
case *Slice:
|
||||
key = Typ[Int]
|
||||
val = typ.elem
|
||||
case *Pointer:
|
||||
if typ, _ := typ.base.Underlying().(*Array); typ != nil {
|
||||
if x.mode != invalid {
|
||||
switch typ := x.typ.Underlying().(type) {
|
||||
case *Basic:
|
||||
if isString(typ) {
|
||||
key = Typ[Int]
|
||||
val = UniverseRune // use 'rune' name
|
||||
}
|
||||
case *Array:
|
||||
key = Typ[Int]
|
||||
val = typ.elem
|
||||
}
|
||||
case *Map:
|
||||
key = typ.key
|
||||
val = typ.elem
|
||||
case *Chan:
|
||||
key = typ.elem
|
||||
val = Typ[Invalid]
|
||||
if typ.dir == SendOnly {
|
||||
check.errorf(x.pos(), "cannot range over send-only channel %s", &x)
|
||||
// ok to continue
|
||||
}
|
||||
if s.Value != nil {
|
||||
check.errorf(s.Value.Pos(), "iteration over %s permits only one iteration variable", &x)
|
||||
// ok to continue
|
||||
case *Slice:
|
||||
key = Typ[Int]
|
||||
val = typ.elem
|
||||
case *Pointer:
|
||||
if typ, _ := typ.base.Underlying().(*Array); typ != nil {
|
||||
key = Typ[Int]
|
||||
val = typ.elem
|
||||
}
|
||||
case *Map:
|
||||
key = typ.key
|
||||
val = typ.elem
|
||||
case *Chan:
|
||||
key = typ.elem
|
||||
val = Typ[Invalid]
|
||||
if typ.dir == SendOnly {
|
||||
check.errorf(x.pos(), "cannot range over send-only channel %s", &x)
|
||||
// ok to continue
|
||||
}
|
||||
if s.Value != nil {
|
||||
check.errorf(s.Value.Pos(), "iteration over %s permits only one iteration variable", &x)
|
||||
// ok to continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if key == nil {
|
||||
check.errorf(x.pos(), "cannot range over %s", &x)
|
||||
// if we don't have a declaration, we can still check the loop's body
|
||||
if !decl {
|
||||
check.stmt(inner, s.Body)
|
||||
}
|
||||
return
|
||||
// ok to continue
|
||||
}
|
||||
|
||||
// check assignment to/declaration of iteration variables
|
||||
|
@ -672,9 +661,9 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
|||
|
||||
// lhs expressions and initialization value (rhs) types
|
||||
lhs := [2]ast.Expr{s.Key, s.Value}
|
||||
rhs := [2]Type{key, val}
|
||||
rhs := [2]Type{key, val} // key, val may be nil
|
||||
|
||||
if decl {
|
||||
if s.Tok == token.DEFINE {
|
||||
// short variable declaration; variable scope starts after the range clause
|
||||
// (the for loop opens a new scope, so variables on the lhs never redeclare
|
||||
// previously declared variables)
|
||||
|
@ -701,10 +690,15 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
|||
}
|
||||
|
||||
// initialize lhs variable
|
||||
x.mode = value
|
||||
x.expr = lhs // we don't have a better rhs expression to use here
|
||||
x.typ = rhs[i]
|
||||
check.initVar(obj, &x, false)
|
||||
if typ := rhs[i]; typ != nil {
|
||||
x.mode = value
|
||||
x.expr = lhs // we don't have a better rhs expression to use here
|
||||
x.typ = typ
|
||||
check.initVar(obj, &x, false)
|
||||
} else {
|
||||
obj.typ = Typ[Invalid]
|
||||
obj.used = true // don't complain about unused variable
|
||||
}
|
||||
}
|
||||
|
||||
// declare variables
|
||||
|
@ -721,10 +715,12 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
|||
if lhs == nil {
|
||||
continue
|
||||
}
|
||||
x.mode = value
|
||||
x.expr = lhs // we don't have a better rhs expression to use here
|
||||
x.typ = rhs[i]
|
||||
check.assignVar(lhs, &x)
|
||||
if typ := rhs[i]; typ != nil {
|
||||
x.mode = value
|
||||
x.expr = lhs // we don't have a better rhs expression to use here
|
||||
x.typ = typ
|
||||
check.assignVar(lhs, &x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -785,6 +785,21 @@ func issue6766b() {
|
|||
for a, a /* ERROR redeclared */ := range []int{1, 2, 3} { _ = a }
|
||||
}
|
||||
|
||||
// Test that despite errors in the range clause,
|
||||
// the loop body is still type-checked (and thus
|
||||
// errors reported).
|
||||
func issue10148() {
|
||||
for y /* ERROR declared but not used */ := range "" {
|
||||
_ = "" /* ERROR cannot convert */ + 1
|
||||
}
|
||||
for range 1 /* ERROR cannot range over 1 */ {
|
||||
_ = "" /* ERROR cannot convert */ + 1
|
||||
}
|
||||
for y := range 1 /* ERROR cannot range over 1 */ {
|
||||
_ = "" /* ERROR cannot convert */ + 1
|
||||
}
|
||||
}
|
||||
|
||||
func labels0() {
|
||||
goto L0
|
||||
goto L1
|
||||
|
|
Loading…
Reference in New Issue