package apidiffimport ()// Two types are correspond if they are identical except for defined types,// which must correspond.//// Two defined types correspond if they can be interchanged in the old and new APIs,// possibly after a renaming.//// This is not a pure function. If we come across named types while traversing,// we establish correspondence.func ( *differ) (, types.Type) bool {return .corr(, , nil)}// corr determines whether old and new correspond. The argument p is a list of// known interface identities, to avoid infinite recursion.//// corr calls itself recursively as much as possible, to establish more// correspondences and so check more of the API. E.g. if the new function has more// parameters than the old, compare all the old ones before returning false.//// Compare this to the implementation of go/types.Identical.func ( *differ) (, types.Type, *ifacePair) bool {// Structure copied from types.Identical.switch old := .(type) {case *types.Basic:returntypes.Identical(, )case *types.Array:if , := .(*types.Array); {return .(.Elem(), .Elem(), ) && .Len() == .Len() }case *types.Slice:if , := .(*types.Slice); {return .(.Elem(), .Elem(), ) }case *types.Map:if , := .(*types.Map); {return .(.Key(), .Key(), ) && .(.Elem(), .Elem(), ) }case *types.Chan:if , := .(*types.Chan); {return .(.Elem(), .Elem(), ) && .Dir() == .Dir() }case *types.Pointer:if , := .(*types.Pointer); {return .(.Elem(), .Elem(), ) }case *types.Signature:if , := .(*types.Signature); { := .(.Params(), .Params(), ) := .(.Results(), .Results(), )return .Variadic() == .Variadic() && && }case *types.Tuple:if , := .(*types.Tuple); {for := 0; < .Len(); ++ {if >= .Len() || !.(.At().Type(), .At().Type(), ) {returnfalse } }return .Len() == .Len() }case *types.Struct:if , := .(*types.Struct); {for := 0; < .NumFields(); ++ {if >= .NumFields() {returnfalse } := .Field() := .Field()if .Anonymous() != .Anonymous() || .Tag() != .Tag() || !.(.Type(), .Type(), ) || !.corrFieldNames(, ) {returnfalse } }return .NumFields() == .NumFields() }case *types.Interface:if , := .(*types.Interface); {// Deal with circularity. See the comment in types.Identical. := &ifacePair{, , }for != nil {if .identical() {returntrue// same pair was compared before } = .prev } := .sortedMethods() := .sortedMethods()for , := range {if >= len() {returnfalse } := []if .methodID() != .methodID() || !.(.Type(), .Type(), ) {returnfalse } }return .NumMethods() == .NumMethods() }case *types.Named:if , := .(*types.Named); {return .establishCorrespondence(, ) }if , := .(*types.Basic); {// Basic types are defined types, too, so we have to support them.return .establishCorrespondence(, ) }case *types.TypeParam:if , := .(*types.TypeParam); {if .Index() == .Index() {returntrue } }default:panic(fmt.Sprintf("unknown type kind %T", )) }returnfalse}// Compare old and new field names. We are determining correspondence across packages,// so just compare names, not packages. For an unexported, embedded field of named// type (non-named embedded fields are possible with aliases), we check that the type// names correspond. We check the types for correspondence before this is called, so// we've established correspondence.func ( *differ) (, *types.Var) bool {if .Anonymous() && .Anonymous() && !.Exported() && !.Exported() {if , := .Type().(*types.Named); { := .Type().(*types.Named)return .establishCorrespondence(, ) } }return .Name() == .Name()}// Establish that old corresponds with new if it does not already// correspond to something else.func ( *differ) ( *types.Named, types.Type) bool { := .Obj() := .correspondMap[]if == nil {// For now, assume the types don't correspond unless they are from the old // and new packages, respectively. // // This is too conservative. For instance, // [old] type A = q.B; [new] type A q.C // could be OK if in package q, B is an alias for C. // Or, using p as the name of the current old/new packages: // [old] type A = q.B; [new] type A int // could be OK if in q, // [old] type B int; [new] type B = p.A // In this case, p.A and q.B name the same type in both old and new worlds. // Note that this case doesn't imply circular package imports: it's possible // that in the old world, p imports q, but in the new, q imports p. // // However, if we didn't do something here, then we'd incorrectly allow cases // like the first one above in which q.B is not an alias for q.C // // What we should do is check that the old type, in the new world's package // of the same path, doesn't correspond to something other than the new type. // That is a bit hard, because there is no easy way to find a new package // matching an old one.if , := .(*types.Named); {if .Obj().Pkg() != .old || .Obj().Pkg() != .new {return .Obj().Id() == .Obj().Id() }// Prior to generics, any two named types could correspond. // Two named types cannot correspond if their type parameter lists don't match.if !typeParamListsMatch(.TypeParams(), .TypeParams()) {returnfalse } }// If there is no correspondence, create one. .correspondMap[] = // Check that the corresponding types are compatible. .checkCompatibleDefined(, , )returntrue }returntypesEquivalent(, )}// Two list of type parameters match if they are the same length, and// the constraints of corresponding type parameters are identical.func (, *types.TypeParamList) bool {if .Len() != .Len() {returnfalse }for := 0; < .Len(); ++ {if !types.Identical(.At().Constraint(), .At().Constraint()) {returnfalse } }returntrue}// typesEquivalent reports whether two types are identical, or if// the types have identical type param lists except that one type has nil// constraints.//// This allows us to match a Type from a method receiver or arg to the Type from// the declaration.func (, types.Type) bool {iftypes.Identical(, ) {returntrue }// Handle two types with the same type params, one // having constraints and one not. , := .(*types.Named)if ! {returnfalse } , := .(*types.Named)if ! {returnfalse } := .TypeParams() := .TypeParams()if .Len() != .Len() {returnfalse }if .Len() == 0 {// Not generic types.returnfalse }for := 0; < .Len(); ++ { := .At() := .At()if .Constraint() == nil || .Constraint() == nil {returntrue }if !types.Identical(.Constraint(), .Constraint()) {returnfalse } }returntrue}func ( *differ) ( *types.Interface) []*types.Func { := make([]*types.Func, .NumMethods())for := 0; < .NumMethods(); ++ { [] = .Method() }sort.Slice(, func(, int) bool { return .methodID([]) < .methodID([]) })return}func ( *differ) ( *types.Func) string {// If the method belongs to one of the two packages being compared, use // just its name even if it's unexported. That lets us treat unexported names // from the old and new packages as equal.if .Pkg() == .old || .Pkg() == .new {return .Name() }return .Id()}// Copied from the go/types package:// An ifacePair is a node in a stack of interface type pairs compared for identity.typeifacePairstruct {x, y *types.Interfaceprev *ifacePair}func ( *ifacePair) ( *ifacePair) bool {return .x == .x && .y == .y || .x == .y && .y == .x}
The pages are generated with Goldsv0.4.9. (GOOS=linux GOARCH=amd64)