package apidiff

import (
	
	
	
)

func ( *differ) ( *types.TypeName, ,  types.Type) {
	switch old := .(type) {
	case *types.Interface:
		if ,  := .(*types.Interface);  {
			.checkCompatibleInterface(, , )
			return
		}

	case *types.Struct:
		if ,  := .(*types.Struct);  {
			.checkCompatibleStruct(, , )
			return
		}

	case *types.Chan:
		if ,  := .(*types.Chan);  {
			.checkCompatibleChan(, , )
			return
		}

	case *types.Basic:
		if ,  := .(*types.Basic);  {
			.checkCompatibleBasic(, , )
			return
		}

	case *types.Named:
		panic("unreachable")

	default:
		.checkCorrespondence(, "", , )
		return

	}
	// Here if old and new are different kinds of types.
	.typeChanged(, "", , )
}

func ( *differ) ( *types.TypeName, ,  *types.Chan) {
	.checkCorrespondence(, ", element type", .Elem(), .Elem())
	if .Dir() != .Dir() {
		if .Dir() == types.SendRecv {
			.compatible(, "", "removed direction")
		} else {
			.incompatible(, "", "changed direction")
		}
	}
}

func ( *differ) ( *types.TypeName, ,  *types.Basic) {
	// Certain changes to numeric types are compatible. Approximately, the info must
	// be the same, and the new values must be a superset of the old.
	if .Kind() == .Kind() {
		// old and new are identical
		return
	}
	if compatibleBasics[[2]types.BasicKind{.Kind(), .Kind()}] {
		.compatible(, "", "changed from %s to %s", , )
	} else {
		.typeChanged(, "", , )
	}
}

// All pairs (old, new) of compatible basic types.
var compatibleBasics = map[[2]types.BasicKind]bool{
	{types.Uint8, types.Uint16}:         true,
	{types.Uint8, types.Uint32}:         true,
	{types.Uint8, types.Uint}:           true,
	{types.Uint8, types.Uint64}:         true,
	{types.Uint16, types.Uint32}:        true,
	{types.Uint16, types.Uint}:          true,
	{types.Uint16, types.Uint64}:        true,
	{types.Uint32, types.Uint}:          true,
	{types.Uint32, types.Uint64}:        true,
	{types.Uint, types.Uint64}:          true,
	{types.Int8, types.Int16}:           true,
	{types.Int8, types.Int32}:           true,
	{types.Int8, types.Int}:             true,
	{types.Int8, types.Int64}:           true,
	{types.Int16, types.Int32}:          true,
	{types.Int16, types.Int}:            true,
	{types.Int16, types.Int64}:          true,
	{types.Int32, types.Int}:            true,
	{types.Int32, types.Int64}:          true,
	{types.Int, types.Int64}:            true,
	{types.Float32, types.Float64}:      true,
	{types.Complex64, types.Complex128}: true,
}

// 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 ( *differ) ( *types.TypeName, ,  *types.Interface) {
	// Method sets are checked in checkCompatibleDefined.

	// Does the old interface have an unexported method?
	if unexportedMethod() != nil {
		.checkMethodSet(, , , additionsCompatible)
	} else {
		// Perform an equivalence check, but with more information.
		.checkMethodSet(, , , additionsIncompatible)
		if  := unexportedMethod();  != nil {
			.incompatible(, .Name(), "added unexported method")
		}
	}
}

// Return an unexported method from the method set of t, or nil if there are none.
func ( *types.Interface) *types.Func {
	for  := 0;  < .NumMethods(); ++ {
		if  := .Method(); !.Exported() {
			return 
		}
	}
	return nil
}

// 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 ( *differ) ( types.Object, ,  *types.Struct) {
	.checkCompatibleObjectSets(, exportedFields(), exportedFields())
	.checkCompatibleObjectSets(, exportedSelectableFields(), exportedSelectableFields())
	// Removing comparability from a struct is an incompatible change.
	if types.Comparable() && !types.Comparable() {
		.incompatible(, "", "old is comparable, new is not")
	}
}

// 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 ( *types.Struct) map[string]types.Object {
	 := map[string]types.Object{}
	for  := 0;  < .NumFields(); ++ {
		 := .Field()
		if .Exported() {
			[.Name()] = 
		}
	}
	return 
}

// 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 ( *types.Struct) map[string]types.Object {
	var (
		    = map[string]types.Object{}
		 []*types.Struct // embedded structs at the next depth
		 []*types.Struct // to handle recursive embedding
	)
	for  := []*types.Struct{}; len() > 0; ,  = , nil {
		 = append(, ...)
		// We only want to consider unambiguous fields. Ambiguous fields (where there
		// is more than one field of the same name at the same level) are legal, but
		// cannot be selected.
		for ,  := range unambiguousFields() {
			// Record an exported field we haven't seen before. If we have seen it,
			// it occurred a lower depth, so it shadows this field.
			if .Exported() && [] == nil {
				[] = 
			}
			// Remember embedded structs for processing at the next depth,
			// but only if we haven't seen the struct at this depth or above.
			if !.Anonymous() {
				continue
			}
			 := .Type().Underlying()
			if ,  := .(*types.Pointer);  {
				 = .Elem().Underlying()
			}
			if ,  := .(*types.Struct);  && !contains(, ) {
				 = append(, )
			}
		}
	}
	return 
}

func ( []*types.Struct,  *types.Struct) bool {
	for ,  := range  {
		if types.Identical(, ) {
			return true
		}
	}
	return false
}

// Given a set of structs at the same depth, the unambiguous fields are the ones whose
// names appear exactly once.
func ( []*types.Struct) map[string]*types.Var {
	 := map[string]*types.Var{}
	 := map[string]bool{}
	for ,  := range  {
		for  := 0;  < .NumFields(); ++ {
			 := .Field()
			 := .Name()
			if [] {
				delete(, )
			} else {
				[] = true
				[] = 
			}
		}
	}
	return 
}

// Anything removed or change from the old set is an incompatible change.
// Anything added to the new set is a compatible change.
func ( *differ) ( types.Object, ,  map[string]types.Object) {
	for ,  := range  {
		 := []
		if  == nil {
			.incompatible(, , "removed")
		} else {
			.checkCorrespondence(, , .Type(), .Type())
		}
	}
	for  := range  {
		if [] == nil {
			.compatible(, , "added")
		}
	}
}

func ( *differ) ( *types.TypeName,  *types.Named,  types.Type) {
	// We've already checked that old and new correspond.
	.checkCompatible(, .Underlying(), .Underlying())
	// If there are different kinds of types (e.g. struct and interface), don't bother checking
	// the method sets.
	if reflect.TypeOf(.Underlying()) != reflect.TypeOf(.Underlying()) {
		return
	}
	// Interface method sets are checked in checkCompatibleInterface.
	if ,  := .Underlying().(*types.Interface);  {
		return
	}

	// A new method set is compatible with an old if the new exported methods are a superset of the old.
	.checkMethodSet(, , , additionsCompatible)
	.checkMethodSet(, types.NewPointer(), types.NewPointer(), additionsCompatible)
}

const (
	additionsCompatible   = true
	additionsIncompatible = false
)

func ( *differ) ( *types.TypeName, ,  types.Type,  bool) {
	// TODO: find a way to use checkCompatibleObjectSets for this.
	 := exportedMethods()
	 := exportedMethods()
	 := .Name()
	if ,  := .(*types.Pointer);  {
		 = "*" + 
	}
	for ,  := range  {
		 := []
		if  == nil {
			var  string
			// Due to embedding, it's possible that the method's receiver type is not
			// the same as the defined type whose method set we're looking at. So for
			// a type T with removed method M that is embedded in some other type U,
			// we will generate two "removed" messages for T.M, one for its own type
			// T and one for the embedded type U. We want both messages to appear,
			// but the messageSet dedup logic will allow only one message for a given
			// object. So use the part string to distinguish them.
			if receiverNamedType().Obj() !=  {
				 = fmt.Sprintf(", method set of %s", )
			}
			.incompatible(, , "removed")
		} else {
			 := 
			// If a value method is changed to a pointer method and has a signature
			// change, then we can get two messages for the same method definition: one
			// for the value method set that says it's removed, and another for the
			// pointer method set that says it changed. To keep both messages (since
			// messageSet dedups), use newMethod for the second. (Slight hack.)
			if !hasPointerReceiver() && hasPointerReceiver() {
				 = 
			}
			.checkCorrespondence(, "", .Type(), .Type())
		}
	}

	// Check for added methods.
	for ,  := range  {
		if [] == nil {
			if  {
				.compatible(, "", "added")
			} else {
				.incompatible(, "", "added")
			}
		}
	}
}

// exportedMethods collects all the exported methods of type's method set.
func ( types.Type) map[string]types.Object {
	 := map[string]types.Object{}
	 := types.NewMethodSet()
	for  := 0;  < .Len(); ++ {
		 := .At().Obj()
		if .Exported() {
			[.Name()] = 
		}
	}
	return 
}

func ( types.Object) types.Type {
	return .Type().(*types.Signature).Recv().Type()
}

func ( types.Object) *types.Named {
	switch t := receiverType().(type) {
	case *types.Pointer:
		return .Elem().(*types.Named)
	case *types.Named:
		return 
	default:
		panic("unreachable")
	}
}

func ( types.Object) bool {
	,  := receiverType().(*types.Pointer)
	return 
}