File: Rules\Compat\CannotRemoveBaseTypeOrInterface.cs
Web Access
Project: src\src\Microsoft.DotNet.ApiCompat\src\Microsoft.DotNet.ApiCompat.Core\Microsoft.DotNet.ApiCompat.Core.csproj (Microsoft.DotNet.ApiCompat.Core)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Generic;
using System.Composition;
using Microsoft.Cci.Extensions;
 
namespace Microsoft.Cci.Differs.Rules
{
    [ExportDifferenceRule]
    internal class CannotRemoveBaseTypeOrInterface : CompatDifferenceRule
    {
        [Import]
        public IEqualityComparer<ITypeReference> _typeComparer { get; set; } = null;
 
        public override DifferenceType Diff(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract)
        {
            if (impl == null || contract == null)
                return DifferenceType.Unknown;
 
            if (AddedBaseType(differences, impl, contract) ||
                AddedInterface(differences, impl, contract))
                return DifferenceType.Changed;
 
            return DifferenceType.Unknown;
        }
 
        private bool AddedBaseType(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract)
        {
            // For interfaces we rely only on the AddedInterface check
            if (impl.IsInterface || contract.IsInterface)
                return false;
 
            // Base types must be in the same order so we have to compare them in order
            List<ITypeReference> implBaseTypes = new List<ITypeReference>(impl.GetAllBaseTypes());
 
            int lastIndex = 0;
            foreach (var contractBaseType in contract.GetAllBaseTypes())
            {
                lastIndex = implBaseTypes.FindIndex(lastIndex, item1BaseType => _typeComparer.Equals(item1BaseType, contractBaseType));
 
                if (lastIndex < 0)
                {
                    differences.AddIncompatibleDifference(this,
                        $"Type '{contract.FullName()}' does not inherit from base type '{contractBaseType.FullName()}' in the {Implementation} but it does in the {Contract}.");
                    return true;
                }
            }
 
            return false;
        }
 
        private bool AddedInterface(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract)
        {
            // Interfaces can be in any order so use a HashSet
            HashSet<ITypeReference> implInterfaces = new HashSet<ITypeReference>(impl.GetAllInterfaces(), _typeComparer);
 
            foreach (var contractInterface in contract.GetAllInterfaces())
            {
                // Ignore internal interfaces
                if (!contractInterface.IsVisibleOutsideAssembly())
                    continue;
 
                if (!implInterfaces.Contains(contractInterface))
                {
                    differences.AddIncompatibleDifference(this,
                        $"Type '{contract.FullName()}' does not implement interface '{contractInterface.FullName()}' in the {Implementation} but it does in the {Contract}.");
                    return true;
                }
            }
 
            return false;
        }
    }
}