Skip to content

Commit

Permalink
check: fix false positive when an interface implements a sum type
Browse files Browse the repository at this point in the history
Sometimes it is possible for a sum type interface to be implemented by
another interface. For example, we have a sum type called T including
3 variants, A, B, C. We also have an U interface embedding T and thus
implementing T. Assuming that B and C implement U, a type switch
statement can be considered exhaustive if all of A, B, C are listed.
It should also be considered exhaustive if only A and U are listed.

However, go-sumtype does not distinguish between concrete and interface
types. It fails in both cases, requiring all of A, B, C, U to be listed.
It is unlikely to code to be written in this way. U already covers B and
C, and it is unnecessary to list them in a type switch statement. This
commit fixes the problem by handling interfaces specially.

Closes: BurntSushi#1
Closes: BurntSushi#3
  • Loading branch information
lantw44 committed Apr 17, 2019
1 parent fcb4a62 commit 25a5a58
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 0 deletions.
44 changes: 44 additions & 0 deletions check_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,50 @@ func main() {
assert.Len(t, errs, 0)
}

// TestNoMissingInterface tests that we correctly detect exhaustive case when
// there is an interface which implements the interface we are going to check.
func TestNoMissingInterface(t *testing.T) {
code := `
package main
//go-sumtype:decl T
type T interface {
sealedT()
}
type A struct {}
func (a *A) sealedT() {}
type U interface {
T
sealedU()
}
type B struct {}
func (b *B) sealedT() {}
func (b *B) sealedU() {}
type C struct {}
func (c *C) sealedT() {}
func (c *C) sealedU() {}
func main() {
switch T(nil).(type) {
case *A, *B, *C:
}
switch T(nil).(type) {
case *A, U:
}
}
`
tmpdir, pkgs := setupPackages(t, code)
defer teardownPackage(t, tmpdir)

errs := run(pkgs)
assert.Len(t, errs, 0)
}

// TestNotSealed tests that we report an error if one tries to declare a sum
// type with an unsealed interface.
func TestNotSealed(t *testing.T) {
Expand Down
9 changes: 9 additions & 0 deletions def.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ func newSumTypeDef(pkg *types.Package, decl sumTypeDecl) (*sumTypeDef, error) {
if types.Identical(ty.Underlying(), iface) {
continue
}
_, ok = ty.Underlying().(*types.Interface)
if ok {
continue
}
if types.Implements(ty, iface) || types.Implements(types.NewPointer(ty), iface) {
def.Variants = append(def.Variants, obj)
}
Expand All @@ -131,6 +135,11 @@ func (def *sumTypeDef) missing(tys []types.Type) []types.Object {
if types.Identical(varty, ty) {
found = true
}
if iface, ok := ty.Underlying().(*types.Interface); ok {
if types.Implements(varty, iface) || types.Implements(types.NewPointer(varty), iface) {
found = true
}
}
}
if !found {
missing = append(missing, v)
Expand Down

0 comments on commit 25a5a58

Please sign in to comment.