// TODO: test swap corresponding types (e.g. u1 <-> u2 and u2 <-> u1)
// TODO: test exported alias refers to something in another package -- does correspondence work then?
// TODO: CODE COVERAGE
// TODO: note that we may miss correspondences because we bail early when we compare a signature (e.g. when lengths differ; we could do up to the shorter)
// TODO: if you add an unexported method to an exposed interface, you have to check that
//		every exposed type that previously implemented the interface still does. Otherwise
//		an external assignment of the exposed type to the interface type could fail.
// TODO: check constant values: large values aren't representable by some types.
// TODO: Document all the incompatibilities we don't check for.

package apidiff

import (
	
	
	
	
	

	
)

// 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.
func (,  *types.Package) Report {
	return changesInternal(, , .Path(), .Path())
}

// 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.
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 
}

// 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.
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 ,  := [];  {
			// shared package, compare surfaces
			 := changesInternal(, , .Path, .Path)
			.Changes = append(.Changes, .Changes...)
		} else {
			// old package was removed
			.Changes = append(.Changes, packageChange(, "removed", false))
		}
	}

	for ,  := range  {
		if ,  := []; ! {
			// new package was added
			.Changes = append(.Changes, packageChange(, "added", true))
		}
	}

	return 
}

func ( *types.Package,  string,  bool) Change {
	return Change{
		Message:    fmt.Sprintf("package %s: %s", .Path(), ),
		Compatible: ,
	}
}

// Module is a convenience type for representing a Go module with a path and a
// slice of Packages contained within.
type Module struct {
	Path     string
	Packages []*types.Package
}

// relativePath computes the module-relative package path of the given Package.
func ( *Module) ( *types.Package) string {
	return strings.TrimPrefix(.Path(), .Path)
}

type differ struct {
	old, new *types.Package
	// 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.
	correspondMap typeutil.Map

	// Messages.
	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) {
	// Determine what has changed between old and new.

	// First, establish correspondences between types with the same name, before
	// looking at aliases. This will avoid confusing messages like "T: changed
	// from T to T", which can happen if a correspondence between an alias
	// and a named type is established first.
	// See testdata/order.go.
	// Example of what this loop looks for:
	//    type A = B // old
	//    type B ... // new
	//
	// We want to ensure that old B and new B correspond.
	//
	// This loop is unnecessary for correctness; skipping symbols will not introduce bugs.
	for ,  := range .old.Scope().Names() {
		 := .old.Scope().Lookup()
		if ,  := .(*types.TypeName);  {
			if ,  := .Type().(*types.Named);  {
				if !.Obj().Exported() {
					continue
				}
				// Skip aliases to instantiated generic types. They end up getting
				// compared to the origin type, which will fail.
				// For example, we don't want to try to make A[int] correspond with
				// A[T any].
				if .IsAlias() && isInstantiated() {
					continue
				}
				// Does new have a named type of the same name? Look up using
				// the old named type's name, oldn.Obj().Name(), not the
				// TypeName tn, which may be an alias.
				 := .new.Scope().Lookup(.Obj().Name())
				if  != nil {
					.checkObjects(, )
				}
			}
		}
	}

	// Next, look at all exported symbols in the old world and compare them
	// with the same-named symbols in the new world.
	for ,  := range .old.Scope().Names() {
		 := .old.Scope().Lookup()
		if !.Exported() {
			continue
		}
		 := .new.Scope().Lookup()
		if  == nil {
			.incompatible(objectWithSide{, false}, "", "removed")
			continue
		}
		.checkObjects(, )
	}

	// Now look at what has been added in the new package.
	for ,  := range .new.Scope().Names() {
		 := .new.Scope().Lookup()
		if .Exported() && .old.Scope().Lookup() == nil {
			.compatible(objectWithSide{, true}, "", "added")
		}
	}

	// Whole-package satisfaction.
	// For every old exposed interface oIface and its corresponding new interface nIface...
	.correspondMap.Iterate(func( types.Type,  any) {
		 := .(*types.Named)
		 := .Obj()
		 := .(types.Type)
		,  := .Type().Underlying().(*types.Interface)
		if ! {
			return
		}
		,  := .Underlying().(*types.Interface)
		if ! {
			// If nt1 isn't an interface but otn1 is, then that's an incompatibility that
			// we've already noticed, so there's no need to do anything here.
			return
		}
		// For every old type that implements oIface, its corresponding new type must implement
		// nIface.
		.correspondMap.Iterate(func( types.Type,  any) {
			 := .(*types.Named)
			 := .Obj()
			 := .(types.Type)
			if  ==  {
				return
			}
			if types.Implements(.Type(), ) && !types.Implements(, ) {
				// TODO(jba): the type name is not sufficient information here; we need the type args
				// if this is an instantiated generic type.
				.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")
	}
	// Here if kind of type changed.
	.incompatible(objectWithSide{, false}, "", "changed from %s to %s",
		objectKindString(), objectKindString())
}

// Compare two constants.
func ( *differ) (,  *types.Const) {
	 := .Type()
	 := .Type()
	// Check for change of type.
	if !.correspond(, ) {
		.typeChanged(objectWithSide{, false}, "", , )
		return
	}
	// Check for change of value.
	// We know the types are the same, so constant.Compare shouldn't panic.
	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", , )
}

// 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.
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())
}