mirror of
https://github.com/golang/go.git
synced 2025-05-07 16:43:03 +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 {
|
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)
|
check.errorf(x.pos(), "%d-valued expression %s used as single value", t.Len(), x)
|
||||||
x.mode = invalid
|
x.mode = invalid
|
||||||
return false
|
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) {
|
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,
|
// If the lhs and rhs have corresponding expressions,
|
||||||
// treat each matching pair as an individual pair.
|
// treat each matching pair as an individual pair.
|
||||||
if len(lhs) == len(rhs) {
|
if l == r {
|
||||||
var x operand
|
var x operand
|
||||||
for i, e := range rhs {
|
for i, e := range rhs {
|
||||||
check.expr(&x, e)
|
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
|
// Otherwise, the rhs must be a single expression (possibly
|
||||||
// a function call returning multiple values, or a comma-ok
|
// a function call returning multiple values, or a comma-ok
|
||||||
// expression).
|
// expression).
|
||||||
if len(rhs) == 1 {
|
if r == 1 {
|
||||||
// len(lhs) > 1
|
// l > 1
|
||||||
// Start with rhs so we have expression types
|
// Start with rhs so we have expression types
|
||||||
// for declarations with implicit types.
|
// for declarations with implicit types.
|
||||||
var x operand
|
var x operand
|
||||||
@ -163,8 +165,10 @@ func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, allowCommaOk bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if t, ok := x.typ.(*Tuple); ok && len(lhs) == t.Len() {
|
if t, ok := x.typ.(*Tuple); ok {
|
||||||
// function result
|
// function result
|
||||||
|
r = t.Len()
|
||||||
|
if l == r {
|
||||||
for i, lhs := range lhs {
|
for i, lhs := range lhs {
|
||||||
x.mode = value
|
x.mode = value
|
||||||
x.expr = rhs[0]
|
x.expr = rhs[0]
|
||||||
@ -173,8 +177,9 @@ func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, allowCommaOk bool) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if allowCommaOk && x.mode == valueok && len(lhs) == 2 {
|
if allowCommaOk && x.mode == valueok && l == 2 {
|
||||||
// comma-ok expression
|
// comma-ok expression
|
||||||
x.mode = value
|
x.mode = value
|
||||||
check.initVar(lhs[0], &x)
|
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);
|
// lhs variables may be function result parameters (return statement);
|
||||||
// use rhs position information for properly located error messages
|
// use rhs position for properly located error messages
|
||||||
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)
|
||||||
invalidateVars(lhs)
|
invalidateVars(lhs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (check *checker) assignVars(lhs, rhs []ast.Expr) {
|
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,
|
// If the lhs and rhs have corresponding expressions,
|
||||||
// treat each matching pair as an individual pair.
|
// treat each matching pair as an individual pair.
|
||||||
if len(lhs) == len(rhs) {
|
if l == r {
|
||||||
var x operand
|
var x operand
|
||||||
for i, e := range rhs {
|
for i, e := range rhs {
|
||||||
check.expr(&x, e)
|
check.expr(&x, e)
|
||||||
@ -209,16 +216,18 @@ func (check *checker) assignVars(lhs, rhs []ast.Expr) {
|
|||||||
// Otherwise, the rhs must be a single expression (possibly
|
// Otherwise, the rhs must be a single expression (possibly
|
||||||
// a function call returning multiple values, or a comma-ok
|
// a function call returning multiple values, or a comma-ok
|
||||||
// expression).
|
// expression).
|
||||||
if len(rhs) == 1 {
|
if r == 1 {
|
||||||
// len(lhs) > 1
|
// l > 1
|
||||||
var x operand
|
var x operand
|
||||||
check.expr(&x, rhs[0])
|
check.expr(&x, rhs[0])
|
||||||
if x.mode == invalid {
|
if x.mode == invalid {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if t, ok := x.typ.(*Tuple); ok && len(lhs) == t.Len() {
|
if t, ok := x.typ.(*Tuple); ok {
|
||||||
// function result
|
// function result
|
||||||
|
r = t.Len()
|
||||||
|
if l == r {
|
||||||
for i, lhs := range lhs {
|
for i, lhs := range lhs {
|
||||||
x.mode = value
|
x.mode = value
|
||||||
x.expr = rhs[0]
|
x.expr = rhs[0]
|
||||||
@ -227,8 +236,9 @@ func (check *checker) assignVars(lhs, rhs []ast.Expr) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if x.mode == valueok && len(lhs) == 2 {
|
if x.mode == valueok && l == 2 {
|
||||||
// comma-ok expression
|
// comma-ok expression
|
||||||
x.mode = value
|
x.mode = value
|
||||||
check.assignVar(lhs[0], &x)
|
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) {
|
func (check *checker) shortVarDecl(lhs, rhs []ast.Expr) {
|
||||||
|
@ -42,11 +42,13 @@ type mdecl struct {
|
|||||||
meth *ast.FuncDecl
|
meth *ast.FuncDecl
|
||||||
}
|
}
|
||||||
|
|
||||||
// A projExpr projects the index'th value of a multi-valued expression.
|
// A multiExpr describes the lhs variables and a single but
|
||||||
// projExpr implements ast.Expr.
|
// (expected to be) multi-valued rhs init expr of a variable
|
||||||
type projExpr struct {
|
// declaration.
|
||||||
lhs []*Var // all variables on the lhs
|
type multiExpr struct {
|
||||||
ast.Expr // rhs
|
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
|
// 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
|
// (lhs may not be fully set up yet, but
|
||||||
// that's fine because declare simply collects
|
// that's fine because declare simply collects
|
||||||
// the information for later processing.)
|
// the information for later processing.)
|
||||||
init = &projExpr{lhs, s.Values[0]}
|
init = &multiExpr{lhs, s.Values, nil}
|
||||||
default:
|
default:
|
||||||
if i < len(s.Values) {
|
if i < len(s.Values) {
|
||||||
init = s.Values[i]
|
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)
|
obj.typ = check.typ(typ, nil, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check initialization
|
||||||
if init == nil {
|
if init == nil {
|
||||||
if typ == nil {
|
if typ == nil {
|
||||||
|
// error reported before by arityMatch
|
||||||
obj.typ = Typ[Invalid]
|
obj.typ = Typ[Invalid]
|
||||||
}
|
}
|
||||||
return // error reported before
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// unpack projection expression, if any
|
if m, _ := init.(*multiExpr); m != nil {
|
||||||
proj, multi := init.(*projExpr)
|
check.initVars(m.lhs, m.rhs, true)
|
||||||
if multi {
|
return
|
||||||
init = proj.Expr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var x operand
|
var x operand
|
||||||
check.expr(&x, init)
|
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)
|
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) {
|
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:
|
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.
|
// initialized lhs. Compute it in a separate pre-pass.
|
||||||
lhs := make([]*Var, len(s.Names))
|
lhs := make([]*Var, len(s.Names))
|
||||||
for i, name := range s.Names {
|
for i, name := range s.Names {
|
||||||
@ -656,8 +613,8 @@ func (check *checker) declStmt(decl ast.Decl) {
|
|||||||
// lhs and rhs match
|
// lhs and rhs match
|
||||||
init = s.Values[i]
|
init = s.Values[i]
|
||||||
case 1:
|
case 1:
|
||||||
// rhs must be a multi-valued expression
|
// rhs is expected to be a multi-valued expression
|
||||||
init = &projExpr{lhs, s.Values[0]}
|
init = &multiExpr{lhs, s.Values, nil}
|
||||||
default:
|
default:
|
||||||
if i < len(s.Values) {
|
if i < len(s.Values) {
|
||||||
init = s.Values[i]
|
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)
|
u2 = iface.([]int)
|
||||||
u3 = iface.(a /* ERROR "not a type" */ )
|
u3 = iface.(a /* ERROR "not a type" */ )
|
||||||
u4, ok = iface.(int)
|
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
|
// Constant expression initializations
|
||||||
|
27
go/types/testdata/vardecl.src
vendored
27
go/types/testdata/vardecl.src
vendored
@ -4,7 +4,10 @@
|
|||||||
|
|
||||||
package vardecl
|
package vardecl
|
||||||
|
|
||||||
|
// Prerequisites.
|
||||||
func f() {}
|
func f() {}
|
||||||
|
func g() (x, y int) { return }
|
||||||
|
var m map[string]int
|
||||||
|
|
||||||
// Var decls must have a type or an initializer.
|
// Var decls must have a type or an initializer.
|
||||||
var _ int
|
var _ int
|
||||||
@ -21,23 +24,39 @@ var _ = f /* ERROR "used as value" */ ()
|
|||||||
// Identifier and expression arity must match.
|
// Identifier and expression arity must match.
|
||||||
var _, _ = 1, 2
|
var _, _ = 1, 2
|
||||||
var _ = 1, 2 /* ERROR "extra init expr 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 _, _, _ /* 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
|
||||||
var _ int = 1, 2 /* ERROR "extra init expr 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 _, _, _ /* ERROR "missing init expr for _" */ int = 1, 2
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_, _ = 1, 2
|
_, _ = 1, 2
|
||||||
_ = 1, 2 /* ERROR "extra init expr 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
|
_, _, _ /* 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
|
||||||
_ int = 1, 2 /* ERROR "extra init expr 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
|
_, _, _ /* ERROR "missing init expr for _" */ int = 1, 2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user