mirror of
https://github.com/golang/go
synced 2024-11-26 01:07:57 -07:00
go/types, types2: typecheck cases even if switch expression is invalid
Rather than returning right away when the switch expression is invalid, continue type checking the type switch case. The code was already written to be able to deal with an invalid switch expression but it returned early nevertheless. Remove the early return and rewrite the switch expression test slightly to better control the scope of the x operand, leading to cleaner code. In the process replace a tricky use of the x operand with a use of the sx operand (plus guard, since sx may be nil if invalid). Fixes #67962. Change-Id: I1dc08d10078753c68449637622beb4018ed23803 Reviewed-on: https://go-review.googlesource.com/c/go/+/592555 Reviewed-by: Robert Findley <rfindley@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Robert Griesemer <gri@google.com> Auto-Submit: Robert Griesemer <gri@google.com>
This commit is contained in:
parent
dbe03e4831
commit
69e7b2bcd6
@ -282,7 +282,11 @@ func (check *Checker) isNil(e syntax.Expr) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the type switch expression is invalid, x is nil.
|
// caseTypes typechecks the type expressions of a type case, checks for duplicate types
|
||||||
|
// using the seen map, and verifies that each type is valid with respect to the type of
|
||||||
|
// the operand x in the type switch clause. If the type switch expression is invalid, x
|
||||||
|
// must be nil. The result is the type of the last type expression; it is nil if the
|
||||||
|
// expression denotes the predeclared nil.
|
||||||
func (check *Checker) caseTypes(x *operand, types []syntax.Expr, seen map[Type]syntax.Expr) (T Type) {
|
func (check *Checker) caseTypes(x *operand, types []syntax.Expr, seen map[Type]syntax.Expr) (T Type) {
|
||||||
var dummy operand
|
var dummy operand
|
||||||
L:
|
L:
|
||||||
@ -739,21 +743,18 @@ func (check *Checker) typeSwitchStmt(inner stmtContext, s *syntax.SwitchStmt, gu
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check rhs
|
// check rhs
|
||||||
var x operand
|
|
||||||
check.expr(nil, &x, guard.X)
|
|
||||||
if x.mode == invalid {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(gri) we may want to permit type switches on type parameter values at some point
|
|
||||||
var sx *operand // switch expression against which cases are compared against; nil if invalid
|
var sx *operand // switch expression against which cases are compared against; nil if invalid
|
||||||
if isTypeParam(x.typ) {
|
{
|
||||||
check.errorf(&x, InvalidTypeSwitch, "cannot use type switch on type parameter value %s", &x)
|
var x operand
|
||||||
} else {
|
check.expr(nil, &x, guard.X)
|
||||||
if _, ok := under(x.typ).(*Interface); ok {
|
if x.mode != invalid {
|
||||||
sx = &x
|
if isTypeParam(x.typ) {
|
||||||
} else {
|
check.errorf(&x, InvalidTypeSwitch, "cannot use type switch on type parameter value %s", &x)
|
||||||
check.errorf(&x, InvalidTypeSwitch, "%s is not an interface", &x)
|
} else if IsInterface(x.typ) {
|
||||||
|
sx = &x
|
||||||
|
} else {
|
||||||
|
check.errorf(&x, InvalidTypeSwitch, "%s is not an interface", &x)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -782,7 +783,10 @@ func (check *Checker) typeSwitchStmt(inner stmtContext, s *syntax.SwitchStmt, gu
|
|||||||
// exactly one type, the variable has that type; otherwise, the variable
|
// exactly one type, the variable has that type; otherwise, the variable
|
||||||
// has the type of the expression in the TypeSwitchGuard."
|
// has the type of the expression in the TypeSwitchGuard."
|
||||||
if len(cases) != 1 || T == nil {
|
if len(cases) != 1 || T == nil {
|
||||||
T = x.typ
|
T = Typ[Invalid]
|
||||||
|
if sx != nil {
|
||||||
|
T = sx.typ
|
||||||
|
}
|
||||||
}
|
}
|
||||||
obj := NewVar(lhs.Pos(), check.pkg, lhs.Value, T)
|
obj := NewVar(lhs.Pos(), check.pkg, lhs.Value, T)
|
||||||
// TODO(mdempsky): Just use clause.Colon? Why did I even suggest
|
// TODO(mdempsky): Just use clause.Colon? Why did I even suggest
|
||||||
|
@ -279,7 +279,11 @@ func (check *Checker) isNil(e ast.Expr) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the type switch expression is invalid, x is nil.
|
// caseTypes typechecks the type expressions of a type case, checks for duplicate types
|
||||||
|
// using the seen map, and verifies that each type is valid with respect to the type of
|
||||||
|
// the operand x in the type switch clause. If the type switch expression is invalid, x
|
||||||
|
// must be nil. The result is the type of the last type expression; it is nil if the
|
||||||
|
// expression denotes the predeclared nil.
|
||||||
func (check *Checker) caseTypes(x *operand, types []ast.Expr, seen map[Type]ast.Expr) (T Type) {
|
func (check *Checker) caseTypes(x *operand, types []ast.Expr, seen map[Type]ast.Expr) (T Type) {
|
||||||
var dummy operand
|
var dummy operand
|
||||||
L:
|
L:
|
||||||
@ -687,20 +691,19 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
|||||||
check.error(s, InvalidSyntaxTree, "incorrect form of type switch guard")
|
check.error(s, InvalidSyntaxTree, "incorrect form of type switch guard")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var x operand
|
|
||||||
check.expr(nil, &x, expr.X)
|
|
||||||
if x.mode == invalid {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// TODO(gri) we may want to permit type switches on type parameter values at some point
|
|
||||||
var sx *operand // switch expression against which cases are compared against; nil if invalid
|
var sx *operand // switch expression against which cases are compared against; nil if invalid
|
||||||
if isTypeParam(x.typ) {
|
{
|
||||||
check.errorf(&x, InvalidTypeSwitch, "cannot use type switch on type parameter value %s", &x)
|
var x operand
|
||||||
} else {
|
check.expr(nil, &x, expr.X)
|
||||||
if _, ok := under(x.typ).(*Interface); ok {
|
if x.mode != invalid {
|
||||||
sx = &x
|
if isTypeParam(x.typ) {
|
||||||
} else {
|
check.errorf(&x, InvalidTypeSwitch, "cannot use type switch on type parameter value %s", &x)
|
||||||
check.errorf(&x, InvalidTypeSwitch, "%s is not an interface", &x)
|
} else if IsInterface(x.typ) {
|
||||||
|
sx = &x
|
||||||
|
} else {
|
||||||
|
check.errorf(&x, InvalidTypeSwitch, "%s is not an interface", &x)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -725,7 +728,10 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
|||||||
// exactly one type, the variable has that type; otherwise, the variable
|
// exactly one type, the variable has that type; otherwise, the variable
|
||||||
// has the type of the expression in the TypeSwitchGuard."
|
// has the type of the expression in the TypeSwitchGuard."
|
||||||
if len(clause.List) != 1 || T == nil {
|
if len(clause.List) != 1 || T == nil {
|
||||||
T = x.typ
|
T = Typ[Invalid]
|
||||||
|
if sx != nil {
|
||||||
|
T = sx.typ
|
||||||
|
}
|
||||||
}
|
}
|
||||||
obj := NewVar(lhs.Pos(), check.pkg, lhs.Name, T)
|
obj := NewVar(lhs.Pos(), check.pkg, lhs.Name, T)
|
||||||
scopePos := clause.Pos() + token.Pos(len("default")) // for default clause (len(List) == 0)
|
scopePos := clause.Pos() + token.Pos(len("default")) // for default clause (len(List) == 0)
|
||||||
|
26
src/internal/types/testdata/fixedbugs/issue67962.go
vendored
Normal file
26
src/internal/types/testdata/fixedbugs/issue67962.go
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Copyright 2024 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
|
||||||
|
|
||||||
|
// No `"fmt" imported and not used` error below.
|
||||||
|
// The switch cases must be typechecked even
|
||||||
|
// though the switch expression is invalid.
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
x := 1
|
||||||
|
for e := range x.m /* ERROR "x.m undefined (type int has no field or method m)" */ () {
|
||||||
|
switch e.(type) {
|
||||||
|
case int:
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t := x /* ERROR "not an interface" */ .(type) {
|
||||||
|
case int, string:
|
||||||
|
_ = t
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user