Пример #1
0
        public override DifferenceType Diff(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract)
        {
            if (impl == null || contract == null)
            {
                return(DifferenceType.Unknown);
            }

            string implObjType     = GetObjectType(impl);
            string contractObjType = GetObjectType(contract);

            if (implObjType != contractObjType)
            {
                differences.AddIncompatibleDifference(this,
                                                      $"Type '{impl.FullName()}' is a '{implObjType}' in the {Implementation} but is a '{contractObjType}' in the {Contract}.");

                return(DifferenceType.Changed);
            }

            if (contract.Attributes.HasIsReadOnlyAttribute() && !impl.Attributes.HasIsReadOnlyAttribute())
            {
                differences.AddIncompatibleDifference(this,
                                                      $"Type '{impl.FullName()}' is marked as readonly in the {Contract} so it must also be marked readonly in the {Implementation}.");

                return(DifferenceType.Changed);
            }

            return(DifferenceType.Unknown);
        }
        private bool CheckModifiersOnParametersAndReturnValue(IDifferences differences, IMethodDefinition implMethod, IMethodDefinition contractMethod)
        {
            int paramCount = implMethod.ParameterCount;

            Contract.Assert(paramCount == contractMethod.ParameterCount);

            if (paramCount == 0)
            {
                return(true);
            }

            IParameterDefinition[] implParams     = implMethod.Parameters.ToArray();
            IParameterDefinition[] contractParams = contractMethod.Parameters.ToArray();

            bool match = true;

            for (int i = 0; i < paramCount; i++)
            {
                IParameterDefinition implParam     = implParams[i];
                IParameterDefinition contractParam = contractParams[i];

                //TODO: Do we care about the compatibility with marshalling attributes Out\In? They don't seem to be set consistently.

                if (GetModifier(implParam) != GetModifier(contractParam))
                {
                    differences.AddIncompatibleDifference(this,
                                                          "Modifiers on parameter '{0}' on method '{1}' are '{2}' in the implementation but '{3}' in the contract.",
                                                          implParam.Name.Value, implMethod.FullName(), GetModifier(implParam), GetModifier(contractParam));
                    match = false;
                }

                // Now check custom modifiers, primarily focused on const & volatile
                if (implParam.IsModified || contractParam.IsModified)
                {
                    var union = implParam.CustomModifiers.Union(contractParam.CustomModifiers);
                    if (implParam.CustomModifiers.Count() != union.Count())
                    {
                        differences.AddIncompatibleDifference(this,
                                                              "Custom modifiers on parameter '{0}' on method '{1}' are '{2}' in the implementation but '{3}' in the contract.",
                                                              implParam.Name.Value, implMethod.FullName(), PrintCustomModifiers(implParam.CustomModifiers), PrintCustomModifiers(contractParam.CustomModifiers));
                        match = false;
                    }
                }
            }

            string implReturnModifier     = GetReturnValueModifier(implMethod);
            string contractReturnModifier = GetReturnValueModifier(contractMethod);

            if (implReturnModifier != contractReturnModifier)
            {
                differences.AddIncompatibleDifference(this,
                                                      "Modifiers on return type of method '{0}' are '{1}' in the implementation but '{2}' in the contract.",
                                                      implMethod.FullName(), implReturnModifier, contractReturnModifier);
                match = false;
            }

            return(match);
        }
        //private bool AnySecurityAttributeAdded(IDifferences differences, IReference target, IEnumerable<ISecurityAttribute> attribues1, IEnumerable<ISecurityAttribute> attributes2)
        //{
        //    return AnyAttributeAdded(differences, target, attribues1.SelectMany(a => a.Attributes), attributes2.SelectMany(a => a.Attributes));
        //}

        private void CheckAttributeDifferences(IDifferences differences, IReference target, IEnumerable <ICustomAttribute> implAttributes, IEnumerable <ICustomAttribute> contractAttributes)
        {
            AttributesMapping <IEnumerable <ICustomAttribute> > attributeMapping = new AttributesMapping <IEnumerable <ICustomAttribute> >(_settings);

            attributeMapping.AddMapping(0, contractAttributes);
            attributeMapping.AddMapping(1, implAttributes);

            foreach (var group in attributeMapping.Attributes)
            {
                switch (group.Difference)
                {
                case DifferenceType.Added:
                {
                    ITypeReference type       = group.Representative.Attributes.First().Type;
                    string         attribName = type.FullName();

                    if (FilterAttribute(attribName))
                    {
                        break;
                    }

                    AttributeAdded(target, attribName);

                    differences.AddIncompatibleDifference("CannotAddAttribute",
                                                          "Attribute '{0}' exists on '{1}' in the implementation but not the contract.",
                                                          attribName, target.FullName());

                    break;
                }

                case DifferenceType.Changed:

                    //TODO: Add some more logic to check the two lists of attributes which have the same type.
                    break;

                case DifferenceType.Removed:
                {
                    ITypeReference type       = group.Representative.Attributes.First().Type;
                    string         attribName = type.FullName();

                    if (s_IgnorableAttributes.Contains(attribName))
                    {
                        break;
                    }

                    differences.AddIncompatibleDifference("CannotRemoveAttribute",
                                                          "Attribute '{0}' exists on '{1}' in the contract but not the implementation.",
                                                          attribName, target.FullName());

                    break;
                }
                }
            }
        }
Пример #4
0
        public override DifferenceType Diff(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract)
        {
            if (impl == null || contract == null)
            {
                return(DifferenceType.Unknown);
            }

            // If implementation is public then contract can be any visibility
            if (impl.GetVisibility() == TypeMemberVisibility.Public)
            {
                return(DifferenceType.Unknown);
            }

            // If implementation is protected then contract must be protected as well.
            if (impl.GetVisibility() == TypeMemberVisibility.Family)
            {
                if (contract.GetVisibility() != TypeMemberVisibility.Family)
                {
                    differences.AddIncompatibleDifference(this,
                                                          "Visibility of type '{0}' is '{1}' in the implementation but '{2}' in the contract.",
                                                          impl.FullName(), impl.GetVisibility(), contract.GetVisibility());
                    return(DifferenceType.Changed);
                }
            }

            return(DifferenceType.Unknown);
        }
Пример #5
0
        public override DifferenceType Diff(IDifferences differences, Mappings.MemberMapping mapping)
        {
            ITypeDefinitionMember impl     = mapping[0];
            ITypeDefinitionMember contract = mapping[1];

            if (impl == null)
            {
                return(DifferenceType.Unknown);
            }

            if (contract == null && impl.IsAbstract())
            {
                // If the type is effectively sealed then it is ok to remove abstract members
                ITypeDefinition contractType = mapping.ContainingType[1];
                // We check that interfaces have the same number of members in another rule so there is no need to check that here.
                if (contractType != null && (contractType.IsEffectivelySealed() || (contractType.IsInterface && mapping.ContainingType[0].IsInterface)))
                {
                    return(DifferenceType.Unknown);
                }

                differences.AddIncompatibleDifference(this, impl.GetMemberViolationMessage("Member", $"is abstract in the {Implementation}", $"is missing in the {Contract}"));
                return(DifferenceType.Changed);
            }
            return(DifferenceType.Unknown);
        }
Пример #6
0
        private bool ParamNamesMatch(IDifferences differences, IMethodDefinition implMethod, IMethodDefinition contractMethod)
        {
            int paramCount = implMethod.ParameterCount;

            Contract.Assert(paramCount == contractMethod.ParameterCount);

            IParameterDefinition[] implParams     = implMethod.Parameters.ToArray();
            IParameterDefinition[] contractParams = contractMethod.Parameters.ToArray();

            bool match = true;

            for (int i = 0; i < paramCount; i++)
            {
                IParameterDefinition implParam     = implParams[i];
                IParameterDefinition contractParam = contractParams[i];

                if (!implParam.Name.Value.Equals(contractParam.Name.Value))
                {
                    differences.AddIncompatibleDifference(this,
                                                          "Parameter name on member '{0}' is '{1}' in the implementation but '{2}' in the contract.",
                                                          implMethod.FullName(), implParam.Name.Value, contractParam.Name.Value);
                    match = false;
                }
            }
            return(match);
        }
Пример #7
0
        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);
        }
Пример #8
0
        private bool ParamNamesAndTypesMatch(IDifferences differences, IMethodDefinition implMethod, IMethodDefinition contractMethod)
        {
            int paramCount = implMethod.ParameterCount;

            Contract.Assert(paramCount == contractMethod.ParameterCount);

            IParameterDefinition[] implParams = implMethod.Parameters.ToArray();
            IParameterDefinition[] contractParams = contractMethod.Parameters.ToArray();

            bool match = true;
            for (int i = 0; i < paramCount; i++)
            {
                IParameterDefinition implParam = implParams[i];
                IParameterDefinition contractParam = contractParams[i];

                if (!implParam.Name.Value.Equals(contractParam.Name.Value))
                {
                    differences.AddIncompatibleDifference("DelegateParamNameMustMatch",
                        "Parameter name on delegate '{0}' is '{1}' in the implementation but '{2}' in the contract.",
                        implMethod.ContainingType.FullName(), implParam.Name.Value, contractParam.Name.Value);
                    match = false;
                }

                if (!_typeComparer.Equals(implParam.Type, contractParam.Type))
                {
                    differences.AddTypeMismatchDifference("DelegateParamTypeMustMatch", implParam.Type, contractParam.Type,
                        "Type for parameter '{0}' on delegate '{1}' is '{2}' in the implementation but '{3}' in the contract.",
                        implParam.Name.Value, implMethod.ContainingType.FullName(), implParam.Type.FullName(), contractParam.Type.FullName());
                    match = false;
                }
            }
            return match;
        }
        private bool ParamNamesMatch(IDifferences differences, IMethodDefinition implMethod, IMethodDefinition contractMethod)
        {
            int paramCount = implMethod.ParameterCount;

            Contract.Assert(paramCount == contractMethod.ParameterCount);

            IParameterDefinition[] implParams = implMethod.Parameters.ToArray();
            IParameterDefinition[] contractParams = contractMethod.Parameters.ToArray();

            bool match = true;
            for (int i = 0; i < paramCount; i++)
            {
                IParameterDefinition implParam = implParams[i];
                IParameterDefinition contractParam = contractParams[i];

                if (!implParam.Name.Value.Equals(contractParam.Name.Value))
                {
                    differences.AddIncompatibleDifference(this,
                        "Parameter name on member '{0}' is '{1}' in the implementation but '{2}' in the contract.",
                        implMethod.FullName(), implParam.Name.Value, contractParam.Name.Value);
                    match = false;
                }
            }
            return match;
        }
Пример #10
0
        public override DifferenceType Diff(IDifferences differences, ITypeDefinitionMember impl, ITypeDefinitionMember contract)
        {
            if (impl == null || contract == null)
                return DifferenceType.Unknown;

            bool isImplOverridable = IsOverridable(impl);
            bool isContractOverridable = IsOverridable(contract);

            /*
            //@todo: Move to a separate rule that's only run in "strict" mode. 
            if (isImplInhertiable && !isContractInheritiable)
            {
                // This is separate because it can be noisy and is generally allowed as long as it is reviewed properly.
                differences.AddIncompatibleDifference("CannotMakeMemberVirtual",
                    "Member '{0}' is virtual in the implementation but non-virtual in the contract.",
                    impl.FullName());

                return DifferenceType.Changed;
            }
            */

            if (isContractOverridable && !isImplOverridable)
            {
                differences.AddIncompatibleDifference("CannotMakeMemberNonVirtual",
                    "Member '{0}' is non-virtual in the implementation but is virtual in the contract.",
                    impl.FullName());

                return DifferenceType.Changed;
            }

            return DifferenceType.Unknown;
        }
Пример #11
0
        private bool ParamNamesAndTypesMatch(IDifferences differences, IMethodDefinition implMethod, IMethodDefinition contractMethod)
        {
            int paramCount = implMethod.ParameterCount;

            Debug.Assert(paramCount == contractMethod.ParameterCount);

            IParameterDefinition[] implParams     = implMethod.Parameters.ToArray();
            IParameterDefinition[] contractParams = contractMethod.Parameters.ToArray();

            bool match = true;

            for (int i = 0; i < paramCount; i++)
            {
                IParameterDefinition implParam     = implParams[i];
                IParameterDefinition contractParam = contractParams[i];

                if (!implParam.Name.Value.Equals(contractParam.Name.Value))
                {
                    differences.AddIncompatibleDifference("DelegateParamNameMustMatch",
                                                          $"Parameter name on delegate '{implMethod.ContainingType.FullName()}' is '{implParam.Name.Value}' in the {Implementation} but '{contractParam.Name.Value}' in the {Contract}.");
                    match = false;
                }

                if (!_typeComparer.Equals(implParam.Type, contractParam.Type))
                {
                    differences.AddTypeMismatchDifference("DelegateParamTypeMustMatch", implParam.Type, contractParam.Type,
                                                          $"Type for parameter '{implParam.Name.Value}' on delegate '{implMethod.ContainingType.FullName()}' is '{implParam.Type.FullName()}' in the {Implementation} but '{contractParam.Type.FullName()}' in the {Contract}.");
                    match = false;
                }
            }
            return(match);
        }
Пример #12
0
        public override DifferenceType Diff(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract)
        {
            if (impl == null || contract == null)
            {
                return(DifferenceType.Unknown);
            }

            // If implementation is public then contract can be any visibility
            if (impl.GetVisibility() == TypeMemberVisibility.Public)
            {
                return(DifferenceType.Unknown);
            }

            // If implementation is protected or protected internal then contract must be protected or protected internal as well.
            if (impl.GetVisibility() == TypeMemberVisibility.Family || impl.GetVisibility() == TypeMemberVisibility.FamilyOrAssembly)
            {
                if (contract.GetVisibility() != TypeMemberVisibility.Family && contract.GetVisibility() != TypeMemberVisibility.FamilyOrAssembly)
                {
                    differences.AddIncompatibleDifference(this,
                                                          $"Visibility of type '{impl.FullName()}' is '{impl.GetVisibility()}' in the {Implementation} but '{contract.GetVisibility()}' in the {Contract}.");
                    return(DifferenceType.Changed);
                }
            }

            return(DifferenceType.Unknown);
        }
Пример #13
0
        public override DifferenceType Diff(IDifferences differences, ITypeDefinitionMember impl, ITypeDefinitionMember contract)
        {
            if (impl == null || contract == null)
                return DifferenceType.Unknown;

            if (!impl.ContainingTypeDefinition.IsEnum || !contract.ContainingTypeDefinition.IsEnum)
                return DifferenceType.Unknown;

            IFieldDefinition implField = impl as IFieldDefinition;
            IFieldDefinition contractField = contract as IFieldDefinition;

            Contract.Assert(implField != null || contractField != null);

            string implValue = Convert.ToString(implField.Constant.Value);
            string contractValue = Convert.ToString(contractField.Constant.Value);

            // Calling the toString method to compare in since we might have the case where one Enum is type a and the other is type b, but they might still have same value.
            if (implValue != contractValue)
            {
                ITypeReference implValType = impl.ContainingTypeDefinition.GetEnumType();
                ITypeReference contractValType = contract.ContainingTypeDefinition.GetEnumType();

                differences.AddIncompatibleDifference(this,
                    "Enum value '{0}' is ({1}){2} in the implementation but ({3}){4} in the contract.",
                    implField.FullName(), implValType.FullName(), implField.Constant.Value,
                    contractValType.FullName(), contractField.Constant.Value);
                return DifferenceType.Changed;
            }

            return DifferenceType.Unknown;
        }
Пример #14
0
        public override DifferenceType Diff(IDifferences differences, ITypeDefinitionMember impl, ITypeDefinitionMember contract)
        {
            if (impl == null || contract == null)
            {
                return(DifferenceType.Unknown);
            }

            if (!impl.ContainingTypeDefinition.IsEnum || !contract.ContainingTypeDefinition.IsEnum)
            {
                return(DifferenceType.Unknown);
            }

            IFieldDefinition implField     = impl as IFieldDefinition;
            IFieldDefinition contractField = contract as IFieldDefinition;

            Debug.Assert(implField != null || contractField != null);

            string implValue     = Convert.ToString(implField.Constant.Value);
            string contractValue = Convert.ToString(contractField.Constant.Value);

            // Calling the toString method to compare in since we might have the case where one Enum is type a and the other is type b, but they might still have same value.
            if (implValue != contractValue)
            {
                ITypeReference implValType     = impl.ContainingTypeDefinition.GetEnumType();
                ITypeReference contractValType = contract.ContainingTypeDefinition.GetEnumType();

                differences.AddIncompatibleDifference(this,
                                                      $"Enum value '{implField.FullName()}' is ({implValType.FullName()}){implField.Constant.Value} in the {Implementation} but ({contractValType.FullName()}){contractField.Constant.Value} in the {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 '{0}' does not inherit from base type '{1}' in the implementation but it does in the contract.",
                        contract.FullName(), contractBaseType.FullName());
                    return true;
                }
            }

            return false;
        }
        public override DifferenceType Diff(IDifferences differences, ITypeDefinitionMember impl, ITypeDefinitionMember contract)
        {
            if (impl == null || contract == null)
            {
                return(DifferenceType.Unknown);
            }

            bool isImplOverridable     = IsOverridable(impl);
            bool isContractOverridable = IsOverridable(contract);

            /*
             * //@todo: Move to a separate rule that's only run in "strict" mode.
             * if (isImplInhertiable && !isContractInheritiable)
             * {
             *  // This is separate because it can be noisy and is generally allowed as long as it is reviewed properly.
             *  differences.AddIncompatibleDifference("CannotMakeMemberVirtual",
             *      "Member '{0}' is virtual in the implementation but non-virtual in the contract.",
             *      impl.FullName());
             *
             *  return DifferenceType.Changed;
             * }
             */

            if (isContractOverridable && !isImplOverridable)
            {
                differences.AddIncompatibleDifference("CannotMakeMemberNonVirtual",
                                                      $"Member '{impl.FullName()}' is non-virtual in the {Implementation} but is virtual in the {Contract}.");

                return(DifferenceType.Changed);
            }

            return(DifferenceType.Unknown);
        }
        public override DifferenceType Diff(IDifferences differences, ITypeDefinitionMember impl, ITypeDefinitionMember contract)
        {
            if (impl == null || contract == null)
            {
                return(DifferenceType.Unknown);
            }

            // If implementation is public then contract can be any visibility
            if (impl.Visibility == TypeMemberVisibility.Public)
            {
                return(DifferenceType.Unknown);
            }

            // If implementation is protected or protected internal then contract must be protected or protected internal as well.
            if (impl.Visibility == TypeMemberVisibility.Family || impl.Visibility == TypeMemberVisibility.FamilyOrAssembly)
            {
                if (contract.Visibility != TypeMemberVisibility.Family && contract.Visibility != TypeMemberVisibility.FamilyOrAssembly)
                {
                    differences.AddIncompatibleDifference(this,
                                                          "Visibility of member '{0}' is '{1}' in the implementation but '{2}' in the contract.",
                                                          impl.FullName(), impl.Visibility, contract.Visibility);
                    return(DifferenceType.Changed);
                }
            }

            return(DifferenceType.Unknown);
        }
Пример #18
0
        private DifferenceType DiffConstraints(IDifferences differences, IReference target, IEnumerable <IGenericParameter> implGenericParams, IEnumerable <IGenericParameter> contractGenericParams)
        {
            int beforeCount = differences.Count();

            IGenericParameter[] implParams     = implGenericParams.ToArray();
            IGenericParameter[] contractParams = contractGenericParams.ToArray();

            // We shoudn't hit this because the types/members shouldn't be matched up if they have different generic argument lists
            if (implParams.Length != contractParams.Length)
            {
                return(DifferenceType.Changed);
            }

            for (int i = 0; i < implParams.Length; i++)
            {
                IGenericParameter implParam     = implParams[i];
                IGenericParameter contractParam = contractParams[i];

                if (contractParam.Variance != TypeParameterVariance.NonVariant &&
                    contractParam.Variance != implParam.Variance)
                {
                    differences.AddIncompatibleDifference("CannotChangeVariance",
                                                          "Variance on generic parameter '{0}' for '{1}' is '{2}' in the implementation but '{3}' in the contract.",
                                                          implParam.FullName(), target.FullName(), implParam.Variance, contractParam.Variance);
                }

                string implConstraints     = string.Join(",", GetConstraints(implParam).OrderBy(s => s, StringComparer.OrdinalIgnoreCase));
                string contractConstraints = string.Join(",", GetConstraints(contractParam).OrderBy(s => s, StringComparer.OrdinalIgnoreCase));

                if (!string.Equals(implConstraints, contractConstraints))
                {
                    differences.AddIncompatibleDifference("CannotChangeGenericConstraints",
                                                          "Constraints for generic parameter '{0}' for '{1}' is '{2}' in the implementation but '{3}' in the contract.",
                                                          implParam.FullName(), target.FullName(), implConstraints, contractConstraints);
                }
            }

            if (differences.Count() != beforeCount)
            {
                return(DifferenceType.Changed);
            }

            return(DifferenceType.Unknown);
        }
        private bool ParamModifiersMatch(IDifferences differences, IMethodDefinition implMethod, IMethodDefinition contractMethod)
        {
            int paramCount = implMethod.ParameterCount;

            Contract.Assert(paramCount == contractMethod.ParameterCount);

            if (paramCount == 0)
                return true;

            IParameterDefinition[] implParams = implMethod.Parameters.ToArray();
            IParameterDefinition[] contractParams = contractMethod.Parameters.ToArray();

            bool match = true;
            for (int i = 0; i < paramCount; i++)
            {
                IParameterDefinition implParam = implParams[i];
                IParameterDefinition contractParam = contractParams[i];

                //TODO: Do we care about the compatibility with marshalling attributes Out\In? They don't seem to be set consistently.

                if (GetModifier(implParam) != GetModifier(contractParam))
                {
                    differences.AddIncompatibleDifference(this,
                        "Modifiers on parameter '{0}' on method '{1}' are '{2}' in the implementation but '{3}' in the contract.",
                        implParam.Name.Value, implMethod.FullName(), GetModifier(implParam), GetModifier(contractParam));
                    match = false;
                }

                // Now check custom modifiers, primarily focused on const & volatile
                if (implParam.IsModified || contractParam.IsModified)
                {
                    var union = implParam.CustomModifiers.Union(contractParam.CustomModifiers);
                    if (implParam.CustomModifiers.Count() != union.Count())
                    {
                        differences.AddIncompatibleDifference(this,
                            "Custom modifiers on parameter '{0}' on method '{1}' are '{2}' in the implementation but '{3}' in the contract.",
                            implParam.Name.Value, implMethod.FullName(), PrintCustomModifiers(implParam.CustomModifiers), PrintCustomModifiers(contractParam.CustomModifiers));
                        match = false;
                    }
                }
            }
            return match;
        }
        // TODO: Add support for property parameter lists
        public override DifferenceType Diff(IDifferences differences, MemberMapping mapping)
        {
            IMethodDefinition method1 = mapping[0] as IMethodDefinition;
            IMethodDefinition method2 = mapping[1] as IMethodDefinition;

            if (method1 == null && method2 == null)
            {
                return(DifferenceType.Unknown);
            }

            if (method1 != null && method2 != null)
            {
                return(DifferenceType.Unknown);
            }

            if (method1 != null)
            {
                IMethodDefinition match = FindBestMatch(method1, mapping.ContainingType, 1, 0);

                if (match != null)
                {
                    differences.AddIncompatibleDifference(this,
                                                          "Cannot change parameter types for method {0} and {1}", method1.GetMethodSignature(), match.GetMethodSignature());
                    return(DifferenceType.Changed);
                }
            }

            if (method2 != null)
            {
                IMethodDefinition match = FindBestMatch(method2, mapping.ContainingType, 0, 1);

                if (match != null)
                {
                    differences.AddIncompatibleDifference(this,
                                                          "Cannot change parameter types for method {0} and {1}", method2.GetMethodSignature(), match.GetMethodSignature());
                    return(DifferenceType.Changed);
                }
            }

            return(DifferenceType.Unknown);
        }
Пример #21
0
        public override DifferenceType Diff(IDifferences differences, MemberMapping mapping)
        {
            ITypeDefinitionMember implMember     = mapping[0];
            ITypeDefinitionMember contractMember = mapping[1];
            IMethodDefinition     foundMethod;

            if (!(implMember == null && contractMember != null))
            {
                return(DifferenceType.Unknown);
            }

            // Nested types are handled separately.
            // @TODO: Events and Properties - should we consider these too (or rely on the fact that dropping one of these will also drop their accessors.)
            if (!(contractMember is IMethodDefinition || contractMember is IFieldDefinition))
            {
                return(DifferenceType.Unknown);
            }

            string incompatibeDifferenceMessage = $"Member '{contractMember.FullName()}' does not exist in the {Implementation} but it does exist in the {Contract}.";

            ITypeDefinition contractType = mapping.ContainingType[0];

            if (contractType != null)
            {
                IMethodDefinition contractMethod = contractMember as IMethodDefinition;
                if (contractMethod != null)
                {
                    // If the contract is a Explicit Interface method, we don't need to check if the method is in implementation since that will be caught by different rule.
                    if (contractMethod.IsExplicitInterfaceMethod())
                    {
                        return(DifferenceType.Unknown);
                    }



                    // It is valid to promote a member from a base type up so check to see if it member exits on a base type.
                    var lookForMethodInBaseResult = FindMatchingBase(contractType, contractMethod, out foundMethod);
                    if (lookForMethodInBaseResult == FindMethodResult.Found)
                    {
                        return(DifferenceType.Unknown);
                    }
                    if (lookForMethodInBaseResult == FindMethodResult.ReturnTypeChanged)
                    {
                        incompatibeDifferenceMessage += $" There does exist a member with return type '{foundMethod.GetReturnType().FullName()}' instead of '{contractMethod.GetReturnType().FullName()}'";
                    }
                }
            }

            differences.AddIncompatibleDifference(this, incompatibeDifferenceMessage);

            return(DifferenceType.Added);
        }
Пример #22
0
        public override DifferenceType Diff(IDifferences differences, ITypeDefinitionMember impl, ITypeDefinitionMember contract)
        {
            if (contract != null && impl == null)
            {
                if (contract.ContainingTypeDefinition.IsInterface)
                {
                    differences.AddIncompatibleDifference(this, contract.GetMemberViolationMessage($"{GetNameOfInterfaceMemberType(contract)}", $"is present in the {Contract}", $"not in the {Implementation}"));
                    return(DifferenceType.Changed);
                }
            }

            if (impl != null && contract == null)
            {
                if (impl.ContainingTypeDefinition.IsInterface && !CanIgnoreAddedInterfaceMember(impl))
                {
                    differences.AddIncompatibleDifference(this, impl.GetMemberViolationMessage($"{GetNameOfInterfaceMemberType(impl)}", $"is present in the {Implementation}", $"not in the {Contract}"));
                    return(DifferenceType.Changed);
                }
            }

            return(base.Diff(differences, impl, contract));
        }
        public override DifferenceType Diff(IDifferences differences, ITypeDefinitionMember impl, ITypeDefinitionMember contract)
        {
            if (contract != null && impl == null)
            {
                if (contract.ContainingTypeDefinition.IsInterface)
                {
                    differences.AddIncompatibleDifference(this, $"Interface member '{contract.FullName()}' is present in the {Contract} but not in the {Implementation}.");
                    return(DifferenceType.Changed);
                }
            }

            if (impl != null && contract == null)
            {
                if (impl.ContainingTypeDefinition.IsInterface && !CanIgnoreAddedInterfaceMember(impl))
                {
                    differences.AddIncompatibleDifference(this, $"Interface member '{impl.FullName()}' is present in the {Implementation} but not in the {Contract}.");
                    return(DifferenceType.Changed);
                }
            }

            return(base.Diff(differences, impl, contract));
        }
Пример #24
0
        public override DifferenceType Diff(IDifferences differences, ITypeDefinitionMember impl, ITypeDefinitionMember contract)
        {
            if (contract != null && impl == null)
            {
                if (contract.ContainingTypeDefinition.IsInterface)
                {
                    differences.AddIncompatibleDifference(this, "Contract interface member '{0}' is not in the implementation.", contract.FullName());
                    return(DifferenceType.Changed);
                }
            }

            if (impl != null && contract == null)
            {
                if (impl.ContainingTypeDefinition.IsInterface)
                {
                    differences.AddIncompatibleDifference(this, "Implementation interface member '{0}' is not in the contract.", impl.FullName());
                    return(DifferenceType.Changed);
                }
            }

            return(base.Diff(differences, impl, contract));
        }
        public override DifferenceType Diff(IDifferences differences, ITypeDefinitionMember impl, ITypeDefinitionMember contract)
        {
            if (contract != null && impl == null)
            {
                if (contract.ContainingTypeDefinition.IsInterface)
                {
                    differences.AddIncompatibleDifference(this, "Contract interface member '{0}' is not in the implementation.", contract.FullName());
                    return DifferenceType.Changed;
                }
            }

            if (impl != null && contract == null)
            {
                if (impl.ContainingTypeDefinition.IsInterface)
                {
                    differences.AddIncompatibleDifference(this, "Implementation interface member '{0}' is not in the contract.", impl.FullName());
                    return DifferenceType.Changed;
                }
            }

            return base.Diff(differences, impl, contract);
        }
Пример #26
0
        private DifferenceType DiffConstraints(IDifferences differences, IReference target, IEnumerable<IGenericParameter> implGenericParams, IEnumerable<IGenericParameter> contractGenericParams)
        {
            int beforeCount = differences.Count();
            IGenericParameter[] implParams = implGenericParams.ToArray();
            IGenericParameter[] contractParams = contractGenericParams.ToArray();

            // We shoudn't hit this because the types/members shouldn't be matched up if they have different generic argument lists
            if (implParams.Length != contractParams.Length)
                return DifferenceType.Changed;

            for (int i = 0; i < implParams.Length; i++)
            {
                IGenericParameter implParam = implParams[i];
                IGenericParameter contractParam = contractParams[i];

                if (contractParam.Variance != TypeParameterVariance.NonVariant &&
                    contractParam.Variance != implParam.Variance)
                {
                    differences.AddIncompatibleDifference("CannotChangeVariance",
                        "Variance on generic parameter '{0}' for '{1}' is '{2}' in the implementation but '{3}' in the contract.",
                        implParam.FullName(), target.FullName(), implParam.Variance, contractParam.Variance);
                }

                string implConstraints = string.Join(",", GetConstraints(implParam).OrderBy(s => s));
                string contractConstraints = string.Join(",", GetConstraints(contractParam).OrderBy(s => s));

                if (!string.Equals(implConstraints, contractConstraints))
                {
                    differences.AddIncompatibleDifference("CannotChangeGenericConstraints",
                        "Constraints for generic parameter '{0}' for '{1}' is '{2}' in the implementation but '{3}' in the contract.",
                        implParam.FullName(), target.FullName(), implConstraints, contractConstraints);
                }
            }

            if (differences.Count() != beforeCount)
                return DifferenceType.Changed;

            return DifferenceType.Unknown;
        }
        // TODO: Add support for property parameter lists
        public override DifferenceType Diff(IDifferences differences, MemberMapping mapping)
        {
            IMethodDefinition method1 = mapping[0] as IMethodDefinition;
            IMethodDefinition method2 = mapping[1] as IMethodDefinition;

            if (method1 == null && method2 == null)
                return DifferenceType.Unknown;

            if (method1 != null && method2 != null)
                return DifferenceType.Unknown;

            if (method1 != null)
            {
                IMethodDefinition match = FindBestMatch(method1, mapping.ContainingType, 1, 0);

                if (match != null)
                {
                    differences.AddIncompatibleDifference(this,
                        "Cannot change parameter types for method {0} and {1}", method1.GetMethodSignature(), match.GetMethodSignature());
                    return DifferenceType.Changed;
                }
            }

            if (method2 != null)
            {
                IMethodDefinition match = FindBestMatch(method2, mapping.ContainingType, 0, 1);

                if (match != null)
                {
                    differences.AddIncompatibleDifference(this,
                        "Cannot change parameter types for method {0} and {1}", method2.GetMethodSignature(), match.GetMethodSignature());
                    return DifferenceType.Changed;
                }
            }

            return DifferenceType.Unknown;
        }
        public override DifferenceType Diff(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract)
        {
            if (impl == null || contract == null)
                return DifferenceType.Unknown;

            if (ImplementsIDisposable(impl) && !ImplementsIDisposable(contract))
            {
                differences.AddIncompatibleDifference(this,
                    "Type '{0}' implements IDisposable in the implementation but not the contract.",
                    impl.FullName());
                return DifferenceType.Changed;
            }

            return DifferenceType.Unknown;
        }
Пример #29
0
        public override DifferenceType Diff(IDifferences differences, ITypeDefinitionMember impl, ITypeDefinitionMember contract)
        {
            if (impl == null || contract == null)
            {
                return(DifferenceType.Unknown);
            }

            if (impl.IsAbstract() && !contract.IsAbstract())
            {
                differences.AddIncompatibleDifference("CannotMakeMemberAbstract", impl.GetMemberViolationMessage("Member", $"is abstract in the {Implementation}", $"is not abstract in the {Contract}"));
                return(DifferenceType.Changed);
            }

            return(DifferenceType.Unknown);
        }
Пример #30
0
        public override DifferenceType Diff(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract)
        {
            if (impl == null || contract == null)
                return DifferenceType.Unknown;

            if (impl.IsEffectivelySealed() && !contract.IsEffectivelySealed())
            {
                differences.AddIncompatibleDifference(this,
                    "Type '{0}' is sealed in the implementation but not sealed in the contract.", impl.FullName());

                return DifferenceType.Changed;
            }

            return DifferenceType.Unknown;
        }
Пример #31
0
        public override DifferenceType Diff(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract)
        {
            if (impl == null && contract != null)
            {
                if (!ReportAsMembersMustExist(contract, differences))
                {
                    differences.AddIncompatibleDifference(this,
                                                          "Type '{0}' does not exist in the implementation but it does exist in the contract.", contract.FullName());
                }

                return(DifferenceType.Added);
            }

            return(DifferenceType.Unknown);
        }
Пример #32
0
        public override DifferenceType Diff(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract)
        {
            if (impl == null && contract != null)
            {
                if (!ReportAsMembersMustExist(contract, differences))
                {
                    differences.AddIncompatibleDifference(this,
                        "Type '{0}' does not exist in the implementation but it does exist in the contract.", contract.FullName());
                }

                return DifferenceType.Added;
            }

            return DifferenceType.Unknown;
        }
Пример #33
0
        public override DifferenceType Diff(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract)
        {
            if (impl == null || contract == null)
                return DifferenceType.Unknown;

            if (impl.IsAbstract && !contract.IsAbstract)
            {
                differences.AddIncompatibleDifference("CannotMakeTypeAbstract",
                    "Type '{0}' is abstract in the implementation but is not abstract in the contract.",
                    impl.FullName());

                return DifferenceType.Changed;
            }

            return DifferenceType.Unknown;
        }
        public override DifferenceType Diff(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract)
        {
            if (impl == null || contract == null)
            {
                return(DifferenceType.Unknown);
            }

            if (ImplementsIDisposable(impl) && !ImplementsIDisposable(contract))
            {
                differences.AddIncompatibleDifference(this,
                                                      $"Type '{impl.FullName()}' implements IDisposable in the {Implementation} but not the {Contract}.");
                return(DifferenceType.Changed);
            }

            return(DifferenceType.Unknown);
        }
Пример #35
0
        public override DifferenceType Diff(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract)
        {
            if (impl == null || contract == null)
            {
                return(DifferenceType.Unknown);
            }

            if (impl.IsEffectivelySealed() && !contract.IsEffectivelySealed())
            {
                differences.AddIncompatibleDifference(this,
                                                      "Type '{0}' is sealed in the implementation but not sealed in the contract.", impl.FullName());

                return(DifferenceType.Changed);
            }

            return(DifferenceType.Unknown);
        }
Пример #36
0
        public override DifferenceType Diff(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract)
        {
            if (impl == null || contract == null)
            {
                return(DifferenceType.Unknown);
            }

            if (impl.IsEffectivelySealed() && !contract.IsEffectivelySealed())
            {
                differences.AddIncompatibleDifference(this,
                                                      $"Type '{impl.FullName()}' is {(impl.IsSealed ? "actually (has the sealed modifier)" : "effectively (has a private constructor)")} sealed in the {Implementation} but not sealed in the {Contract}.");

                return(DifferenceType.Changed);
            }

            return(DifferenceType.Unknown);
        }
        public override DifferenceType Diff(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract)
        {
            if (impl == null || contract == null)
                return DifferenceType.Unknown;

            string implObjType = GetObjectType(impl);
            string contractObjType = GetObjectType(contract);

            if (implObjType != contractObjType)
            {
                differences.AddIncompatibleDifference(this,
                    "Type '{0}' is a {1} in the implementation but is a {2} in the contract.",
                    impl.FullName(), implObjType, contractObjType);
            }

            return DifferenceType.Unknown;
        }
        public override DifferenceType Diff(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract)
        {
            if (impl == null || contract == null)
            {
                return(DifferenceType.Unknown);
            }

            if (impl.IsAbstract && !contract.IsAbstract)
            {
                differences.AddIncompatibleDifference("CannotMakeTypeAbstract",
                                                      $"Type '{impl.FullName()}' is abstract in the {Implementation} but is not abstract in the {Contract}.");

                return(DifferenceType.Changed);
            }

            return(DifferenceType.Unknown);
        }
Пример #39
0
        public override DifferenceType Diff(IDifferences differences, ITypeDefinitionMember impl, ITypeDefinitionMember contract)
        {
            if (impl == null || contract == null)
            {
                return(DifferenceType.Unknown);
            }

            if (impl.IsAbstract() && !contract.IsAbstract())
            {
                differences.AddIncompatibleDifference("CannotMakeMemberAbstract",
                                                      "Member '{0}' is abstract in the implementation but is not abstract in the contract.",
                                                      impl.FullName());

                return(DifferenceType.Changed);
            }

            return(DifferenceType.Unknown);
        }
        // Consider the following object hierarchy.  Remember we are not enforcing a subset relationship
        // on both types.  Our goal is to build a third API that is a subset of both, with versioning rules
        // that will produce a consistent universe with both new frameworks.
        //             Object
        //        Fruit        Shape
        //       Apple
        //      Red Delicious
        //
        // 1) A type is not reparented across the hierarchy.  (ie, Apple may subclass Object, but may not subclass Shape)
        // 2) Removing a type like Apple from the inheritance hierarchy is legal, BUT all new methods on Apple must be
        //    duplicated on Red Delicious.  (Methods will be tracked in a separate rule)
        // 3) If you removed a type like Apple, then all overridden methods from Fruit & higher should be
        //    re-overridden by Red Delicious to ensure behavior is compatible.  (methods will be tracked in a separate rule)
        // 4) It is legal to add a type like Fruit into the hierarchy - consider this the reverse of #2.
        // 5) If one Framework had Object -> NewType -> Apple, that is only legal if NewType is in the other
        //    Framework's inheritance hierarchy somewhere between Object and Apple.  IE, NewType could be
        //    "Food" or "RoundFruit", but could not be "Shape"
        public override DifferenceType Diff(IDifferences differences, ITypeDefinition item1, ITypeDefinition item2)
        {
            if (item1 == null || item2 == null)
            {
                return(DifferenceType.Unknown);
            }

            Debug.Assert(_typeComparer != null);

            IEnumerable <ITypeReference> item1BaseClassChain = GetBaseClassChain(item1);
            IEnumerable <ITypeReference> item2BaseClassChain = GetBaseClassChain(item2);

            bool added   = item2BaseClassChain.Except(item1BaseClassChain, _typeComparer).Any(t => true);
            bool removed = item1BaseClassChain.Except(item2BaseClassChain, _typeComparer).Any(t => true);

            // To a first approximation, we cannot both add base types and remove base types.
            // IE, we cannot take an Apple, remove Fruit and add Shape.
            // However there are more pathologically complicated inheritance hierarchies that are linear but where we
            // add one type & remove another.  If both additions & removals occur, they're only legal if one of those
            // added or removed types subclasses the other one.  We do not currently check for that.
            if (added && removed)
            {
                // Special case for DependencyObject and its derived types
                if (item1BaseClassChain.Any((type) => TypeHelper.GetTypeName(type) == "System.Windows.DependencyObject") &&
                    item2BaseClassChain.Any((type) => TypeHelper.GetTypeName(type) == "Windows.UI.DirectUI.DependencyObject"))
                {
                    // If the new type name is the same as the old type name in a new namespace, let's consider that a known issue.
                    String oldBaseTypeName = TypeHelper.GetTypeName(item1BaseClassChain.First());
                    String newBaseTypeName = TypeHelper.GetTypeName(item2BaseClassChain.First());
                    oldBaseTypeName = oldBaseTypeName.Replace("System.Windows", "Windows.UI.DirectUI");
                    if (oldBaseTypeName == newBaseTypeName)
                    {
                        return(DifferenceType.Unknown);
                    }
                }

                differences.AddIncompatibleDifference(this,
                                                      "Type {0} or one of its base classes was reparented in an incompatible way.  Old base classes: {1}  New Base Classes: {2}",
                                                      item1.FullName(), PrintClassHierarchy(item1BaseClassChain), PrintClassHierarchy(item2BaseClassChain));
                return(DifferenceType.Changed);
            }

            return(DifferenceType.Unknown);
        }
        private bool AnyAttributeAdded(IDifferences differences, IReference target, IEnumerable <ICustomAttribute> implAttributes, IEnumerable <ICustomAttribute> contractAttributes)
        {
            bool added = false;

            AttributesMapping <IEnumerable <ICustomAttribute> > attributeMapping = new AttributesMapping <IEnumerable <ICustomAttribute> >(_settings);

            attributeMapping.AddMapping(0, implAttributes);
            attributeMapping.AddMapping(1, contractAttributes);

            foreach (var group in attributeMapping.Attributes)
            {
                switch (group.Difference)
                {
                case DifferenceType.Added:
                    ITypeReference type       = group.Representative.Attributes.First().Type;
                    string         attribName = type.FullName();

                    if (s_IgnorableAttributes.Contains(attribName))
                    {
                        break;
                    }

                    differences.AddIncompatibleDifference(this,
                                                          "Attribute '{0}' exists in the contract but not the implementation.",
                                                          attribName, target.FullName());

                    added = true;

                    break;

                case DifferenceType.Changed:

                    //TODO: Add some more logic to check the two lists of attributes which have the same type.
                    break;

                case DifferenceType.Removed:
                    // Removing attributes is OK
                    break;
                }
            }

            return(added);
        }
Пример #42
0
        public override DifferenceType Diff(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract)
        {
            if (impl == null || contract == null)
            {
                return(DifferenceType.Unknown);
            }

            string implObjType     = GetObjectType(impl);
            string contractObjType = GetObjectType(contract);

            if (implObjType != contractObjType)
            {
                differences.AddIncompatibleDifference(this,
                                                      "Type '{0}' is a {1} in the implementation but is a {2} in the contract.",
                                                      impl.FullName(), implObjType, contractObjType);
            }

            return(DifferenceType.Unknown);
        }
Пример #43
0
        public override DifferenceType Diff(IDifferences differences, MemberMapping mapping)
        {
            ITypeDefinitionMember implMember     = mapping[0];
            ITypeDefinitionMember contractMember = mapping[1];

            if (!(implMember == null && contractMember != null))
            {
                return(DifferenceType.Unknown);
            }

            // Nested types are handled separately.
            // @TODO: Events and Properties - should we consider these too (or rely on the fact that dropping one of these will also drop their accessors.)
            if (!(contractMember is IMethodDefinition || contractMember is IFieldDefinition))
            {
                return(DifferenceType.Unknown);
            }

            string incompatibleDifferenceMessage = contractMember.GetMemberViolationMessage("Member", $"does not exist in the {Implementation}", $"it does exist in the {Contract}");

            ITypeDefinition contractType = mapping.ContainingType[0];

            if (contractType != null)
            {
                if (contractMember is IMethodDefinition contractMethod)
                {
                    // If the contract is a Explicit Interface method, we don't need to check if the method is in implementation since that will be caught by different rule.
                    if (contractMethod.IsExplicitInterfaceMethod())
                    {
                        return(DifferenceType.Unknown);
                    }

                    // It is valid to promote a member from a base type up so check to see if it member exits on a base type.
                    if (FindMatchingMethodOnBase(contractType, contractMethod))
                    {
                        return(DifferenceType.Unknown);
                    }
                }
            }

            differences.AddIncompatibleDifference(this, incompatibleDifferenceMessage);
            return(DifferenceType.Added);
        }
Пример #44
0
        public override DifferenceType Diff(IDifferences differences, MemberMapping mapping)
        {
            ITypeDefinitionMember implMember = mapping[0];
            ITypeDefinitionMember contractMember = mapping[1];
            IMethodDefinition foundMethod;

            if (!(implMember == null && contractMember != null))
                return DifferenceType.Unknown;

            // Nested types are handled separately.
            // @TODO: Events and Properties - should we consider these too (or rely on the fact that dropping one of these will also drop their accessors.) 
            if (!(contractMember is IMethodDefinition || contractMember is IFieldDefinition))
                return DifferenceType.Unknown;

            string incompatibeDifferenceMessage = string.Format("Member '{0}' does not exist in the implementation but it does exist in the contract.", contractMember.FullName());
            string returnTypeChangedMessageFormat = "Member '{0}' does not exist, but there does exist a member with return type '{1}' instead of '{2}'";
            ITypeDefinition contractType = mapping.ContainingType[0];
            if (contractType != null)
            {
                IMethodDefinition contractMethod = contractMember as IMethodDefinition;
                if (contractMethod != null)
                {
                    // If the contract is a Explicit Interface method, we don't need to check if the method is in implementation since that will be catched by different rule.
                    if (contractMethod.IsExplicitInterfaceMethod())
                        return DifferenceType.Unknown;



                    // It is valid to promote a member from a base type up so check to see if it member exits on a base type.
                    var lookForMethodInBaseResult = FindMatchingBase(contractType, contractMethod, out foundMethod);
                    if (lookForMethodInBaseResult == FindMethodResult.Found)
                        return DifferenceType.Unknown;
                    if (lookForMethodInBaseResult == FindMethodResult.ReturnTypeChanged)
                        incompatibeDifferenceMessage = string.Format(returnTypeChangedMessageFormat, foundMethod.FullName(), foundMethod.GetReturnType().FullName(), contractMethod.GetReturnType().FullName());
                }
            }

            differences.AddIncompatibleDifference(this, incompatibeDifferenceMessage);

            return DifferenceType.Added;
        }
        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;
        }
        // Consider the following object hierarchy.  Remember we are not enforcing a subset relationship
        // on both types.  Our goal is to build a third API that is a subset of both, with versioning rules
        // that will produce a consistent universe with both new frameworks.
        //             Object
        //        Fruit        Shape
        //       Apple
        //      Red Delicious
        // 
        // 1) A type is not reparented across the hierarchy.  (ie, Apple may subclass Object, but may not subclass Shape)
        // 2) Removing a type like Apple from the inheritance hierarchy is legal, BUT all new methods on Apple must be
        //    duplicated on Red Delicious.  (Methods will be tracked in a separate rule)
        // 3) If you removed a type like Apple, then all overridden methods from Fruit & higher should be
        //    re-overridden by Red Delicious to ensure behavior is compatible.  (methods will be tracked in a separate rule)
        // 4) It is legal to add a type like Fruit into the hierarchy - consider this the reverse of #2.
        // 5) If one Framework had Object -> NewType -> Apple, that is only legal if NewType is in the other 
        //    Framework's inheritance hierarchy somewhere between Object and Apple.  IE, NewType could be 
        //    "Food" or "RoundFruit", but could not be "Shape"
        public override DifferenceType Diff(IDifferences differences, ITypeDefinition item1, ITypeDefinition item2)
        {
            if (item1 == null || item2 == null)
                return DifferenceType.Unknown;

            Contract.Assert(_typeComparer != null);

            IEnumerable<ITypeReference> item1BaseClassChain = GetBaseClassChain(item1);
            IEnumerable<ITypeReference> item2BaseClassChain = GetBaseClassChain(item2);

            bool added = item2BaseClassChain.Except(item1BaseClassChain, _typeComparer).Any(t => true);
            bool removed = item1BaseClassChain.Except(item2BaseClassChain, _typeComparer).Any(t => true);

            // To a first approximation, we cannot both add base types and remove base types.
            // IE, we cannot take an Apple, remove Fruit and add Shape.
            // However there are more pathologically complicated inheritance hierarchies that are linear but where we 
            // add one type & remove another.  If both additions & removals occur, they're only legal if one of those 
            // added or removed types subclasses the other one.  We do not currently check for that.
            if (added && removed)
            {
                // Special case for DependencyObject and its derived types
                if (item1BaseClassChain.Any((type) => TypeHelper.GetTypeName(type) == "System.Windows.DependencyObject") &&
                    item2BaseClassChain.Any((type) => TypeHelper.GetTypeName(type) == "Windows.UI.DirectUI.DependencyObject"))
                {
                    // If the new type name is the same as the old type name in a new namespace, let's consider that a known issue.
                    String oldBaseTypeName = TypeHelper.GetTypeName(item1BaseClassChain.First());
                    String newBaseTypeName = TypeHelper.GetTypeName(item2BaseClassChain.First());
                    oldBaseTypeName = oldBaseTypeName.Replace("System.Windows", "Windows.UI.DirectUI");
                    if (oldBaseTypeName == newBaseTypeName)
                        return DifferenceType.Unknown;
                }

                differences.AddIncompatibleDifference(this,
                    "Type {0} or one of its base classes was reparented in an incompatible way.  Old base classes: {1}  New Base Classes: {2}",
                    item1.FullName(), PrintClassHierarchy(item1BaseClassChain), PrintClassHierarchy(item2BaseClassChain));
                return DifferenceType.Changed;
            }

            return DifferenceType.Unknown;
        }
        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 '{0}' does not implement interface '{1}' in the implementation but it does in the contract.",
                            contract.FullName(), contractInterface.FullName());
                    return true;
                }
            }

            return false;
        }
Пример #48
0
        public override DifferenceType Diff(IDifferences differences, Mappings.MemberMapping mapping)
        {
            ITypeDefinitionMember impl = mapping[0];
            ITypeDefinitionMember contract = mapping[1];

            if (impl == null)
                return DifferenceType.Unknown;

            if (contract == null && impl.IsAbstract())
            {
                // If the type is effectively sealed then it is ok to remove abstract members
                ITypeDefinition contractType = mapping.ContainingType[1];
                // We check that interfaces have the same number of members in another rule so there is no need to check that here.
                if (contractType != null && (contractType.IsEffectivelySealed() || (contractType.IsInterface && mapping.ContainingType[0].IsInterface)))
                    return DifferenceType.Unknown;

                differences.AddIncompatibleDifference(this,
                    "Member '{0}' is abstract in the implementation but is missing in the contract.",
                    impl.FullName());
                return DifferenceType.Changed;
            }
            return DifferenceType.Unknown;
        }
Пример #49
0
        public override DifferenceType Diff(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract)
        {
            if (impl == null || contract == null)
                return DifferenceType.Unknown;

            // If implementation is public then contract can be any visibility
            if (impl.GetVisibility() == TypeMemberVisibility.Public)
                return DifferenceType.Unknown;

            // If implementation is protected then contract must be protected as well. 
            if (impl.GetVisibility() == TypeMemberVisibility.Family)
            {
                if (contract.GetVisibility() != TypeMemberVisibility.Family)
                {
                    differences.AddIncompatibleDifference(this,
                        "Visibility of type '{0}' is '{1}' in the implementation but '{2}' in the contract.",
                        impl.FullName(), impl.GetVisibility(), contract.GetVisibility());
                    return DifferenceType.Changed;
                }
            }

            return DifferenceType.Unknown;
        }
Пример #50
0
        private bool AnyAttributeAdded(IDifferences differences, IReference target, IEnumerable<ICustomAttribute> implAttributes, IEnumerable<ICustomAttribute> contractAttributes)
        {
            bool added = false;

            AttributesMapping<IEnumerable<ICustomAttribute>> attributeMapping = new AttributesMapping<IEnumerable<ICustomAttribute>>(_settings);
            attributeMapping.AddMapping(0, implAttributes);
            attributeMapping.AddMapping(1, contractAttributes);

            foreach (var group in attributeMapping.Attributes)
            {
                switch (group.Difference)
                {
                    case DifferenceType.Added:
                        ITypeReference type = group.Representative.Attributes.First().Type;
                        string attribName = type.FullName();

                        if (s_IgnorableAttributes.Contains(attribName))
                            break;

                        differences.AddIncompatibleDifference(this,
                            "Attribute '{0}' exists in the contract but not the implementation.",
                            attribName, target.FullName());

                        added = true;

                        break;
                    case DifferenceType.Changed:

                        //TODO: Add some more logic to check the two lists of attributes which have the same type.
                        break;

                    case DifferenceType.Removed:
                        // Removing attributes is OK
                        break;
                }
            }

            return added;
        }
Пример #51
0
        //private bool AnySecurityAttributeAdded(IDifferences differences, IReference target, IEnumerable<ISecurityAttribute> attribues1, IEnumerable<ISecurityAttribute> attributes2)
        //{
        //    return AnyAttributeAdded(differences, target, attribues1.SelectMany(a => a.Attributes), attributes2.SelectMany(a => a.Attributes));
        //}

        private void CheckAttributeDifferences(IDifferences differences, IReference target, IEnumerable<ICustomAttribute> implAttributes, IEnumerable<ICustomAttribute> contractAttributes)
        {
            AttributesMapping<IEnumerable<ICustomAttribute>> attributeMapping = new AttributesMapping<IEnumerable<ICustomAttribute>>(_settings);
            attributeMapping.AddMapping(0, contractAttributes);
            attributeMapping.AddMapping(1, implAttributes);

            foreach (var group in attributeMapping.Attributes)
            {
                switch (group.Difference)
                {
                    case DifferenceType.Added:
                        {
                            ITypeReference type = group.Representative.Attributes.First().Type;
                            string attribName = type.FullName();

                            if (FilterAttribute(attribName))
                                break;

                            AttributeAdded(target, attribName);

                            differences.AddIncompatibleDifference("CannotAddAttribute",
                                "Attribute '{0}' exists on '{1}' in the implementation but not the contract.",
                                attribName, target.FullName());

                            break;
                        }
                    case DifferenceType.Changed:

                        //TODO: Add some more logic to check the two lists of attributes which have the same type.
                        break;

                    case DifferenceType.Removed:
                        {
                            ITypeReference type = group.Representative.Attributes.First().Type;
                            string attribName = type.FullName();

                            if (s_IgnorableAttributes.Contains(attribName))
                                break;

                            differences.AddIncompatibleDifference("CannotRemoveAttribute",
                                "Attribute '{0}' exists on '{1}' in the contract but not the implementation.",
                                attribName, target.FullName());

                            break;
                        }
                }
            }
        }