func (d *differ) checkCompatible(otn *types.TypeName, old, new types.Type) { … } func (d *differ) checkCompatibleChan(otn *types.TypeName, old, new *types.Chan) { … } func (d *differ) checkCompatibleBasic(otn *types.TypeName, old, new *types.Basic) { … } var compatibleBasics … // Interface compatibility: // If the old interface has an unexported method, the new interface is compatible // if its exported method set is a superset of the old. (Users could not implement, // only embed.) // // If the old interface did not have an unexported method, the new interface is // compatible if its exported method set is the same as the old, and it has no // unexported methods. (Adding an unexported method makes the interface // unimplementable outside the package.) // // TODO: must also check that if any methods were added or removed, every exposed // type in the package that implemented the interface in old still implements it in // new. Otherwise external assignments could fail. func (d *differ) checkCompatibleInterface(otn *types.TypeName, old, new *types.Interface) { … } // Return an unexported method from the method set of t, or nil if there are none. func unexportedMethod(t *types.Interface) *types.Func { … } // We need to check three things for structs: // 1. The set of exported fields must be compatible. This ensures that keyed struct // literals continue to compile. (There is no compatibility guarantee for unkeyed // struct literals.) // 2. The set of exported *selectable* fields must be compatible. This includes the exported // fields of all embedded structs. This ensures that selections continue to compile. // 3. If the old struct is comparable, so must the new one be. This ensures that equality // expressions and uses of struct values as map keys continue to compile. // // An unexported embedded struct can't appear in a struct literal outside the // package, so it doesn't have to be present, or have the same name, in the new // struct. // // Field tags are ignored: they have no compile-time implications. func (d *differ) checkCompatibleStruct(obj types.Object, old, new *types.Struct) { … } // exportedFields collects all the immediate fields of the struct that are exported. // This is also the set of exported keys for keyed struct literals. func exportedFields(s *types.Struct) map[string]types.Object { … } // exportedSelectableFields collects all the exported fields of the struct, including // exported fields of embedded structs. // // We traverse the struct breadth-first, because of the rule that a lower-depth field // shadows one at a higher depth. func exportedSelectableFields(s *types.Struct) map[string]types.Object { … } func contains(ts []*types.Struct, t *types.Struct) bool { … } // Given a set of structs at the same depth, the unambiguous fields are the ones whose // names appear exactly once. func unambiguousFields(structs []*types.Struct) map[string]*types.Var { … } // Anything removed or change from the old set is an incompatible change. // Anything added to the new set is a compatible change. func (d *differ) checkCompatibleObjectSets(obj types.Object, old, new map[string]types.Object) { … } func (d *differ) checkCompatibleDefined(otn *types.TypeName, old *types.Named, new types.Type) { … } const additionsCompatible … const additionsIncompatible … func (d *differ) checkMethodSet(otn *types.TypeName, oldt, newt types.Type, addcompat bool) { … } // exportedMethods collects all the exported methods of type's method set. func exportedMethods(t types.Type) map[string]*types.Func { … } func hasPointerReceiver(method *types.Func) bool { … }