1
0
mirror of https://github.com/golang/go synced 2024-09-28 21:14:28 -06:00

go/types, types2: refactor initVars

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>
This commit is contained in:
Robert Griesemer 2023-03-22 15:04:35 -07:00 committed by Gopher Robot
parent 422f4483f1
commit 8c5e8a38df
11 changed files with 188 additions and 99 deletions

View 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,33 +319,14 @@ 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) 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
}
}
if returnStmt != nil {
var at poser = returnStmt
func (check *Checker) returnError(at poser, lhs []*Var, rhs []*operand) {
l, r := len(lhs), len(rhs)
qualifier := "not enough"
if len(rhs) > len(lhs) {
at = rhs[len(lhs)].expr // report at first extra value
if r > l {
at = rhs[l] // report at first extra value
qualifier = "too many"
} else if len(rhs) > 0 {
at = rhs[len(rhs)-1].expr // report at last value
} else if r > 0 {
at = rhs[r-1] // report at last value
}
var err error_
err.code = WrongResultCount
@ -353,39 +334,89 @@ func (check *Checker) initVars(lhs []*Var, orig_rhs []syntax.Expr, returnStmt sy
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))
return
}
}
// 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) {
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)
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
}
ok := true
for i, lhs := range lhs {
if check.initVar(lhs, rhs[i], context) == nil {
ok = false
// 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 {
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]
}
}
check.use(orig_rhs...)
return
}
// avoid follow-on "declared and not used" errors if any initialization failed
if !ok {
for _, lhs := range lhs {
lhs.used = true
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
}
// 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 []syntax.Expr) {
l, r := len(lhs), len(orig_rhs)

View 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
}
}
if returnStmt != nil {
var at positioner = returnStmt
func (check *Checker) returnError(at positioner, lhs []*Var, rhs []*operand) {
l, r := len(lhs), len(rhs)
qualifier := "not enough"
if len(rhs) > len(lhs) {
at = rhs[len(lhs)].expr // report at first extra value
if r > l {
at = rhs[l] // report at first extra value
qualifier = "too many"
} else if len(rhs) > 0 {
at = rhs[len(rhs)-1].expr // report at last value
} else if r > 0 {
at = rhs[r-1] // report at last value
}
err := newErrorf(at, WrongResultCount, "%s return values", qualifier)
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)
return
}
check.assignError(origRHS, len(lhs), len(rhs))
return
}
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"
}
if commaOk {
check.initVar(lhs[0], rhs[0], context)
check.initVar(lhs[1], rhs[1], context)
check.recordCommaOkTypes(origRHS[0], rhs)
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 {
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]
}
}
check.use(orig_rhs...)
return
}
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
}
// 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)

View File

@ -8,4 +8,5 @@ package main
func main() {
s := vlong(0); // ERROR "undef"
_ = s
}

View File

@ -8,4 +8,5 @@ package main
func main() {
s := string(bug); // ERROR "undef"
_ = s
}

View File

@ -18,6 +18,7 @@ func f2() {
func f3() {
i := c // ERROR "undef"
_ = i
}
func main() {

View File

@ -10,5 +10,6 @@ func f() /* no return type */ {}
func main() {
x := f(); // ERROR "mismatch|as value|no type"
_ = x
}

View 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
}

View 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
}

View File

@ -10,4 +10,5 @@ func f() (int, bool) { return 0, true }
func main() {
x, y := f(), 2 // ERROR "multi|2-valued"
_, _ = x, y
}

View 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() {

View 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