1
0
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:
Robert Griesemer 2013-10-24 15:20:03 -07:00
parent 96bdcd22e3
commit 69f5b543df
3 changed files with 57 additions and 16 deletions

View File

@ -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)

View File

@ -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 {

View File

@ -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