go/types, types2: refactor assignVars

Rather than using exprList and handle all cases together, split
apart the cases of n:n assignments and the cases of n:1 assignments.
For the former, the lhs types may (in a future CL) be used to infer
types on the rhs. This is a preparatory step.

Because the two cases are handled separately, the code is longer
(but also more explicit).

Some test cases were adjusted to avoifd (legitimate, but previously
supressed) "declared but not used" errors.

Change-Id: Ia43265f84e423b0ad5594612ba5a0ddce31a4a37
Reviewed-on: https://go-review.googlesource.com/c/go/+/478256
Reviewed-by: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Griesemer <gri@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Robert Griesemer <gri@google.com>
This commit is contained in:
Robert Griesemer 2023-03-21 09:58:03 -07:00 committed by Gopher Robot
parent 4237dea5e3
commit c02e1bfbdb
4 changed files with 83 additions and 31 deletions

View File

@ -387,30 +387,55 @@ func (check *Checker) initVars(lhs []*Var, orig_rhs []syntax.Expr, returnStmt sy
} }
func (check *Checker) assignVars(lhs, orig_rhs []syntax.Expr) { func (check *Checker) assignVars(lhs, orig_rhs []syntax.Expr) {
rhs, commaOk := check.exprList(orig_rhs, len(lhs) == 2) l, r := len(lhs), len(orig_rhs)
if len(lhs) != len(rhs) { // If l == 1 and the rhs is a single call, for a better
check.useLHS(lhs...) // error message don't handle it as n:n mapping below.
// don't report an error if we already reported one isCall := false
for _, x := range rhs { if r == 1 {
if x.mode == invalid { _, isCall = unparen(orig_rhs[0]).(*syntax.CallExpr)
return }
}
// If we have a n:n mapping from lhs variable to rhs expression,
// each value can be assigned to its corresponding variable.
if l == r && !isCall {
for i, lhs := range lhs {
var x operand
check.expr(&x, orig_rhs[i])
check.assignVar(lhs, &x)
} }
check.assignError(orig_rhs, len(lhs), len(rhs))
return return
} }
if commaOk { // If we don't have an n:n mapping, the rhs must be a single expression
check.assignVar(lhs[0], rhs[0]) // resulting in 2 or more values; otherwise we have an assignment mismatch.
check.assignVar(lhs[1], rhs[1]) if r != 1 {
check.recordCommaOkTypes(orig_rhs[0], rhs) check.assignError(orig_rhs, l, r)
check.useLHS(lhs...)
check.use(orig_rhs...)
return return
} }
for i, lhs := range lhs { rhs, commaOk := check.multiExpr(orig_rhs[0], l == 2)
check.assignVar(lhs, rhs[i]) r = len(rhs)
if l == r {
for i, lhs := range lhs {
check.assignVar(lhs, rhs[i])
}
if commaOk {
check.recordCommaOkTypes(orig_rhs[0], rhs)
}
return
} }
// In all other cases we have an assignment mismatch.
// Only report a mismatch error if there was no error
// on the rhs.
if rhs[0].mode != invalid {
check.assignError(orig_rhs, l, r)
}
check.useLHS(lhs...)
// orig_rhs[0] was already evaluated
} }
// unpackExpr unpacks a *syntax.ListExpr into a list of syntax.Expr. // unpackExpr unpacks a *syntax.ListExpr into a list of syntax.Expr.

View File

@ -1826,6 +1826,7 @@ func (check *Checker) expr(x *operand, e syntax.Expr) {
// If allowCommaOk is set and e is a map index, comma-ok, or comma-err // If allowCommaOk is set and e is a map index, comma-ok, or comma-err
// expression, the result is a two-element list containing the value // expression, the result is a two-element list containing the value
// of e, and an untyped bool value or an error value, respectively. // of e, and an untyped bool value or an error value, respectively.
// If an error occurred, list[0] is not valid.
func (check *Checker) multiExpr(e syntax.Expr, allowCommaOk bool) (list []*operand, commaOk bool) { func (check *Checker) multiExpr(e syntax.Expr, allowCommaOk bool) (list []*operand, commaOk bool) {
var x operand var x operand
check.rawExpr(&x, e, nil, false) check.rawExpr(&x, e, nil, false)

View File

@ -372,31 +372,56 @@ func (check *Checker) initVars(lhs []*Var, origRHS []ast.Expr, returnStmt ast.St
} }
} }
func (check *Checker) assignVars(lhs, origRHS []ast.Expr) { func (check *Checker) assignVars(lhs, orig_rhs []ast.Expr) {
rhs, commaOk := check.exprList(origRHS, len(lhs) == 2) l, r := len(lhs), len(orig_rhs)
if len(lhs) != len(rhs) { // If l == 1 and the rhs is a single call, for a better
check.useLHS(lhs...) // error message don't handle it as n:n mapping below.
// don't report an error if we already reported one isCall := false
for _, x := range rhs { if r == 1 {
if x.mode == invalid { _, isCall = unparen(orig_rhs[0]).(*ast.CallExpr)
return }
}
// If we have a n:n mapping from lhs variable to rhs expression,
// each value can be assigned to its corresponding variable.
if l == r && !isCall {
for i, lhs := range lhs {
var x operand
check.expr(&x, orig_rhs[i])
check.assignVar(lhs, &x)
} }
check.assignError(origRHS, len(lhs), len(rhs))
return return
} }
if commaOk { // If we don't have an n:n mapping, the rhs must be a single expression
check.assignVar(lhs[0], rhs[0]) // resulting in 2 or more values; otherwise we have an assignment mismatch.
check.assignVar(lhs[1], rhs[1]) if r != 1 {
check.recordCommaOkTypes(origRHS[0], rhs) check.assignError(orig_rhs, l, r)
check.useLHS(lhs...)
check.use(orig_rhs...)
return return
} }
for i, lhs := range lhs { rhs, commaOk := check.multiExpr(orig_rhs[0], l == 2)
check.assignVar(lhs, rhs[i]) r = len(rhs)
if l == r {
for i, lhs := range lhs {
check.assignVar(lhs, rhs[i])
}
if commaOk {
check.recordCommaOkTypes(orig_rhs[0], rhs)
}
return
} }
// In all other cases we have an assignment mismatch.
// Only report a mismatch error if there was no error
// on the rhs.
if rhs[0].mode != invalid {
check.assignError(orig_rhs, l, r)
}
check.useLHS(lhs...)
// orig_rhs[0] was already evaluated
} }
func (check *Checker) shortVarDecl(pos positioner, lhs, rhs []ast.Expr) { func (check *Checker) shortVarDecl(pos positioner, lhs, rhs []ast.Expr) {

View File

@ -1773,6 +1773,7 @@ func (check *Checker) expr(x *operand, e ast.Expr) {
// If allowCommaOk is set and e is a map index, comma-ok, or comma-err // If allowCommaOk is set and e is a map index, comma-ok, or comma-err
// expression, the result is a two-element list containing the value // expression, the result is a two-element list containing the value
// of e, and an untyped bool value or an error value, respectively. // of e, and an untyped bool value or an error value, respectively.
// If an error occurred, list[0] is not valid.
func (check *Checker) multiExpr(e ast.Expr, allowCommaOk bool) (list []*operand, commaOk bool) { func (check *Checker) multiExpr(e ast.Expr, allowCommaOk bool) (list []*operand, commaOk bool) {
var x operand var x operand
check.rawExpr(&x, e, nil, false) check.rawExpr(&x, e, nil, false)