go/types: better error message when using multi-valued expressions in single-value context
Also: Added initial set of (missing and/or spread out) tests for binary operations.
Fixes #11896.
Change-Id: I037436d8318c18f9758b435eca2d45b3bdd17ef8
Reviewed-on: https://go-review.googlesource.com/14660
Reviewed-by: Alan Donovan <adonovan@google.com>
diff --git a/src/go/types/assignments.go b/src/go/types/assignments.go
index 4231196..240cea2 100644
--- a/src/go/types/assignments.go
+++ b/src/go/types/assignments.go
@@ -30,6 +30,8 @@
// x must be a single value
// (tuple types are never named - no need for underlying type)
+ // TODO(gri) We may be able to get rid of this check now that
+ // we check for single-valued expressions more rigorously.
if t, _ := x.typ.(*Tuple); t != nil {
assert(t.Len() > 1)
check.errorf(x.pos(), "%d-valued expression %s used as single value", t.Len(), x)
@@ -205,7 +207,7 @@
// 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)
- get, r, commaOk := unpack(func(x *operand, i int) { check.expr(x, rhs[i]) }, len(rhs), l == 2 && !returnPos.IsValid())
+ get, r, commaOk := unpack(func(x *operand, i int) { check.multiExpr(x, rhs[i]) }, len(rhs), l == 2 && !returnPos.IsValid())
if get == nil || l != r {
// invalidate lhs and use rhs
for _, obj := range lhs {
@@ -244,7 +246,7 @@
func (check *Checker) assignVars(lhs, rhs []ast.Expr) {
l := len(lhs)
- get, r, commaOk := unpack(func(x *operand, i int) { check.expr(x, rhs[i]) }, len(rhs), l == 2)
+ get, r, commaOk := unpack(func(x *operand, i int) { check.multiExpr(x, rhs[i]) }, len(rhs), l == 2)
if get == nil {
return // error reported by unpack
}
diff --git a/src/go/types/builtins.go b/src/go/types/builtins.go
index a879c81..be6c929 100644
--- a/src/go/types/builtins.go
+++ b/src/go/types/builtins.go
@@ -44,7 +44,7 @@
switch id {
default:
// make argument getter
- arg, nargs, _ = unpack(func(x *operand, i int) { check.expr(x, call.Args[i]) }, nargs, false)
+ arg, nargs, _ = unpack(func(x *operand, i int) { check.multiExpr(x, call.Args[i]) }, nargs, false)
if arg == nil {
return
}
diff --git a/src/go/types/call.go b/src/go/types/call.go
index c3ed077..14c94de 100644
--- a/src/go/types/call.go
+++ b/src/go/types/call.go
@@ -61,7 +61,7 @@
return statement
}
- arg, n, _ := unpack(func(x *operand, i int) { check.expr(x, e.Args[i]) }, len(e.Args), false)
+ arg, n, _ := unpack(func(x *operand, i int) { check.multiExpr(x, e.Args[i]) }, len(e.Args), false)
if arg == nil {
x.mode = invalid
x.expr = e
diff --git a/src/go/types/expr.go b/src/go/types/expr.go
index 9d2331a..bbdaf9b 100644
--- a/src/go/types/expr.go
+++ b/src/go/types/expr.go
@@ -1453,23 +1453,40 @@
check.errorf(pos, "%s cannot have dynamic type %s (%s %s)", x, T, msg, method.name)
}
+func (check *Checker) singleValue(x *operand) {
+ if x.mode == value {
+ // tuple types are never named - no need for Underlying() below
+ if t, ok := x.typ.(*Tuple); ok && t.Len() != 1 {
+ check.errorf(x.pos(), "%d-valued %s in single-value context", t.Len(), x)
+ x.mode = invalid
+ }
+ }
+}
+
// expr typechecks expression e and initializes x with the expression value.
+// The result must be a single value.
// If an error occurred, x.mode is set to invalid.
//
func (check *Checker) expr(x *operand, e ast.Expr) {
+ check.multiExpr(x, e)
+ check.singleValue(x)
+}
+
+// multiExpr is like expr but the result may be a multi-value.
+func (check *Checker) multiExpr(x *operand, e ast.Expr) {
check.rawExpr(x, e, nil)
var msg string
switch x.mode {
default:
return
case novalue:
- msg = "used as value"
+ msg = "%s used as value"
case builtin:
- msg = "must be called"
+ msg = "%s must be called"
case typexpr:
- msg = "is not an expression"
+ msg = "%s is not an expression"
}
- check.errorf(x.pos(), "%s %s", x, msg)
+ check.errorf(x.pos(), msg, x)
x.mode = invalid
}
@@ -1480,18 +1497,19 @@
func (check *Checker) exprWithHint(x *operand, e ast.Expr, hint Type) {
assert(hint != nil)
check.rawExpr(x, e, hint)
+ check.singleValue(x)
var msg string
switch x.mode {
default:
return
case novalue:
- msg = "used as value"
+ msg = "%s used as value"
case builtin:
- msg = "must be called"
+ msg = "%s must be called"
case typexpr:
- msg = "is not an expression"
+ msg = "%s is not an expression"
}
- check.errorf(x.pos(), "%s %s", x, msg)
+ check.errorf(x.pos(), msg, x)
x.mode = invalid
}
@@ -1500,6 +1518,7 @@
//
func (check *Checker) exprOrType(x *operand, e ast.Expr) {
check.rawExpr(x, e, nil)
+ check.singleValue(x)
if x.mode == novalue {
check.errorf(x.pos(), "%s used as value or type", x)
x.mode = invalid
diff --git a/src/go/types/testdata/expr0.src b/src/go/types/testdata/expr0.src
index 3120c6f..2a917c0 100644
--- a/src/go/types/testdata/expr0.src
+++ b/src/go/types/testdata/expr0.src
@@ -172,3 +172,9 @@
p3 P = &p2
)
+func g() (a, b int) { return }
+
+func _() {
+ _ = -g /* ERROR 2-valued g */ ()
+ _ = <-g /* ERROR 2-valued g */ ()
+}
diff --git a/src/go/types/testdata/expr1.src b/src/go/types/testdata/expr1.src
index 8ef0aed..eaaf610 100644
--- a/src/go/types/testdata/expr1.src
+++ b/src/go/types/testdata/expr1.src
@@ -5,3 +5,123 @@
// binary expressions
package expr1
+
+type mybool bool
+
+func _(x, y bool, z mybool) {
+ x = x || y
+ x = x || true
+ x = x || false
+ x = x && y
+ x = x && true
+ x = x && false
+
+ z = z /* ERROR mismatched types */ || y
+ z = z || true
+ z = z || false
+ z = z /* ERROR mismatched types */ && y
+ z = z && true
+ z = z && false
+}
+
+type myint int
+
+func _(x, y int, z myint) {
+ x = x + 1
+ x = x + 1.0
+ x = x + 1.1 // ERROR truncated to int
+ x = x + y
+ x = x - y
+ x = x * y
+ x = x / y
+ x = x % y
+ x = x << y // ERROR must be unsigned integer
+ x = x >> y // ERROR must be unsigned integer
+
+ z = z + 1
+ z = z + 1.0
+ z = z + 1.1 // ERROR truncated to int
+ z = z /* ERROR mismatched types */ + y
+ z = z /* ERROR mismatched types */ - y
+ z = z /* ERROR mismatched types */ * y
+ z = z /* ERROR mismatched types */ / y
+ z = z /* ERROR mismatched types */ % y
+ z = z << y // ERROR must be unsigned integer
+ z = z >> y // ERROR must be unsigned integer
+}
+
+type myuint uint
+
+func _(x, y uint, z myuint) {
+ x = x + 1
+ x = x + - /* ERROR overflows uint */ 1
+ x = x + 1.0
+ x = x + 1.1 // ERROR truncated to uint
+ x = x + y
+ x = x - y
+ x = x * y
+ x = x / y
+ x = x % y
+ x = x << y
+ x = x >> y
+
+ z = z + 1
+ z = x + - /* ERROR overflows uint */ 1
+ z = z + 1.0
+ z = z + 1.1 // ERROR truncated to uint
+ z = z /* ERROR mismatched types */ + y
+ z = z /* ERROR mismatched types */ - y
+ z = z /* ERROR mismatched types */ * y
+ z = z /* ERROR mismatched types */ / y
+ z = z /* ERROR mismatched types */ % y
+ z = z << y
+ z = z >> y
+}
+
+type myfloat64 float64
+
+func _(x, y float64, z myfloat64) {
+ x = x + 1
+ x = x + -1
+ x = x + 1.0
+ x = x + 1.1
+ x = x + y
+ x = x - y
+ x = x * y
+ x = x / y
+ x = x /* ERROR not defined */ % y
+ x = x /* ERROR operand x .* must be integer */ << y
+ x = x /* ERROR operand x .* must be integer */ >> y
+
+ z = z + 1
+ z = z + -1
+ z = z + 1.0
+ z = z + 1.1
+ z = z /* ERROR mismatched types */ + y
+ z = z /* ERROR mismatched types */ - y
+ z = z /* ERROR mismatched types */ * y
+ z = z /* ERROR mismatched types */ / y
+ z = z /* ERROR mismatched types */ % y
+ z = z /* ERROR operand z .* must be integer */ << y
+ z = z /* ERROR operand z .* must be integer */ >> y
+}
+
+type mystring string
+
+func _(x, y string, z mystring) {
+ x = x + "foo"
+ x = x /* ERROR not defined */ - "foo"
+ x = x + 1 // ERROR cannot convert
+ x = x + y
+ x = x /* ERROR not defined */ - y
+ x = x * 10 // ERROR cannot convert
+}
+
+func f() (a, b int) { return }
+
+func _(x int) {
+ _ = f /* ERROR 2-valued f */ () + 1
+ _ = x + f /* ERROR 2-valued f */ ()
+ _ = f /* ERROR 2-valued f */ () + f
+ _ = f /* ERROR 2-valued f */ () + f /* ERROR 2-valued f */ ()
+}
diff --git a/src/go/types/testdata/stmt0.src b/src/go/types/testdata/stmt0.src
index 80abbd1..52ed65c 100644
--- a/src/go/types/testdata/stmt0.src
+++ b/src/go/types/testdata/stmt0.src
@@ -631,14 +631,14 @@
func issue11687() {
f := func() (_, _ int) { return }
- switch f /* ERROR "2-valued expression" */ () {
+ switch f /* ERROR "2-valued f" */ () {
}
var x int
- switch f /* ERROR "2-valued expression" */ () {
+ switch f /* ERROR "2-valued f" */ () {
case x:
}
switch x {
- case f /* ERROR "cannot compare" */ (): // TODO(gri) better error message (issue 11896)
+ case f /* ERROR "2-valued f" */ ():
}
}
diff --git a/src/go/types/testdata/vardecl.src b/src/go/types/testdata/vardecl.src
index fb6b5f7..0082537 100644
--- a/src/go/types/testdata/vardecl.src
+++ b/src/go/types/testdata/vardecl.src
@@ -31,7 +31,7 @@
var _, _ = 1 /* ERROR "assignment count mismatch" */
var _, _, _ /* ERROR "missing init expr for _" */ = 1, 2
-var _ = g /* ERROR "2-valued expr" */ ()
+var _ = g /* ERROR "2-valued g" */ ()
var _, _ = g()
var _, _, _ = g /* ERROR "assignment count mismatch" */ ()
@@ -50,7 +50,7 @@
_, _ = 1 /* ERROR "assignment count mismatch" */
_, _, _ /* ERROR "missing init expr for _" */ = 1, 2
- _ = g /* ERROR "2-valued expr" */ ()
+ _ = g /* ERROR "2-valued g" */ ()
_, _ = g()
_, _, _ = g /* ERROR "assignment count mismatch" */ ()