package apidiff
import (
)
func (, *types.Package) Report {
return changesInternal(, , .Path(), .Path())
}
func (, *types.Package, , string) Report {
:= newDiffer(, )
.checkPackage()
:= Report{}
for , := range .incompatibles.collect(, ) {
.Changes = append(.Changes, Change{Message: , Compatible: false})
}
for , := range .compatibles.collect(, ) {
.Changes = append(.Changes, Change{Message: , Compatible: true})
}
return
}
func (, *Module) Report {
var Report
:= make(map[string]*types.Package)
for , := range .Packages {
[.relativePath()] =
}
:= make(map[string]*types.Package)
for , := range .Packages {
[.relativePath()] =
}
for , := range {
if , := []; {
:= changesInternal(, , .Path, .Path)
.Changes = append(.Changes, .Changes...)
} else {
.Changes = append(.Changes, packageChange(, "removed", false))
}
}
for , := range {
if , := []; ! {
.Changes = append(.Changes, packageChange(, "added", true))
}
}
return
}
func ( *types.Package, string, bool) Change {
return Change{
Message: fmt.Sprintf("package %s: %s", .Path(), ),
Compatible: ,
}
}
type Module struct {
Path string
Packages []*types.Package
}
func ( *Module) ( *types.Package) string {
return strings.TrimPrefix(.Path(), .Path)
}
type differ struct {
old, new *types.Package
correspondMap typeutil.Map
incompatibles messageSet
compatibles messageSet
}
func (, *types.Package) *differ {
return &differ{
old: ,
new: ,
incompatibles: messageSet{},
compatibles: messageSet{},
}
}
func ( *differ) ( objectWithSide, , string, ...interface{}) {
addMessage(.incompatibles, , , , )
}
func ( *differ) ( objectWithSide, , string, ...interface{}) {
addMessage(.compatibles, , , , )
}
func ( messageSet, objectWithSide, , string, []interface{}) {
.add(, , fmt.Sprintf(, ...))
}
func ( *differ) ( string) {
for , := range .old.Scope().Names() {
:= .old.Scope().Lookup()
if , := .(*types.TypeName); {
if , := .Type().(*types.Named); {
if !.Obj().Exported() {
continue
}
if .IsAlias() && isInstantiated() {
continue
}
:= .new.Scope().Lookup(.Obj().Name())
if != nil {
.checkObjects(, )
}
}
}
}
for , := range .old.Scope().Names() {
:= .old.Scope().Lookup()
if !.Exported() {
continue
}
:= .new.Scope().Lookup()
if == nil {
.incompatible(objectWithSide{, false}, "", "removed")
continue
}
.checkObjects(, )
}
for , := range .new.Scope().Names() {
:= .new.Scope().Lookup()
if .Exported() && .old.Scope().Lookup() == nil {
.compatible(objectWithSide{, true}, "", "added")
}
}
.correspondMap.Iterate(func( types.Type, any) {
:= .(*types.Named)
:= .Obj()
:= .(types.Type)
, := .Type().Underlying().(*types.Interface)
if ! {
return
}
, := .Underlying().(*types.Interface)
if ! {
return
}
.correspondMap.Iterate(func( types.Type, any) {
:= .(*types.Named)
:= .Obj()
:= .(types.Type)
if == {
return
}
if types.Implements(.Type(), ) && !types.Implements(, ) {
.incompatible(objectWithSide{, false}, "", "no longer implements %s", objectString(, ))
}
})
})
}
func ( *differ) (, types.Object) {
switch old := .(type) {
case *types.Const:
if , := .(*types.Const); {
.constChanges(, )
return
}
case *types.Var:
if , := .(*types.Var); {
.checkCorrespondence(objectWithSide{, false}, "", .Type(), .Type())
return
}
case *types.Func:
switch new := .(type) {
case *types.Func:
.checkCorrespondence(objectWithSide{, false}, "", .Type(), .Type())
return
case *types.Var:
.compatible(objectWithSide{, false}, "", "changed from func to var")
.checkCorrespondence(objectWithSide{, false}, "", .Type(), .Type())
return
}
case *types.TypeName:
if , := .(*types.TypeName); {
.checkCorrespondence(objectWithSide{, false}, "", .Type(), .Type())
return
}
default:
panic("unexpected obj type")
}
.incompatible(objectWithSide{, false}, "", "changed from %s to %s",
objectKindString(), objectKindString())
}
func ( *differ) (, *types.Const) {
:= .Type()
:= .Type()
if !.correspond(, ) {
.typeChanged(objectWithSide{, false}, "", , )
return
}
if !constant.Compare(.Val(), token.EQL, .Val()) {
.incompatible(objectWithSide{, false}, "", "value changed from %s to %s", .Val(), .Val())
}
}
func ( types.Object) string {
switch .(type) {
case *types.Const:
return "const"
case *types.Var:
return "var"
case *types.Func:
return "func"
case *types.TypeName:
return "type"
default:
return "???"
}
}
func ( *differ) ( objectWithSide, string, , types.Type) {
if !.correspond(, ) {
.typeChanged(, , , )
}
}
func ( *differ) ( objectWithSide, string, , types.Type) {
= types.Unalias()
= types.Unalias()
= removeNamesFromSignature()
= removeNamesFromSignature()
:= types.TypeString(, types.RelativeTo(.old))
:= types.TypeString(, types.RelativeTo(.new))
.incompatible(, , "changed from %s to %s", , )
}
func ( types.Type) types.Type {
, := .(*types.Signature)
if ! {
return
}
:= func( *types.Tuple) *types.Tuple {
var []*types.Var
for := 0; < .Len(); ++ {
:= .At()
= append(, types.NewVar(.Pos(), .Pkg(), "", .Type()))
}
return types.NewTuple(...)
}
return types.NewSignature(.Recv(), (.Params()), (.Results()), .Variadic())
}