package apidiff
import (
)
func (, *types.Package) 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
}
type differ struct {
old, new *types.Package
correspondMap map[*types.TypeName]types.Type
incompatibles messageSet
compatibles messageSet
}
func (, *types.Package) *differ {
return &differ{
old: ,
new: ,
correspondMap: map[*types.TypeName]types.Type{},
incompatibles: messageSet{},
compatibles: messageSet{},
}
}
func ( *differ) ( types.Object, , string, ...interface{}) {
addMessage(.incompatibles, , , , )
}
func ( *differ) ( types.Object, , string, ...interface{}) {
addMessage(.compatibles, , , , )
}
func ( messageSet, types.Object, , string, []interface{}) {
.add(, , fmt.Sprintf(, ...))
}
func ( *differ) () {
for , := range .old.Scope().Names() {
:= .old.Scope().Lookup()
if !.Exported() {
continue
}
:= .new.Scope().Lookup()
if == nil {
.incompatible(, "", "removed")
continue
}
.checkObjects(, )
}
for , := range .new.Scope().Names() {
:= .new.Scope().Lookup()
if .Exported() && .old.Scope().Lookup() == nil {
.compatible(, "", "added")
}
}
for , := range .correspondMap {
, := .Type().Underlying().(*types.Interface)
if ! {
continue
}
, := .Underlying().(*types.Interface)
if ! {
continue
}
for , := range .correspondMap {
if == {
continue
}
if types.Implements(.Type(), ) && !types.Implements(, ) {
.incompatible(, "", "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(, "", .Type(), .Type())
return
}
case *types.Func:
switch new := .(type) {
case *types.Func:
.checkCorrespondence(, "", .Type(), .Type())
return
case *types.Var:
.compatible(, "", "changed from func to var")
.checkCorrespondence(, "", .Type(), .Type())
return
}
case *types.TypeName:
if , := .(*types.TypeName); {
.checkCorrespondence(, "", .Type(), .Type())
return
}
default:
panic("unexpected obj type")
}
.incompatible(, "", "changed from %s to %s",
objectKindString(), objectKindString())
}
func ( *differ) (, *types.Const) {
:= .Type()
:= .Type()
if !.correspond(, ) {
.typeChanged(, "", , )
return
}
if !constant.Compare(.Val(), token.EQL, .Val()) {
.incompatible(, "", "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) ( types.Object, string, , types.Type) {
if !.correspond(, ) {
.typeChanged(, , , )
}
}
func ( *differ) ( types.Object, string, , types.Type) {
= 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())
}