go.tools/go/types: fix a couple of std lib test failures

1. handle return statements with zero (but expected) return values
2. indices provided for array or slice composite literals must be integer constants

Added additional test cases.

R=adonovan
CC=golang-dev
https://golang.org/cl/13734043
This commit is contained in:
Robert Griesemer 2013-09-17 10:26:06 -07:00
parent cf1e27bbda
commit 4acd602bea
7 changed files with 71 additions and 19 deletions

View File

@ -8,6 +8,7 @@ package types
import ( import (
"go/ast" "go/ast"
"go/token"
"code.google.com/p/go.tools/go/exact" "code.google.com/p/go.tools/go/exact"
) )
@ -154,7 +155,9 @@ func (check *checker) assignVar(lhs ast.Expr, x *operand) Type {
return z.typ return z.typ
} }
func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, allowCommaOk bool) { // If returnPos is valid, initVars is called to type-check the assignment of
// return expressions, and returnPos is the position of the return statement.
func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, returnPos token.Pos) {
l := len(lhs) l := len(lhs)
r := len(rhs) r := len(rhs)
assert(l > 0) assert(l > 0)
@ -199,8 +202,8 @@ func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, allowCommaOk bool) {
} }
} }
if allowCommaOk && x.mode == valueok && l == 2 { if !returnPos.IsValid() && x.mode == valueok && l == 2 {
// comma-ok expression // comma-ok expression (not permitted with return statements)
x.mode = value x.mode = value
t1 := check.initVar(lhs[0], &x) t1 := check.initVar(lhs[0], &x)
@ -216,10 +219,15 @@ func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, allowCommaOk bool) {
} }
} }
invalidateVars(lhs)
// lhs variables may be function result parameters (return statement); // lhs variables may be function result parameters (return statement);
// use rhs position for properly located error messages // use rhs position for properly located error messages
if returnPos.IsValid() {
check.errorf(returnPos, "wrong number of return values (want %d, got %d)", l, r)
return
}
check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r) check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r)
invalidateVars(lhs)
} }
func (check *checker) assignVars(lhs, rhs []ast.Expr) { func (check *checker) assignVars(lhs, rhs []ast.Expr) {
@ -318,7 +326,7 @@ func (check *checker) shortVarDecl(lhs, rhs []ast.Expr) {
vars[i] = obj vars[i] = obj
} }
check.initVars(vars, rhs, true) check.initVars(vars, rhs, token.NoPos)
// declare variables // declare variables
n := scope.Len() n := scope.Len()

View File

@ -768,14 +768,14 @@ func (check *checker) index(arg ast.Expr, length int64) (i int64, ok bool) {
// the index/size must be of integer type // the index/size must be of integer type
if !isInteger(x.typ) { if !isInteger(x.typ) {
check.invalidArg(x.pos(), "%s must be integer", &x) check.invalidArg(x.pos(), "index %s must be integer", &x)
return return
} }
// a constant index/size i must be 0 <= i < length // a constant index/size i must be 0 <= i < length
if x.mode == constant { if x.mode == constant {
if exact.Sign(x.val) < 0 { if exact.Sign(x.val) < 0 {
check.invalidArg(x.pos(), "%s must not be negative", &x) check.invalidArg(x.pos(), "index %s must not be negative", &x)
return return
} }
i, ok = exact.Int64Val(x.val) i, ok = exact.Int64Val(x.val)
@ -806,8 +806,10 @@ func (check *checker) indexedElts(elts []ast.Expr, typ Type, length int64) int64
if i, ok := check.index(kv.Key, length); ok { if i, ok := check.index(kv.Key, length); ok {
if i >= 0 { if i >= 0 {
index = i index = i
}
validIndex = true validIndex = true
} else {
check.errorf(e.Pos(), "index %s must be integer constant", kv.Key)
}
} }
eval = kv.Value eval = kv.Value
} else if length >= 0 && index >= length { } else if length >= 0 && index >= length {

View File

@ -473,7 +473,7 @@ func (check *checker) varDecl(obj *Var, typ, init ast.Expr) {
} }
if m, _ := init.(*multiExpr); m != nil { if m, _ := init.(*multiExpr); m != nil {
check.initVars(m.lhs, m.rhs, true) check.initVars(m.lhs, m.rhs, token.NoPos)
return return
} }

View File

@ -112,12 +112,10 @@ func TestStdfixed(t *testing.T) {
"bug136.go", "bug179.go", "bug344.go", // TODO(gri) implement missing label checks "bug136.go", "bug179.go", "bug344.go", // TODO(gri) implement missing label checks
"bug251.go", // TODO(gri) incorrect cycle checks for interface types "bug251.go", // TODO(gri) incorrect cycle checks for interface types
"bug165.go", // TODO(gri) isComparable not working for incomplete struct type "bug165.go", // TODO(gri) isComparable not working for incomplete struct type
"bug176.go", // TODO(gri) composite literal array index must be non-negative constant
"bug200.go", // TODO(gri) complete duplicate checking in expr switches "bug200.go", // TODO(gri) complete duplicate checking in expr switches
"bug223.go", "bug413.go", "bug459.go", // TODO(gri) complete initialization checks "bug223.go", "bug413.go", "bug459.go", // TODO(gri) complete initialization checks
"bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore "bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore
"bug250.go", // TODO(gri) fix recursive interfaces "bug250.go", // TODO(gri) fix recursive interfaces
"bug326.go", // TODO(gri) assignment doesn't guard against len(rhs) == 0
"bug373.go", // TODO(gri) implement use checks "bug373.go", // TODO(gri) implement use checks
"bug376.go", // TODO(gri) built-ins must be called (no built-in function expressions) "bug376.go", // TODO(gri) built-ins must be called (no built-in function expressions)
"issue3924.go", // TODO(gri) && and || produce bool result (not untyped bool) "issue3924.go", // TODO(gri) && and || produce bool result (not untyped bool)

View File

@ -230,7 +230,7 @@ func (check *checker) stmt(s ast.Stmt, fallthroughOk bool) {
lhs[i] = res lhs[i] = res
} }
if len(s.Results) > 0 || !named { if len(s.Results) > 0 || !named {
check.initVars(lhs, s.Results, false) check.initVars(lhs, s.Results, s.Return)
return return
} }
} else if len(s.Results) > 0 { } else if len(s.Results) > 0 {

View File

@ -178,6 +178,14 @@ func array_literals() {
_ = A1{2.1 /* ERROR "overflows" */ } _ = A1{2.1 /* ERROR "overflows" */ }
_ = A1{"foo" /* ERROR "cannot convert" */ } _ = A1{"foo" /* ERROR "cannot convert" */ }
// indices must be integer constants
i := 1
const f = 2.1
const s = "foo"
_ = A1{i /* ERROR "index i must be integer constant" */ : 0}
_ = A1{f /* ERROR "cannot be .* represented" */ : 0}
_ = A1{s /* ERROR "cannot convert" */ : 0}
a0 := [...]int{} a0 := [...]int{}
assert(len(a0) == 0) assert(len(a0) == 0)
@ -226,14 +234,22 @@ func slice_literals() {
_ = S0{"foo" /* ERROR "cannot convert" */ } _ = S0{"foo" /* ERROR "cannot convert" */ }
// indices must be resolved correctly // indices must be resolved correctly
// (for details, see comment in go/parser/parser.go, method parseElement) const index1 = 1
index1 := 1
_ = S0{index1: 1} _ = S0{index1: 1}
_ = S0{index2: 2} _ = S0{index2: 2}
_ = S0{index3 /* ERROR "undeclared name" */ : 3} _ = S0{index3 /* ERROR "undeclared name" */ : 3}
// indices must be integer constants
i := 1
const f = 2.1
const s = "foo"
_ = S0{i /* ERROR "index i must be integer constant" */ : 0}
_ = S0{f /* ERROR "cannot be .* represented" */ : 0}
_ = S0{s /* ERROR "cannot convert" */ : 0}
} }
var index2 int = 2 const index2 int = 2
func map_literals() { func map_literals() {
type M0 map[string]int type M0 map[string]int
@ -247,14 +263,14 @@ func map_literals() {
_ = M0{"foo": 1, "bar": 2, "foo" /* ERROR "duplicate key" */ : 3 } _ = M0{"foo": 1, "bar": 2, "foo" /* ERROR "duplicate key" */ : 3 }
// map keys must be resolved correctly // map keys must be resolved correctly
// (for details, see comment in go/parser/parser.go, method parseElement)
key1 := "foo" key1 := "foo"
_ = M0{key1: 1} _ = M0{key1: 1}
_ = M0{key2: 2} _ = M0{key2: 2}
_ = M0{key3 /* ERROR "undeclared name" */ : 2} _ = M0{key3 /* ERROR "undeclared name" */ : 2}
var value int
_ = M1{true: 1, false: 0} _ = M1{true: 1, false: 0}
_ = M2{nil: 0, &index2: 1} _ = M2{nil: 0, &value: 1}
} }
var key2 string = "bar" var key2 string = "bar"

View File

@ -6,7 +6,35 @@
package stmt0 package stmt0
func assignments() { func assignments0() (int, int) {
var a, b, c int
var ch chan int
f0 := func() {}
f1 := func() int { return 1 }
f2 := func() (int, int) { return 1, 2 }
f3 := func() (int, int, int) { return 1, 2, 3 }
a, b, c = 1, 2, 3
a, b, c = 1 /* ERROR "assignment count mismatch" */ , 2
a, b, c = 1 /* ERROR "assignment count mismatch" */ , 2, 3, 4
a = f0 /* ERROR "used as value" */ ()
a = f1()
a = f2 /* ERROR "used as single value" */ ()
a, b = f2()
a, b, c = f2 /* ERROR "assignment count mismatch" */ ()
a, b, c = f3()
a, b = f3 /* ERROR "assignment count mismatch" */ ()
a, b, c = <- /* ERROR "assignment count mismatch" */ ch
return /* ERROR "wrong number of return values" */
return /* ERROR "wrong number of return values" */ 1
return 1, 2
return /* ERROR "wrong number of return values" */ 1, 2, 3
}
func assignments1() {
b, i, f, c, s := false, 1, 1.0, 1i, "foo" b, i, f, c, s := false, 1, 1.0, 1i, "foo"
b = i /* ERROR "cannot assign" */ b = i /* ERROR "cannot assign" */
i = f /* ERROR "cannot assign" */ i = f /* ERROR "cannot assign" */
@ -49,7 +77,7 @@ func assignments() {
// test cases for issue 5500 // test cases for issue 5500
_ = func() (int, bool) { _ = func() (int, bool) {
var m map[int]int var m map[int]int
return m /* ERROR "assignment count mismatch" */ [0] return /* ERROR "wrong number of return values" */ m[0]
} }
g := func(int, bool){} g := func(int, bool){}