mirror of
https://github.com/golang/go.git
synced 2025-05-05 23:53:05 +00:00
go.tools/go/types: reuse code for m:n assignments
- more test cases R=adonovan CC=golang-dev https://golang.org/cl/10952044
This commit is contained in:
parent
59f09dcb8c
commit
1f28df4b6c
@ -24,7 +24,7 @@ func (check *checker) assignment(x *operand, to Type) bool {
|
||||
}
|
||||
|
||||
if t, ok := x.typ.(*Tuple); ok {
|
||||
// TODO(gri) elsewhere we use "assignment count mismatch" (consolidate)
|
||||
assert(t.Len() > 1)
|
||||
check.errorf(x.pos(), "%d-valued expression %s used as single value", t.Len(), x)
|
||||
x.mode = invalid
|
||||
return false
|
||||
@ -136,11 +136,13 @@ func (check *checker) assignVar(lhs ast.Expr, x *operand) {
|
||||
}
|
||||
|
||||
func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, allowCommaOk bool) {
|
||||
assert(len(lhs) > 0)
|
||||
l := len(lhs)
|
||||
r := len(rhs)
|
||||
assert(l > 0)
|
||||
|
||||
// If the lhs and rhs have corresponding expressions,
|
||||
// treat each matching pair as an individual pair.
|
||||
if len(lhs) == len(rhs) {
|
||||
if l == r {
|
||||
var x operand
|
||||
for i, e := range rhs {
|
||||
check.expr(&x, e)
|
||||
@ -152,8 +154,8 @@ func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, allowCommaOk bool) {
|
||||
// Otherwise, the rhs must be a single expression (possibly
|
||||
// a function call returning multiple values, or a comma-ok
|
||||
// expression).
|
||||
if len(rhs) == 1 {
|
||||
// len(lhs) > 1
|
||||
if r == 1 {
|
||||
// l > 1
|
||||
// Start with rhs so we have expression types
|
||||
// for declarations with implicit types.
|
||||
var x operand
|
||||
@ -163,18 +165,21 @@ func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, allowCommaOk bool) {
|
||||
return
|
||||
}
|
||||
|
||||
if t, ok := x.typ.(*Tuple); ok && len(lhs) == t.Len() {
|
||||
if t, ok := x.typ.(*Tuple); ok {
|
||||
// function result
|
||||
for i, lhs := range lhs {
|
||||
x.mode = value
|
||||
x.expr = rhs[0]
|
||||
x.typ = t.At(i).typ
|
||||
check.initVar(lhs, &x)
|
||||
r = t.Len()
|
||||
if l == r {
|
||||
for i, lhs := range lhs {
|
||||
x.mode = value
|
||||
x.expr = rhs[0]
|
||||
x.typ = t.At(i).typ
|
||||
check.initVar(lhs, &x)
|
||||
}
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if allowCommaOk && x.mode == valueok && len(lhs) == 2 {
|
||||
if allowCommaOk && x.mode == valueok && l == 2 {
|
||||
// comma-ok expression
|
||||
x.mode = value
|
||||
check.initVar(lhs[0], &x)
|
||||
@ -186,18 +191,20 @@ func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, allowCommaOk bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// lhs variables may be function parameters (return assignment);
|
||||
// use rhs position information for properly located error messages
|
||||
check.errorf(rhs[0].Pos(), "assignment count mismatch: %d = %d", len(lhs), len(rhs))
|
||||
// lhs variables may be function result parameters (return statement);
|
||||
// use rhs position for properly located error messages
|
||||
check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r)
|
||||
invalidateVars(lhs)
|
||||
}
|
||||
|
||||
func (check *checker) assignVars(lhs, rhs []ast.Expr) {
|
||||
assert(len(lhs) > 0)
|
||||
l := len(lhs)
|
||||
r := len(rhs)
|
||||
assert(l > 0)
|
||||
|
||||
// If the lhs and rhs have corresponding expressions,
|
||||
// treat each matching pair as an individual pair.
|
||||
if len(lhs) == len(rhs) {
|
||||
if l == r {
|
||||
var x operand
|
||||
for i, e := range rhs {
|
||||
check.expr(&x, e)
|
||||
@ -209,26 +216,29 @@ func (check *checker) assignVars(lhs, rhs []ast.Expr) {
|
||||
// Otherwise, the rhs must be a single expression (possibly
|
||||
// a function call returning multiple values, or a comma-ok
|
||||
// expression).
|
||||
if len(rhs) == 1 {
|
||||
// len(lhs) > 1
|
||||
if r == 1 {
|
||||
// l > 1
|
||||
var x operand
|
||||
check.expr(&x, rhs[0])
|
||||
if x.mode == invalid {
|
||||
return
|
||||
}
|
||||
|
||||
if t, ok := x.typ.(*Tuple); ok && len(lhs) == t.Len() {
|
||||
if t, ok := x.typ.(*Tuple); ok {
|
||||
// function result
|
||||
for i, lhs := range lhs {
|
||||
x.mode = value
|
||||
x.expr = rhs[0]
|
||||
x.typ = t.At(i).typ
|
||||
check.assignVar(lhs, &x)
|
||||
r = t.Len()
|
||||
if l == r {
|
||||
for i, lhs := range lhs {
|
||||
x.mode = value
|
||||
x.expr = rhs[0]
|
||||
x.typ = t.At(i).typ
|
||||
check.assignVar(lhs, &x)
|
||||
}
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if x.mode == valueok && len(lhs) == 2 {
|
||||
if x.mode == valueok && l == 2 {
|
||||
// comma-ok expression
|
||||
x.mode = value
|
||||
check.assignVar(lhs[0], &x)
|
||||
@ -240,7 +250,7 @@ func (check *checker) assignVars(lhs, rhs []ast.Expr) {
|
||||
}
|
||||
}
|
||||
|
||||
check.errorf(rhs[0].Pos(), "assignment count mismatch: %d = %d", len(lhs), len(rhs))
|
||||
check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r)
|
||||
}
|
||||
|
||||
func (check *checker) shortVarDecl(lhs, rhs []ast.Expr) {
|
||||
|
@ -42,11 +42,13 @@ type mdecl struct {
|
||||
meth *ast.FuncDecl
|
||||
}
|
||||
|
||||
// A projExpr projects the index'th value of a multi-valued expression.
|
||||
// projExpr implements ast.Expr.
|
||||
type projExpr struct {
|
||||
lhs []*Var // all variables on the lhs
|
||||
ast.Expr // rhs
|
||||
// A multiExpr describes the lhs variables and a single but
|
||||
// (expected to be) multi-valued rhs init expr of a variable
|
||||
// declaration.
|
||||
type multiExpr struct {
|
||||
lhs []*Var
|
||||
rhs []ast.Expr // len(rhs) == 1
|
||||
ast.Expr // dummy to satisfy ast.Expr interface
|
||||
}
|
||||
|
||||
// arityMatch checks that the lhs and rhs of a const or var decl
|
||||
@ -227,7 +229,7 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
|
||||
// (lhs may not be fully set up yet, but
|
||||
// that's fine because declare simply collects
|
||||
// the information for later processing.)
|
||||
init = &projExpr{lhs, s.Values[0]}
|
||||
init = &multiExpr{lhs, s.Values, nil}
|
||||
default:
|
||||
if i < len(s.Values) {
|
||||
init = s.Values[i]
|
||||
@ -421,68 +423,23 @@ func (check *checker) declareVar(obj *Var, typ, init ast.Expr) {
|
||||
obj.typ = check.typ(typ, nil, false)
|
||||
}
|
||||
|
||||
// check initialization
|
||||
if init == nil {
|
||||
if typ == nil {
|
||||
// error reported before by arityMatch
|
||||
obj.typ = Typ[Invalid]
|
||||
}
|
||||
return // error reported before
|
||||
return
|
||||
}
|
||||
|
||||
// unpack projection expression, if any
|
||||
proj, multi := init.(*projExpr)
|
||||
if multi {
|
||||
init = proj.Expr
|
||||
if m, _ := init.(*multiExpr); m != nil {
|
||||
check.initVars(m.lhs, m.rhs, true)
|
||||
return
|
||||
}
|
||||
|
||||
var x operand
|
||||
check.expr(&x, init)
|
||||
if x.mode == invalid {
|
||||
goto Error
|
||||
}
|
||||
|
||||
if multi {
|
||||
if t, ok := x.typ.(*Tuple); ok && len(proj.lhs) == t.Len() {
|
||||
// function result
|
||||
x.mode = value
|
||||
for i, lhs := range proj.lhs {
|
||||
x.expr = nil // TODO(gri) should do better here
|
||||
x.typ = t.At(i).typ
|
||||
check.initVar(lhs, &x)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if x.mode == valueok && len(proj.lhs) == 2 {
|
||||
// comma-ok expression
|
||||
x.mode = value
|
||||
check.initVar(proj.lhs[0], &x)
|
||||
|
||||
x.typ = Typ[UntypedBool]
|
||||
check.initVar(proj.lhs[1], &x)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(gri) better error message
|
||||
check.errorf(proj.lhs[0].Pos(), "assignment count mismatch")
|
||||
goto Error
|
||||
}
|
||||
|
||||
check.initVar(obj, &x)
|
||||
return
|
||||
|
||||
Error:
|
||||
// mark all involved variables so we can avoid repeated error messages
|
||||
if multi {
|
||||
for _, obj := range proj.lhs {
|
||||
if obj.typ == nil {
|
||||
obj.typ = Typ[Invalid]
|
||||
obj.visited = true
|
||||
}
|
||||
}
|
||||
} else if obj.typ == nil {
|
||||
obj.typ = Typ[Invalid]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (check *checker) declareType(obj *TypeName, typ ast.Expr, def *Named, cycleOk bool) {
|
||||
@ -641,7 +598,7 @@ func (check *checker) declStmt(decl ast.Decl) {
|
||||
}
|
||||
|
||||
case token.VAR:
|
||||
// For declareVar called with a projExpr we need the fully
|
||||
// For declareVar called with a multiExpr we need the fully
|
||||
// initialized lhs. Compute it in a separate pre-pass.
|
||||
lhs := make([]*Var, len(s.Names))
|
||||
for i, name := range s.Names {
|
||||
@ -656,8 +613,8 @@ func (check *checker) declStmt(decl ast.Decl) {
|
||||
// lhs and rhs match
|
||||
init = s.Values[i]
|
||||
case 1:
|
||||
// rhs must be a multi-valued expression
|
||||
init = &projExpr{lhs, s.Values[0]}
|
||||
// rhs is expected to be a multi-valued expression
|
||||
init = &multiExpr{lhs, s.Values, nil}
|
||||
default:
|
||||
if i < len(s.Values) {
|
||||
init = s.Values[i]
|
||||
|
2
go/types/testdata/decls1.src
vendored
2
go/types/testdata/decls1.src
vendored
@ -77,7 +77,7 @@ var (
|
||||
u2 = iface.([]int)
|
||||
u3 = iface.(a /* ERROR "not a type" */ )
|
||||
u4, ok = iface.(int)
|
||||
u5 /* ERROR "assignment count mismatch" */ , ok2, ok3 = iface.(int)
|
||||
u5, ok2, ok3 = iface /* ERROR "assignment count mismatch" */ .(int)
|
||||
)
|
||||
|
||||
// Constant expression initializations
|
||||
|
27
go/types/testdata/vardecl.src
vendored
27
go/types/testdata/vardecl.src
vendored
@ -4,7 +4,10 @@
|
||||
|
||||
package vardecl
|
||||
|
||||
// Prerequisites.
|
||||
func f() {}
|
||||
func g() (x, y int) { return }
|
||||
var m map[string]int
|
||||
|
||||
// Var decls must have a type or an initializer.
|
||||
var _ int
|
||||
@ -21,23 +24,39 @@ var _ = f /* ERROR "used as value" */ ()
|
||||
// Identifier and expression arity must match.
|
||||
var _, _ = 1, 2
|
||||
var _ = 1, 2 /* ERROR "extra init expr 2" */
|
||||
var _ /* ERROR "assignment count mismatch" */ , _ = 1 // TODO(gri) better error message here
|
||||
var _, _ = 1 /* ERROR "assignment count mismatch" */
|
||||
var _, _, _ /* ERROR "missing init expr for _" */ = 1, 2
|
||||
|
||||
var _ = g /* ERROR "2-valued expr" */ ()
|
||||
var _, _ = g()
|
||||
var _, _, _ = g /* ERROR "assignment count mismatch" */ ()
|
||||
|
||||
var _ = m["foo"]
|
||||
var _, _ = m["foo"]
|
||||
var _, _, _ = m /* ERROR "assignment count mismatch" */ ["foo"]
|
||||
|
||||
var _, _ int = 1, 2
|
||||
var _ int = 1, 2 /* ERROR "extra init expr 2" */
|
||||
var _ /* ERROR "assignment count mismatch" */ , _ int = 1 // TODO(gri) better error message here
|
||||
var _, _ int = 1 /* ERROR "assignment count mismatch" */
|
||||
var _, _, _ /* ERROR "missing init expr for _" */ int = 1, 2
|
||||
|
||||
var (
|
||||
_, _ = 1, 2
|
||||
_ = 1, 2 /* ERROR "extra init expr 2" */
|
||||
_ /* ERROR "assignment count mismatch" */ , _ = 1 // TODO(gri) better error message here
|
||||
_, _ = 1 /* ERROR "assignment count mismatch" */
|
||||
_, _, _ /* ERROR "missing init expr for _" */ = 1, 2
|
||||
|
||||
_ = g /* ERROR "2-valued expr" */ ()
|
||||
_, _ = g()
|
||||
_, _, _ = g /* ERROR "assignment count mismatch" */ ()
|
||||
|
||||
_ = m["foo"]
|
||||
_, _ = m["foo"]
|
||||
_, _, _ = m /* ERROR "assignment count mismatch" */ ["foo"]
|
||||
|
||||
_, _ int = 1, 2
|
||||
_ int = 1, 2 /* ERROR "extra init expr 2" */
|
||||
_ /* ERROR "assignment count mismatch" */ , _ int = 1 // TODO(gri) better error message here
|
||||
_, _ int = 1 /* ERROR "assignment count mismatch" */
|
||||
_, _, _ /* ERROR "missing init expr for _" */ int = 1, 2
|
||||
)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user