package apidiff
import (
)
func ( *differ) ( *types.TypeName, , types.Type) {
switch old := .(type) {
case *types.Interface:
if , := .(*types.Interface); {
.checkCompatibleInterface(, , )
return
}
case *types.Struct:
if , := .(*types.Struct); {
.checkCompatibleStruct(, , )
return
}
case *types.Chan:
if , := .(*types.Chan); {
.checkCompatibleChan(, , )
return
}
case *types.Basic:
if , := .(*types.Basic); {
.checkCompatibleBasic(, , )
return
}
case *types.Named:
panic("unreachable")
default:
.checkCorrespondence(, "", , )
return
}
.typeChanged(, "", , )
}
func ( *differ) ( *types.TypeName, , *types.Chan) {
.checkCorrespondence(, ", element type", .Elem(), .Elem())
if .Dir() != .Dir() {
if .Dir() == types.SendRecv {
.compatible(, "", "removed direction")
} else {
.incompatible(, "", "changed direction")
}
}
}
func ( *differ) ( *types.TypeName, , *types.Basic) {
if .Kind() == .Kind() {
return
}
if compatibleBasics[[2]types.BasicKind{.Kind(), .Kind()}] {
.compatible(, "", "changed from %s to %s", , )
} else {
.typeChanged(, "", , )
}
}
var compatibleBasics = map[[2]types.BasicKind]bool{
{types.Uint8, types.Uint16}: true,
{types.Uint8, types.Uint32}: true,
{types.Uint8, types.Uint}: true,
{types.Uint8, types.Uint64}: true,
{types.Uint16, types.Uint32}: true,
{types.Uint16, types.Uint}: true,
{types.Uint16, types.Uint64}: true,
{types.Uint32, types.Uint}: true,
{types.Uint32, types.Uint64}: true,
{types.Uint, types.Uint64}: true,
{types.Int8, types.Int16}: true,
{types.Int8, types.Int32}: true,
{types.Int8, types.Int}: true,
{types.Int8, types.Int64}: true,
{types.Int16, types.Int32}: true,
{types.Int16, types.Int}: true,
{types.Int16, types.Int64}: true,
{types.Int32, types.Int}: true,
{types.Int32, types.Int64}: true,
{types.Int, types.Int64}: true,
{types.Float32, types.Float64}: true,
{types.Complex64, types.Complex128}: true,
}
func ( *differ) ( *types.TypeName, , *types.Interface) {
if unexportedMethod() != nil {
.checkMethodSet(, , , additionsCompatible)
} else {
.checkMethodSet(, , , additionsIncompatible)
if := unexportedMethod(); != nil {
.incompatible(, .Name(), "added unexported method")
}
}
}
func ( *types.Interface) *types.Func {
for := 0; < .NumMethods(); ++ {
if := .Method(); !.Exported() {
return
}
}
return nil
}
func ( *differ) ( types.Object, , *types.Struct) {
.checkCompatibleObjectSets(, exportedFields(), exportedFields())
.checkCompatibleObjectSets(, exportedSelectableFields(), exportedSelectableFields())
if types.Comparable() && !types.Comparable() {
.incompatible(, "", "old is comparable, new is not")
}
}
func ( *types.Struct) map[string]types.Object {
:= map[string]types.Object{}
for := 0; < .NumFields(); ++ {
:= .Field()
if .Exported() {
[.Name()] =
}
}
return
}
func ( *types.Struct) map[string]types.Object {
var (
= map[string]types.Object{}
[]*types.Struct
[]*types.Struct
)
for := []*types.Struct{}; len() > 0; , = , nil {
= append(, ...)
for , := range unambiguousFields() {
if .Exported() && [] == nil {
[] =
}
if !.Anonymous() {
continue
}
:= .Type().Underlying()
if , := .(*types.Pointer); {
= .Elem().Underlying()
}
if , := .(*types.Struct); && !contains(, ) {
= append(, )
}
}
}
return
}
func ( []*types.Struct, *types.Struct) bool {
for , := range {
if types.Identical(, ) {
return true
}
}
return false
}
func ( []*types.Struct) map[string]*types.Var {
:= map[string]*types.Var{}
:= map[string]bool{}
for , := range {
for := 0; < .NumFields(); ++ {
:= .Field()
:= .Name()
if [] {
delete(, )
} else {
[] = true
[] =
}
}
}
return
}
func ( *differ) ( types.Object, , map[string]types.Object) {
for , := range {
:= []
if == nil {
.incompatible(, , "removed")
} else {
.checkCorrespondence(, , .Type(), .Type())
}
}
for := range {
if [] == nil {
.compatible(, , "added")
}
}
}
func ( *differ) ( *types.TypeName, *types.Named, types.Type) {
.checkCompatible(, .Underlying(), .Underlying())
if reflect.TypeOf(.Underlying()) != reflect.TypeOf(.Underlying()) {
return
}
if , := .Underlying().(*types.Interface); {
return
}
.checkMethodSet(, , , additionsCompatible)
.checkMethodSet(, types.NewPointer(), types.NewPointer(), additionsCompatible)
}
const (
additionsCompatible = true
additionsIncompatible = false
)
func ( *differ) ( *types.TypeName, , types.Type, bool) {
:= exportedMethods()
:= exportedMethods()
:= .Name()
if , := .(*types.Pointer); {
= "*" +
}
for , := range {
:= []
if == nil {
var string
if receiverNamedType().Obj() != {
= fmt.Sprintf(", method set of %s", )
}
.incompatible(, , "removed")
} else {
:=
if !hasPointerReceiver() && hasPointerReceiver() {
=
}
.checkCorrespondence(, "", .Type(), .Type())
}
}
for , := range {
if [] == nil {
if {
.compatible(, "", "added")
} else {
.incompatible(, "", "added")
}
}
}
}
func ( types.Type) map[string]types.Object {
:= map[string]types.Object{}
:= types.NewMethodSet()
for := 0; < .Len(); ++ {
:= .At().Obj()
if .Exported() {
[.Name()] =
}
}
return
}
func ( types.Object) types.Type {
return .Type().(*types.Signature).Recv().Type()
}
func ( types.Object) *types.Named {
switch t := receiverType().(type) {
case *types.Pointer:
return .Elem().(*types.Named)
case *types.Named:
return
default:
panic("unreachable")
}
}
func ( types.Object) bool {
, := receiverType().(*types.Pointer)
return
}