1
0
mirror of https://github.com/golang/go synced 2024-09-28 21:24:29 -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) return fmt.Sprintf("%d %s", x, unit)
} }
func (check *Checker) assignError(rhs []syntax.Expr, nvars, nvals int) { func (check *Checker) assignError(rhs []syntax.Expr, l, r int) {
vars := measure(nvars, "variable") vars := measure(l, "variable")
vals := measure(nvals, "value") vals := measure(r, "value")
rhs0 := rhs[0] rhs0 := rhs[0]
if len(rhs) == 1 { 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) check.errorf(rhs0, WrongAssignCount, "assignment mismatch: %s but %s", vars, vals)
} }
// If returnStmt != nil, initVars is called to type-check the assignment func (check *Checker) returnError(at poser, lhs []*Var, rhs []*operand) {
// of return expressions, and returnStmt is the return statement. l, r := len(lhs), len(rhs)
func (check *Checker) initVars(lhs []*Var, orig_rhs []syntax.Expr, returnStmt syntax.Stmt) { qualifier := "not enough"
rhs, commaOk := check.exprList(orig_rhs, len(lhs) == 2 && returnStmt == nil) if r > l {
at = rhs[l] // report at first extra value
if len(lhs) != len(rhs) { qualifier = "too many"
// invalidate lhs } else if r > 0 {
for _, obj := range lhs { at = rhs[r-1] // report at last value
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
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
}
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))
return
} }
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) {
context := "assignment" context := "assignment"
if returnStmt != nil { if returnStmt != nil {
context = "return statement" context = "return statement"
} }
if commaOk { l, r := len(lhs), len(orig_rhs)
check.initVar(lhs[0], rhs[0], context)
check.initVar(lhs[1], rhs[1], context) // If l == 1 and the rhs is a single call, for a better
check.recordCommaOkTypes(orig_rhs[0], rhs) // 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 return
} }
ok := true // If we don't have an n:n mapping, the rhs must be a single expression
for i, lhs := range lhs { // resulting in 2 or more values; otherwise we have an assignment mismatch.
if check.initVar(lhs, rhs[i], context) == nil { if r != 1 {
ok = false 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 rhs, commaOk := check.multiExpr(orig_rhs[0], l == 2 && returnStmt == nil)
if !ok { r = len(rhs)
for _, lhs := range lhs { if l == r {
lhs.used = true 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) { func (check *Checker) assignVars(lhs, orig_rhs []syntax.Expr) {
l, r := len(lhs), len(orig_rhs) 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) return fmt.Sprintf("%d %s", x, unit)
} }
func (check *Checker) assignError(rhs []ast.Expr, nvars, nvals int) { func (check *Checker) assignError(rhs []ast.Expr, l, r int) {
vars := measure(nvars, "variable") vars := measure(l, "variable")
vals := measure(nvals, "value") vals := measure(r, "value")
rhs0 := rhs[0] rhs0 := rhs[0]
if len(rhs) == 1 { 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) check.errorf(rhs0, WrongAssignCount, "assignment mismatch: %s but %s", vars, vals)
} }
// If returnStmt != nil, initVars is called to type-check the assignment func (check *Checker) returnError(at positioner, lhs []*Var, rhs []*operand) {
// of return expressions, and returnStmt is the return statement. l, r := len(lhs), len(rhs)
func (check *Checker) initVars(lhs []*Var, origRHS []ast.Expr, returnStmt ast.Stmt) { qualifier := "not enough"
rhs, commaOk := check.exprList(origRHS, len(lhs) == 2 && returnStmt == nil) if r > l {
at = rhs[l] // report at first extra value
if len(lhs) != len(rhs) { qualifier = "too many"
// invalidate lhs } else if r > 0 {
for _, obj := range lhs { at = rhs[r-1] // report at last value
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
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
}
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))
return
} }
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" context := "assignment"
if returnStmt != nil { if returnStmt != nil {
context = "return statement" context = "return statement"
} }
if commaOk { l, r := len(lhs), len(orig_rhs)
check.initVar(lhs[0], rhs[0], context)
check.initVar(lhs[1], rhs[1], context) // If l == 1 and the rhs is a single call, for a better
check.recordCommaOkTypes(origRHS[0], rhs) // 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 return
} }
for i, lhs := range lhs { // If we don't have an n:n mapping, the rhs must be a single expression
check.initVar(lhs, rhs[i], context) // 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) { func (check *Checker) assignVars(lhs, orig_rhs []ast.Expr) {
l, r := len(lhs), len(orig_rhs) l, r := len(lhs), len(orig_rhs)

View File

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

View File

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

View File

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

View File

@ -10,5 +10,6 @@ func f() /* no return type */ {}
func main() { func main() {
x := f(); // ERROR "mismatch|as value|no type" 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 // In the next line "os" should refer to the result variable, not
// to the package. // to the package.
v := os.Open("", 0, 0); // ERROR "undefined" v := os.Open("", 0, 0); // ERROR "undefined"
_ = v
return 0 return 0
} }

View File

@ -9,4 +9,5 @@ package main
func main() { func main() {
// should allow at most 2 sizes // should allow at most 2 sizes
a := make([]int, 10, 20, 30, 40); // ERROR "too many|expects 2 or 3 arguments; found 5" 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() { func main() {
x, y := f(), 2 // ERROR "multi|2-valued" x, y := f(), 2 // ERROR "multi|2-valued"
_, _ = x, y
} }

View File

@ -15,7 +15,7 @@ func f(x int, y uint) {
if true { 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 "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() { func main() {

View File

@ -41,6 +41,10 @@ func _() {
a1 := f3() // ERROR "assignment mismatch: 1 variable but f3 returns 3 values" 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" 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" a3, b3, c3 := f2() // ERROR "assignment mismatch: 3 variables but f2 returns 2 values"
_ = a1
_, _ = a2, b2
_, _, _ = a3, b3, c3
} }
type T struct{} type T struct{}
@ -66,6 +70,10 @@ func _(x T) {
a1 := x.f3() // ERROR "assignment mismatch: 1 variable but .\.f3 returns 3 values" 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" 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" 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 // some one-off cases