Module is a convenience type for representing a Go module with a path and a
slice of Packages contained within.Packages[]*types.PackagePathstring relativePath computes the module-relative package path of the given Package.
func ModuleChanges(old, new *Module) Report
compatiblesmessageSet Correspondences between named types.
Even though it is the named types (*types.Named) that correspond, we use
*types.TypeName as a map key because they are canonical.
The values can be either named types or basic types. Messages.new*types.Packageold*types.Package(*differ) checkCompatible(otn *types.TypeName, old, new types.Type)(*differ) checkCompatibleBasic(otn *types.TypeName, old, new *types.Basic)(*differ) checkCompatibleChan(otn *types.TypeName, old, new *types.Chan)(*differ) checkCompatibleDefined(otn *types.TypeName, old *types.Named, new types.Type) 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. Anything removed or change from the old set is an incompatible change.
Anything added to the new set is a compatible change. 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.(*differ) checkCorrespondence(obj objectWithSide, part string, old, new types.Type)(*differ) checkMethodSet(otn *types.TypeName, oldt, newt types.Type, addcompat bool)(*differ) checkObjects(old, new types.Object)(*differ) checkPackage(oldRootPackagePath string)(*differ) compatible(obj objectWithSide, part, format string, args ...interface{}) Compare two constants. 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. 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. 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. establishCorrespondence records and validates a correspondence between
old and new.
If this is the first type corresponding to old, it checks that the type
declaration is compatible with old and records its correspondence.
Otherwise, it checks that new is equivalent to the previously recorded
type corresponding to old.(*differ) incompatible(obj objectWithSide, part, format string, args ...interface{})(*differ) methodID(m *types.Func) string(*differ) sortedMethods(iface *types.Interface) []*types.Func(*differ) typeChanged(obj objectWithSide, part string, old, new types.Type)(*differ) typeListsCorrespond(tl1, tl2 *types.TypeList) bool Two list of type parameters correspond if they are the same length, and
the constraints of corresponding type parameters correspond.
func newDiffer(old, new *types.Package) *differ
There can be at most one message for each object or part thereof.
Parts include interface methods and struct fields.
The part thing is necessary. Method (Func) objects have sufficient info, but field
Vars do not: they just have a field name and a type, without the enclosing struct. Add a message for obj and part, overwriting a previous message
(shouldn't happen).
obj is required but part can be empty.( messageSet) collect(oldRootPackagePath, newRootPackagePath string) []string
func addMessage(ms messageSet, obj objectWithSide, part, format string, args []interface{})
objectWithSide contains an object, and information on which side (old or new)
of the comparison it relates to. This matters when need to express the object's
package path, relative to the root path of the comparison, as the old and new
sides can have different roots (e.g. comparing somepackage/v2 vs. somepackage/v3).isNewboolobjecttypes.Object
func addMessage(ms messageSet, obj objectWithSide, part, format string, args []interface{})
Package-Level Functions (total 20, in which 2 are exported)
Changes reports on the differences between the APIs of the old and new packages.
It classifies each difference as either compatible or incompatible (breaking.) For
a detailed discussion of what constitutes an incompatible change, see the README.
ModuleChanges reports on the differences between the APIs of the old and new
modules. It classifies each difference as either compatible or incompatible
(breaking). This includes the addition and removal of entire packages. For a
detailed discussion of what constitutes an incompatible change, see the README.
changesInternal contains the core logic for comparing a single package, shared
between Changes and ModuleChanges. The root package path arguments refer to the
context of this apidiff invocation - when diffing a single package, they will be
that package, but when diffing a whole module, they will be the root path of the
module. This is used to give change messages appropriate context for object names.
The old and new root must be tracked independently, since each side of the diff
operation may be a different path.
exportedFields collects all the immediate fields of the struct that are exported.
This is also the set of exported keys for keyed struct literals.
exportedMethods collects all the exported methods of type's method set.
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.
go/types always includes the argument and result names when formatting a signature.
Since these can change without affecting compatibility, we don't want users to
be distracted by them, so we remove them.
Given a set of structs at the same depth, the unambiguous fields are the ones whose
names appear exactly once.
Return an unexported method from the method set of t, or nil if there are none.
Package-Level Variables (only one, which is unexported)
All pairs (old, new) of compatible basic types.
Package-Level Constants (total 2, neither is exported)