]> Cypherpunks.ru repositories - gostls13.git/commitdiff
go/types, types2: refactor initVars
authorRobert Griesemer <gri@golang.org>
Wed, 22 Mar 2023 22:04:35 +0000 (15:04 -0700)
committerGopher Robot <gobot@golang.org>
Tue, 28 Mar 2023 18:13:13 +0000 (18:13 +0000)
As with changes in prior CLs, we don't suppress legitimate
"declared but not used" errors anymore simply because the
respective variables are used in incorrect assignments,
unrelated to the variables in question.
Adjust several (ancient) tests accordingly.

Change-Id: I5826393264d9d8085c64777a330d4efeb735dd2d
Reviewed-on: https://go-review.googlesource.com/c/go/+/478716
Reviewed-by: Robert Griesemer <gri@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Griesemer <gri@google.com>

src/cmd/compile/internal/types2/assignments.go
src/go/types/assignments.go
test/fixedbugs/bug037.go
test/fixedbugs/bug072.go
test/fixedbugs/bug091.go
test/fixedbugs/bug103.go
test/fixedbugs/bug107.go
test/fixedbugs/bug122.go
test/fixedbugs/bug175.go
test/fixedbugs/issue19012.go
test/fixedbugs/issue48558.go

index 2d6391cf59fd8be25585117946b86e3090e810de..5436c46bf149d81721004c4e14727129b66f7438 100644 (file)
@@ -305,9 +305,9 @@ func measure(x int, unit string) string {
        return fmt.Sprintf("%d %s", x, unit)
 }
 
-func (check *Checker) assignError(rhs []syntax.Expr, nvars, nvals int) {
-       vars := measure(nvars, "variable")
-       vals := measure(nvals, "value")
+func (check *Checker) assignError(rhs []syntax.Expr, l, r int) {
+       vars := measure(l, "variable")
+       vals := measure(r, "value")
        rhs0 := rhs[0]
 
        if len(rhs) == 1 {
@@ -319,73 +319,104 @@ func (check *Checker) assignError(rhs []syntax.Expr, nvars, nvals int) {
        check.errorf(rhs0, WrongAssignCount, "assignment mismatch: %s but %s", vars, vals)
 }
 
-// If returnStmt != nil, initVars is called to type-check the assignment
-// of return expressions, and returnStmt is the return statement.
+func (check *Checker) returnError(at poser, lhs []*Var, rhs []*operand) {
+       l, r := len(lhs), len(rhs)
+       qualifier := "not enough"
+       if r > l {
+               at = rhs[l] // report at first extra value
+               qualifier = "too many"
+       } else if r > 0 {
+               at = rhs[r-1] // report at last value
+       }
+       var err error_
+       err.code = WrongResultCount
+       err.errorf(at, "%s return values", qualifier)
+       err.errorf(nopos, "have %s", check.typesSummary(operandTypes(rhs), false))
+       err.errorf(nopos, "want %s", check.typesSummary(varTypes(lhs), false))
+       check.report(&err)
+}
+
+// initVars type-checks assignments of initialization expressions orig_rhs
+// to variables lhs.
+// If returnStmt is non-nil, initVars type-checks the implicit assignment
+// of result expressions orig_rhs to function result parameters lhs.
 func (check *Checker) initVars(lhs []*Var, orig_rhs []syntax.Expr, returnStmt syntax.Stmt) {
-       rhs, commaOk := check.exprList(orig_rhs, len(lhs) == 2 && returnStmt == nil)
-
-       if len(lhs) != len(rhs) {
-               // invalidate lhs
-               for _, obj := range lhs {
-                       obj.used = true // avoid declared and not used errors
-                       if obj.typ == nil {
-                               obj.typ = Typ[Invalid]
-                       }
-               }
-               // don't report an error if we already reported one
-               for _, x := range rhs {
-                       if x.mode == invalid {
-                               return
-                       }
+       context := "assignment"
+       if returnStmt != nil {
+               context = "return statement"
+       }
+
+       l, r := len(lhs), len(orig_rhs)
+
+       // If l == 1 and the rhs is a single call, for a better
+       // error message don't handle it as n:n mapping below.
+       isCall := false
+       if r == 1 {
+               _, isCall = unparen(orig_rhs[0]).(*syntax.CallExpr)
+       }
+
+       // 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 {
+               var x operand
+               for i, lhs := range lhs {
+                       check.expr(&x, orig_rhs[i])
+                       check.initVar(lhs, &x, context)
                }
+               return
+       }
+
+       // If we don't have an n:n mapping, the rhs must be a single expression
+       // resulting in 2 or more values; otherwise we have an assignment mismatch.
+       if r != 1 {
                if returnStmt != nil {
-                       var at poser = returnStmt
-                       qualifier := "not enough"
-                       if len(rhs) > len(lhs) {
-                               at = rhs[len(lhs)].expr // report at first extra value
-                               qualifier = "too many"
-                       } else if len(rhs) > 0 {
-                               at = rhs[len(rhs)-1].expr // report at last value
+                       rhs, _ := check.exprList(orig_rhs, false)
+                       check.returnError(returnStmt, lhs, rhs)
+               } else {
+                       check.assignError(orig_rhs, l, r)
+               }
+               // ensure that LHS variables have a type
+               for _, v := range lhs {
+                       if v.typ == nil {
+                               v.typ = Typ[Invalid]
                        }
-                       var err error_
-                       err.code = WrongResultCount
-                       err.errorf(at, "%s return values", qualifier)
-                       err.errorf(nopos, "have %s", check.typesSummary(operandTypes(rhs), false))
-                       err.errorf(nopos, "want %s", check.typesSummary(varTypes(lhs), false))
-                       check.report(&err)
-                       return
                }
-               check.assignError(orig_rhs, len(lhs), len(rhs))
+               check.use(orig_rhs...)
                return
        }
 
-       context := "assignment"
-       if returnStmt != nil {
-               context = "return statement"
-       }
-
-       if commaOk {
-               check.initVar(lhs[0], rhs[0], context)
-               check.initVar(lhs[1], rhs[1], context)
-               check.recordCommaOkTypes(orig_rhs[0], rhs)
+       rhs, commaOk := check.multiExpr(orig_rhs[0], l == 2 && returnStmt == nil)
+       r = len(rhs)
+       if l == r {
+               for i, lhs := range lhs {
+                       check.initVar(lhs, rhs[i], context)
+               }
+               if commaOk {
+                       check.recordCommaOkTypes(orig_rhs[0], rhs)
+               }
                return
        }
 
-       ok := true
-       for i, lhs := range lhs {
-               if check.initVar(lhs, rhs[i], context) == nil {
-                       ok = false
+       // 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 {
+               if returnStmt != nil {
+                       check.returnError(returnStmt, lhs, rhs)
+               } else {
+                       check.assignError(orig_rhs, l, r)
                }
        }
-
-       // avoid follow-on "declared and not used" errors if any initialization failed
-       if !ok {
-               for _, lhs := range lhs {
-                       lhs.used = true
+       // ensure that LHS variables have a type
+       for _, v := range lhs {
+               if v.typ == nil {
+                       v.typ = Typ[Invalid]
                }
        }
+       // orig_rhs[0] was already evaluated
 }
 
+// assignVars type-checks assignments of expressions orig_rhs to variables lhs.
 func (check *Checker) assignVars(lhs, orig_rhs []syntax.Expr) {
        l, r := len(lhs), len(orig_rhs)
 
index 05049e0a6f923aca39e936840a42b2ce1f231527..84b45f140310351023e300993d14922d6e9f1f13 100644 (file)
@@ -303,9 +303,9 @@ func measure(x int, unit string) string {
        return fmt.Sprintf("%d %s", x, unit)
 }
 
-func (check *Checker) assignError(rhs []ast.Expr, nvars, nvals int) {
-       vars := measure(nvars, "variable")
-       vals := measure(nvals, "value")
+func (check *Checker) assignError(rhs []ast.Expr, l, r int) {
+       vars := measure(l, "variable")
+       vals := measure(r, "value")
        rhs0 := rhs[0]
 
        if len(rhs) == 1 {
@@ -317,61 +317,104 @@ func (check *Checker) assignError(rhs []ast.Expr, nvars, nvals int) {
        check.errorf(rhs0, WrongAssignCount, "assignment mismatch: %s but %s", vars, vals)
 }
 
-// If returnStmt != nil, initVars is called to type-check the assignment
-// of return expressions, and returnStmt is the return statement.
-func (check *Checker) initVars(lhs []*Var, origRHS []ast.Expr, returnStmt ast.Stmt) {
-       rhs, commaOk := check.exprList(origRHS, len(lhs) == 2 && returnStmt == nil)
-
-       if len(lhs) != len(rhs) {
-               // invalidate lhs
-               for _, obj := range lhs {
-                       obj.used = true // avoid declared and not used errors
-                       if obj.typ == nil {
-                               obj.typ = Typ[Invalid]
-                       }
-               }
-               // don't report an error if we already reported one
-               for _, x := range rhs {
-                       if x.mode == invalid {
-                               return
-                       }
+func (check *Checker) returnError(at positioner, lhs []*Var, rhs []*operand) {
+       l, r := len(lhs), len(rhs)
+       qualifier := "not enough"
+       if r > l {
+               at = rhs[l] // report at first extra value
+               qualifier = "too many"
+       } else if r > 0 {
+               at = rhs[r-1] // report at last value
+       }
+       var err error_
+       err.code = WrongResultCount
+       err.errorf(at.Pos(), "%s return values", qualifier)
+       err.errorf(nopos, "have %s", check.typesSummary(operandTypes(rhs), false))
+       err.errorf(nopos, "want %s", check.typesSummary(varTypes(lhs), false))
+       check.report(&err)
+}
+
+// initVars type-checks assignments of initialization expressions orig_rhs
+// to variables lhs.
+// If returnStmt is non-nil, initVars type-checks the implicit assignment
+// of result expressions orig_rhs to function result parameters lhs.
+func (check *Checker) initVars(lhs []*Var, orig_rhs []ast.Expr, returnStmt ast.Stmt) {
+       context := "assignment"
+       if returnStmt != nil {
+               context = "return statement"
+       }
+
+       l, r := len(lhs), len(orig_rhs)
+
+       // If l == 1 and the rhs is a single call, for a better
+       // error message don't handle it as n:n mapping below.
+       isCall := false
+       if r == 1 {
+               _, isCall = unparen(orig_rhs[0]).(*ast.CallExpr)
+       }
+
+       // 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 {
+               var x operand
+               for i, lhs := range lhs {
+                       check.expr(&x, orig_rhs[i])
+                       check.initVar(lhs, &x, context)
                }
+               return
+       }
+
+       // If we don't have an n:n mapping, the rhs must be a single expression
+       // resulting in 2 or more values; otherwise we have an assignment mismatch.
+       if r != 1 {
                if returnStmt != nil {
-                       var at positioner = returnStmt
-                       qualifier := "not enough"
-                       if len(rhs) > len(lhs) {
-                               at = rhs[len(lhs)].expr // report at first extra value
-                               qualifier = "too many"
-                       } else if len(rhs) > 0 {
-                               at = rhs[len(rhs)-1].expr // report at last value
+                       rhs, _ := check.exprList(orig_rhs, false)
+                       check.returnError(returnStmt, lhs, rhs)
+               } else {
+                       check.assignError(orig_rhs, l, r)
+               }
+               // ensure that LHS variables have a type
+               for _, v := range lhs {
+                       if v.typ == nil {
+                               v.typ = Typ[Invalid]
                        }
-                       err := newErrorf(at, WrongResultCount, "%s return values", qualifier)
-                       err.errorf(nopos, "have %s", check.typesSummary(operandTypes(rhs), false))
-                       err.errorf(nopos, "want %s", check.typesSummary(varTypes(lhs), false))
-                       check.report(err)
-                       return
                }
-               check.assignError(origRHS, len(lhs), len(rhs))
+               check.use(orig_rhs...)
                return
        }
 
-       context := "assignment"
-       if returnStmt != nil {
-               context = "return statement"
-       }
-
-       if commaOk {
-               check.initVar(lhs[0], rhs[0], context)
-               check.initVar(lhs[1], rhs[1], context)
-               check.recordCommaOkTypes(origRHS[0], rhs)
+       rhs, commaOk := check.multiExpr(orig_rhs[0], l == 2 && returnStmt == nil)
+       r = len(rhs)
+       if l == r {
+               for i, lhs := range lhs {
+                       check.initVar(lhs, rhs[i], context)
+               }
+               if commaOk {
+                       check.recordCommaOkTypes(orig_rhs[0], rhs)
+               }
                return
        }
 
-       for i, lhs := range lhs {
-               check.initVar(lhs, rhs[i], context)
+       // 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 {
+               if returnStmt != nil {
+                       check.returnError(returnStmt, lhs, rhs)
+               } else {
+                       check.assignError(orig_rhs, l, r)
+               }
        }
+       // ensure that LHS variables have a type
+       for _, v := range lhs {
+               if v.typ == nil {
+                       v.typ = Typ[Invalid]
+               }
+       }
+       // orig_rhs[0] was already evaluated
 }
 
+// assignVars type-checks assignments of expressions orig_rhs to variables lhs.
 func (check *Checker) assignVars(lhs, orig_rhs []ast.Expr) {
        l, r := len(lhs), len(orig_rhs)
 
index f17fb3fd74cc0a283621d44bc800d4951d1ff4d6..ed95cacc96b970dff83e5c2fd207d35245bf93aa 100644 (file)
@@ -8,4 +8,5 @@ package main
 
 func main() {
        s := vlong(0);  // ERROR "undef"
+       _ = s
 }
index 05ad93dac2ded82d078a8a2ac903e317b4556e99..1c0c4ee12a55027827a89fc54ba25140b6fff3f4 100644 (file)
@@ -8,4 +8,5 @@ package main
 
 func main() {
        s := string(bug);  // ERROR "undef"
+       _ = s
 }
index dbb1287a151777ba14dd26b633ed9040288243c8..0e239e023ae23819c289d93008b5398f5b37b298 100644 (file)
@@ -18,6 +18,7 @@ func f2() {
 
 func f3() {
        i := c // ERROR "undef"
+       _ = i
 }
 
 func main() {
index 1cb710e36887f77754bb4cb3407c0a95c6996dd8..743a3c4b4fbe2615c66eb00624a8b6bffa7b36bd 100644 (file)
@@ -10,5 +10,6 @@ func f() /* no return type */ {}
 
 func main() {
        x := f();  // ERROR "mismatch|as value|no type"
+       _ = x
 }
 
index dcd8e9d1138f4481eb5399b7f476a70b6ed49da6..e4b9eb1e94b46c162807b196dcc7720588bda0c0 100644 (file)
@@ -11,5 +11,6 @@ func f() (os int) {
         // In the next line "os" should refer to the result variable, not
         // to the package.
         v := os.Open("", 0, 0);        // ERROR "undefined"
+        _ = v
         return 0
 }
index 5640cf263a8e8a6e4baaf863c43560f94b56987f..0d9dcd1807582b02ed82a6bf6fc2fe67a8f5a0ce 100644 (file)
@@ -9,4 +9,5 @@ package main
 func main() {
        // should allow at most 2 sizes
        a := make([]int, 10, 20, 30, 40); // ERROR "too many|expects 2 or 3 arguments; found 5"
+       _ = a
 }
index caf3168536e2a8ce3e978a280e51cded7563dcff..f19025a7a6c39f5da27b0e63f69c783d3bb16201 100644 (file)
@@ -10,4 +10,5 @@ func f() (int, bool) { return 0, true }
 
 func main() {
        x, y := f(), 2 // ERROR "multi|2-valued"
+       _, _ = x, y
 }
index 77b22360635eea769edd5132a2b35d0312e5022e..c911a9a1d06a0037588b70ba04ac941ac0b11eb8 100644 (file)
@@ -15,7 +15,7 @@ func f(x int, y uint) {
        if true {
                return "a" > 10 // ERROR "^too many arguments to return$|return with value in function with no return|no result values expected|mismatched types"
        }
-       return "gopher" == true, 10 // ERROR "^too many arguments to return$|return with value in function with no return|no result values expected|mismatched types"
+       return "gopher" == true, 10 // ERROR "^too many arguments to return$|return with value in function with no return|no result values expected|mismatched types" "too many return values"
 }
 
 func main() {
index 9ab56d9e46242932566b2a75aa658f7ae2dde63e..590fd9b7c1ad890cee2111f90fa2e28b9c3370c4 100644 (file)
@@ -41,6 +41,10 @@ func _() {
        a1 := f3()         // ERROR "assignment mismatch: 1 variable but f3 returns 3 values"
        a2, b2 := f1()     // ERROR "assignment mismatch: 2 variables but f1 returns 1 value"
        a3, b3, c3 := f2() // ERROR "assignment mismatch: 3 variables but f2 returns 2 values"
+
+       _ = a1
+       _, _ = a2, b2
+       _, _, _ = a3, b3, c3
 }
 
 type T struct{}
@@ -66,6 +70,10 @@ func _(x T) {
        a1 := x.f3()         // ERROR "assignment mismatch: 1 variable but .\.f3 returns 3 values"
        a2, b2 := x.f1()     // ERROR "assignment mismatch: 2 variables but .\.f1 returns 1 value"
        a3, b3, c3 := x.f2() // ERROR "assignment mismatch: 3 variables but .\.f2 returns 2 values"
+
+       _ = a1
+       _, _ = a2, b2
+       _, _, _ = a3, b3, c3
 }
 
 // some one-off cases