cmd/compile: restore original assignment error messages

When used with the compiler, types2 will report assignment error
messages that closely match what the compiler type checker (types1)
produces.

Also, mark lhs variables as used in invalid variable initializations
to avoid a class of follow-on errors.

Fixes #48558.

Change-Id: I92d1de006c66b3a2364bb1bea773a312963afe75
Reviewed-on: https://go-review.googlesource.com/c/go/+/351669
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Robert Griesemer 2021-09-22 17:08:00 -07:00
parent 2fc7697da4
commit 0626ac064d
6 changed files with 124 additions and 9 deletions

View file

@ -6,7 +6,10 @@
package types2
import "cmd/compile/internal/syntax"
import (
"cmd/compile/internal/syntax"
"fmt"
)
// assignment reports whether x can be assigned to a variable of type T,
// if necessary by attempting to convert untyped values to the appropriate
@ -236,6 +239,28 @@ func (check *Checker) assignVar(lhs syntax.Expr, x *operand) Type {
return x.typ
}
func (check *Checker) assignError(rhs []syntax.Expr, nvars, nvals int) {
measure := func(x int, unit string) string {
s := fmt.Sprintf("%d %s", x, unit)
if x != 1 {
s += "s"
}
return s
}
vars := measure(nvars, "variable")
vals := measure(nvals, "value")
rhs0 := rhs[0]
if len(rhs) == 1 {
if call, _ := unparen(rhs0).(*syntax.CallExpr); call != nil {
check.errorf(rhs0, "assignment mismatch: %s but %s returns %s", vars, call.Fun, vals)
return
}
}
check.errorf(rhs0, "assignment mismatch: %s but %s", vars, vals)
}
// If returnPos is valid, initVars is called to type-check the assignment of
// return expressions, and returnPos is the position of the return statement.
func (check *Checker) initVars(lhs []*Var, orig_rhs []syntax.Expr, returnPos syntax.Pos) {
@ -244,6 +269,7 @@ func (check *Checker) initVars(lhs []*Var, orig_rhs []syntax.Expr, returnPos syn
if len(lhs) != len(rhs) {
// invalidate lhs
for _, obj := range lhs {
obj.used = true // avoid declared but not used errors
if obj.typ == nil {
obj.typ = Typ[Invalid]
}
@ -258,7 +284,11 @@ func (check *Checker) initVars(lhs []*Var, orig_rhs []syntax.Expr, returnPos syn
check.errorf(returnPos, "wrong number of return values (want %d, got %d)", len(lhs), len(rhs))
return
}
check.errorf(rhs[0], "cannot initialize %d variables with %d values", len(lhs), len(rhs))
if check.conf.CompilerErrorMessages {
check.assignError(orig_rhs, len(lhs), len(rhs))
} else {
check.errorf(rhs[0], "cannot initialize %d variables with %d values", len(lhs), len(rhs))
}
return
}
@ -292,7 +322,11 @@ func (check *Checker) assignVars(lhs, orig_rhs []syntax.Expr) {
return
}
}
check.errorf(rhs[0], "cannot assign %d values to %d variables", len(rhs), len(lhs))
if check.conf.CompilerErrorMessages {
check.assignError(orig_rhs, len(lhs), len(rhs))
} else {
check.errorf(rhs[0], "cannot assign %d values to %d variables", len(rhs), len(lhs))
}
return
}

View file

@ -1689,7 +1689,11 @@ func (check *Checker) singleValue(x *operand) {
// tuple types are never named - no need for underlying type below
if t, ok := x.typ.(*Tuple); ok {
assert(t.Len() != 1)
check.errorf(x, "%d-valued %s where single value is expected", t.Len(), x)
if check.conf.CompilerErrorMessages {
check.errorf(x, "multiple-value %s in single-value context", x)
} else {
check.errorf(x, "%d-valued %s where single value is expected", t.Len(), x)
}
x.mode = invalid
}
}

View file

@ -15,5 +15,5 @@ func f() {
func g2() ([]byte, []byte) { return nil, nil }
func f2() {
g2()[:] // ERROR "multiple-value g2.. in single-value context|attempt to slice object that is not|2\-valued g"
g2()[:] // ERROR "multiple-value g2.* in single-value context|attempt to slice object that is not|2\-valued g"
}

View file

@ -6,11 +6,11 @@
package p
var x int = three() // ERROR "assignment mismatch: 1 variable but three returns 3 values|multiple-value function call in single-value context|3\-valued"
var x int = three() // ERROR "assignment mismatch: 1 variable but three returns 3 values|multiple-value function call in single-value context|multiple-value "
func f() {
var _ int = three() // ERROR "assignment mismatch: 1 variable but three returns 3 values|multiple-value function call in single-value context|3\-valued"
var a int = three() // ERROR "assignment mismatch: 1 variable but three returns 3 values|multiple-value function call in single-value context|3\-valued"
var _ int = three() // ERROR "assignment mismatch: 1 variable but three returns 3 values|multiple-value function call in single-value context|multiple-value "
var a int = three() // ERROR "assignment mismatch: 1 variable but three returns 3 values|multiple-value function call in single-value context|multiple-value "
a = three() // ERROR "assignment mismatch: 1 variable but three returns 3 values|multiple-value function call in single-value context|cannot assign"
b := three() // ERROR "assignment mismatch: 1 variable but three returns 3 values|single variable set to multiple-value|multiple-value function call in single-value context|cannot initialize"
_, _ = a, b

View file

@ -6,7 +6,7 @@
package main
var a = twoResults() // ERROR "assignment mismatch: 1 variable but twoResults returns 2 values|2\-valued"
var a = twoResults() // ERROR "assignment mismatch: 1 variable but twoResults returns 2 values|multiple-value twoResults\(\) .*in single-value context"
var b, c, d = twoResults() // ERROR "assignment mismatch: 3 variables but twoResults returns 2 values|cannot initialize"
var e, f = oneResult() // ERROR "assignment mismatch: 2 variables but oneResult returns 1 value|cannot initialize"

View file

@ -0,0 +1,77 @@
// errorcheck
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package p
func _(a, b, c int) {
_ = a
_ = a, b // ERROR "assignment mismatch: 1 variable but 2 values"
_ = a, b, c // ERROR "assignment mismatch: 1 variable but 3 values"
_, _ = a // ERROR "assignment mismatch: 2 variables but 1 value"
_, _ = a, b
_, _ = a, b, c // ERROR "assignment mismatch: 2 variables but 3 values"
_, _, _ = a // ERROR "assignment mismatch: 3 variables but 1 value"
_, _, _ = a, b // ERROR "assignment mismatch: 3 variables but 2 values"
_, _, _ = a, b, c
}
func f1() int
func f2() (int, int)
func f3() (int, int, int)
func _() {
_ = f1()
_ = f2() // ERROR "assignment mismatch: 1 variable but f2 returns 2 values"
_ = f3() // ERROR "assignment mismatch: 1 variable but f3 returns 3 values"
_, _ = f1() // ERROR "assignment mismatch: 2 variables but f1 returns 1 value"
_, _ = f2()
_, _ = f3() // ERROR "assignment mismatch: 2 variables but f3 returns 3 values"
_, _, _ = f1() // ERROR "assignment mismatch: 3 variables but f1 returns 1 value"
_, _, _ = f2() // ERROR "assignment mismatch: 3 variables but f2 returns 2 values"
_, _, _ = f3()
// test just a few := cases as they use the same code as the = case
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"
}
type T struct{}
func (T) f1() int
func (T) f2() (int, int)
func (T) f3() (int, int, int)
func _(x T) {
_ = x.f1()
_ = x.f2() // ERROR "assignment mismatch: 1 variable but .\.f2 returns 2 values"
_ = x.f3() // ERROR "assignment mismatch: 1 variable but .\.f3 returns 3 values"
_, _ = x.f1() // ERROR "assignment mismatch: 2 variables but .\.f1 returns 1 value"
_, _ = x.f2()
_, _ = x.f3() // ERROR "assignment mismatch: 2 variables but .\.f3 returns 3 values"
_, _, _ = x.f1() // ERROR "assignment mismatch: 3 variables but .\.f1 returns 1 value"
_, _, _ = x.f2() // ERROR "assignment mismatch: 3 variables but .\.f2 returns 2 values"
_, _, _ = x.f3()
// test just a few := cases as they use the same code as the = case
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"
}
// some one-off cases
func _() {
_ = (f2)
_ = f1(), 2 // ERROR "assignment mismatch: 1 variable but 2 values"
_, _ = (f1()), f2() // ERROR "multiple-value f2\(\) .*in single-value context"
_, _, _ = f3(), 3 // ERROR "assignment mismatch: 3 variables but 2 values|multiple-value f3\(\) .*in single-value context"
}