From e4a1c78f0f69fbde8bb74f5e9f4adb037a68d753 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Fri, 13 Mar 2015 14:35:38 -0700 Subject: [PATCH] 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 --- go/types/stmt.go | 100 +++++++++++++++++------------------- go/types/testdata/stmt0.src | 15 ++++++ 2 files changed, 63 insertions(+), 52 deletions(-) diff --git a/go/types/stmt.go b/go/types/stmt.go index 3917336d..3ef04556 100644 --- a/go/types/stmt.go +++ b/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) + } } } diff --git a/go/types/testdata/stmt0.src b/go/types/testdata/stmt0.src index 073e83b2..fd1ddba2 100644 --- a/go/types/testdata/stmt0.src +++ b/go/types/testdata/stmt0.src @@ -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