mirror of
https://github.com/golang/go
synced 2024-11-19 01:54:39 -07:00
go.tools/go/types: check for duplicate types in type switches
R=adonovan CC=golang-dev https://golang.org/cl/16860043
This commit is contained in:
parent
96bdcd22e3
commit
69f5b543df
@ -119,14 +119,12 @@ func TestStdtest(t *testing.T) {
|
|||||||
testTestDir(t, filepath.Join(runtime.GOROOT(), "test"),
|
testTestDir(t, filepath.Join(runtime.GOROOT(), "test"),
|
||||||
"cmplxdivide.go", // also needs file cmplxdivide1.go - ignore
|
"cmplxdivide.go", // also needs file cmplxdivide1.go - ignore
|
||||||
"mapnan.go", "sigchld.go", // don't work on Windows; testTestDir should consult build tags
|
"mapnan.go", "sigchld.go", // don't work on Windows; testTestDir should consult build tags
|
||||||
"typeswitch2.go", // TODO(gri) implement duplicate checking in type switches
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStdfixed(t *testing.T) {
|
func TestStdfixed(t *testing.T) {
|
||||||
testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "fixedbugs"),
|
testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "fixedbugs"),
|
||||||
"bug165.go", // TODO(gri) isComparable not working for incomplete struct type
|
"bug165.go", // TODO(gri) isComparable not working for incomplete struct type
|
||||||
"bug200.go", // TODO(gri) complete duplicate checking in type switches
|
|
||||||
"bug223.go", "bug413.go", "bug459.go", // TODO(gri) complete initialization checks
|
"bug223.go", "bug413.go", "bug459.go", // TODO(gri) complete initialization checks
|
||||||
"bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore
|
"bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore
|
||||||
"issue3924.go", // TODO(gri) && and || produce bool result (not untyped bool)
|
"issue3924.go", // TODO(gri) && and || produce bool result (not untyped bool)
|
||||||
|
@ -121,6 +121,31 @@ func (check *checker) caseValues(x operand /* copy argument (not *operand!) */,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (check *checker) caseTypes(x *operand, xtyp *Interface, types []ast.Expr, seen map[Type]token.Pos) (T Type) {
|
||||||
|
L:
|
||||||
|
for _, e := range types {
|
||||||
|
T = check.typOrNil(e)
|
||||||
|
if T == Typ[Invalid] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// complain about duplicate types
|
||||||
|
// TODO(gri) use a type hash to avoid quadratic algorithm
|
||||||
|
for t, pos := range seen {
|
||||||
|
if T == nil && t == nil || T != nil && t != nil && IsIdentical(T, t) {
|
||||||
|
// talk about "case" rather than "type" because of nil case
|
||||||
|
check.errorf(e.Pos(), "duplicate case in type switch")
|
||||||
|
check.errorf(pos, "previous case %s", T)
|
||||||
|
continue L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
seen[T] = e.Pos()
|
||||||
|
if T != nil {
|
||||||
|
check.typeAssertion(e.Pos(), x, xtyp, T)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// stmt typechecks statement s.
|
// stmt typechecks statement s.
|
||||||
func (check *checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
func (check *checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
||||||
// statements cannot use iota in general
|
// statements cannot use iota in general
|
||||||
@ -397,19 +422,15 @@ func (check *checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
|||||||
check.multipleDefaults(s.Body.List)
|
check.multipleDefaults(s.Body.List)
|
||||||
|
|
||||||
var lhsVars []*Var // set of implicitly declared lhs variables
|
var lhsVars []*Var // set of implicitly declared lhs variables
|
||||||
|
seen := make(map[Type]token.Pos) // map of seen types to positions
|
||||||
for _, s := range s.Body.List {
|
for _, s := range s.Body.List {
|
||||||
clause, _ := s.(*ast.CaseClause)
|
clause, _ := s.(*ast.CaseClause)
|
||||||
if clause == nil {
|
if clause == nil {
|
||||||
continue // error reported before
|
check.invalidAST(s.Pos(), "incorrect type switch case")
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
// Check each type in this type switch case.
|
// Check each type in this type switch case.
|
||||||
var T Type
|
T := check.caseTypes(&x, xtyp, clause.List, seen)
|
||||||
for _, expr := range clause.List {
|
|
||||||
T = check.typOrNil(expr)
|
|
||||||
if T != nil && T != Typ[Invalid] {
|
|
||||||
check.typeAssertion(expr.Pos(), &x, xtyp, T)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
check.openScope(clause)
|
check.openScope(clause)
|
||||||
// If lhs exists, declare a corresponding variable in the case-local scope if necessary.
|
// If lhs exists, declare a corresponding variable in the case-local scope if necessary.
|
||||||
if lhs != nil {
|
if lhs != nil {
|
||||||
|
22
go/types/testdata/stmt0.src
vendored
22
go/types/testdata/stmt0.src
vendored
@ -535,6 +535,28 @@ func typeswitch2() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func typeswitch3(x interface{}) {
|
||||||
|
switch x.(type) {
|
||||||
|
case int /* ERROR previous case int */ :
|
||||||
|
case float64:
|
||||||
|
case int /* ERROR duplicate case */ :
|
||||||
|
}
|
||||||
|
|
||||||
|
switch x.(type) {
|
||||||
|
case nil /* ERROR previous case <nil> */ /* ERROR previous case <nil> */ :
|
||||||
|
case int:
|
||||||
|
case nil /* ERROR duplicate case */ , nil /* ERROR duplicate case */ :
|
||||||
|
}
|
||||||
|
|
||||||
|
type F func(int)
|
||||||
|
switch x.(type) {
|
||||||
|
case nil:
|
||||||
|
case int, func /* ERROR previous case */ (int):
|
||||||
|
case float32, func /* ERROR duplicate case */ (x int):
|
||||||
|
case F:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func rangeloops1() {
|
func rangeloops1() {
|
||||||
var (
|
var (
|
||||||
x int
|
x int
|
||||||
|
Loading…
Reference in New Issue
Block a user