forked from vikunja/vikunja
76 lines
2.1 KiB
Go
76 lines
2.1 KiB
Go
package typeutil
|
|
|
|
import (
|
|
"go/types"
|
|
)
|
|
|
|
// Identical reports whether x and y are identical types.
|
|
// Unlike types.Identical, receivers of Signature types are not ignored.
|
|
// Unlike types.Identical, interfaces are compared via pointer equality (except for the empty interface, which gets deduplicated).
|
|
// Unlike types.Identical, structs are compared via pointer equality.
|
|
func Identical(x, y types.Type) (ret bool) {
|
|
if !types.Identical(x, y) {
|
|
return false
|
|
}
|
|
|
|
switch x := x.(type) {
|
|
case *types.Struct:
|
|
y, ok := y.(*types.Struct)
|
|
if !ok {
|
|
// should be impossible
|
|
return true
|
|
}
|
|
return x == y
|
|
case *types.Interface:
|
|
// The issue with interfaces, typeutil.Map and types.Identical
|
|
//
|
|
// types.Identical, when comparing two interfaces, only looks at the set
|
|
// of all methods, not differentiating between implicit (embedded) and
|
|
// explicit methods.
|
|
//
|
|
// When we see the following two types, in source order
|
|
//
|
|
// type I1 interface { foo() }
|
|
// type I2 interface { I1 }
|
|
//
|
|
// then we will first correctly process I1 and its underlying type. When
|
|
// we get to I2, we will see that its underlying type is identical to
|
|
// that of I1 and not process it again. This, however, means that we will
|
|
// not record the fact that I2 embeds I1. If only I2 is reachable via the
|
|
// graph root, then I1 will not be considered used.
|
|
//
|
|
// We choose to be lazy and compare interfaces by their
|
|
// pointers. This will obviously miss identical interfaces,
|
|
// but this only has a runtime cost, it doesn't affect
|
|
// correctness.
|
|
y, ok := y.(*types.Interface)
|
|
if !ok {
|
|
// should be impossible
|
|
return true
|
|
}
|
|
if x.NumEmbeddeds() == 0 &&
|
|
y.NumEmbeddeds() == 0 &&
|
|
x.NumMethods() == 0 &&
|
|
y.NumMethods() == 0 {
|
|
// all truly empty interfaces are the same
|
|
return true
|
|
}
|
|
return x == y
|
|
case *types.Signature:
|
|
y, ok := y.(*types.Signature)
|
|
if !ok {
|
|
// should be impossible
|
|
return true
|
|
}
|
|
if x.Recv() == y.Recv() {
|
|
return true
|
|
}
|
|
if x.Recv() == nil || y.Recv() == nil {
|
|
return false
|
|
}
|
|
return Identical(x.Recv().Type(), y.Recv().Type())
|
|
default:
|
|
return true
|
|
}
|
|
}
|