File: CodeDomFixup\CodeDomVisitors\AttributeFixer.cs
Web Access
Project: src\src\dotnet-svcutil\lib\src\dotnet-svcutil-lib.csproj (dotnet-svcutil-lib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Diagnostics.CodeAnalysis;
using System.ServiceModel;
using System.ServiceModel.Description;
using Microsoft.CodeDom;
using Microsoft.CodeDom.Compiler;
 
namespace Microsoft.Tools.ServiceModel.Svcutil
{
    internal class AttributeFixer : CodeDomVisitor
    {
        private static readonly string s_excludeFromCodeCoverageAttributeFullName = typeof(ExcludeFromCodeCoverageAttribute).FullName;
 
        private static Type[] s_attrsToRemove = new Type[]
                    {
                        typeof(TransactionFlowAttribute),
                    };
 
        private static string[] s_serviceContractPropsToRemove = new string[]
                    {
                        "ProtectionLevel",
                        "SessionMode",
                    };
 
        private static string[] s_operationContractPropsToRemove = new string[]
                    {
                        "ProtectionLevel",
                        "IsInitiating",
                        "IsTerminating",
                    };
 
        private static string[] s_faultContractPropsToRemove = new string[]
                    {
                        "ProtectionLevel"
                    };
 
        public AttributeFixer(ServiceContractGenerator generator)
        {
            System.Diagnostics.Debug.Assert(generator != null);
        }
 
        protected override void Visit(CodeTypeDeclaration type)
        {
            base.Visit(type);
 
            CollectionHelpers.MapList<CodeAttributeDeclaration>(type.CustomAttributes, IsValidAttribute, null);
 
            FixupGeneratedCodeAndEnsureExcludeFromCoverage(type.CustomAttributes);
        }
 
        protected override void Visit(CodeTypeMember member)
        {
            base.Visit(member);
 
            CollectionHelpers.MapList<CodeAttributeDeclaration>(member.CustomAttributes, IsValidAttribute, null);
 
            // Runs before `CodeDomVisitor` enumerates `member.CustomAttributes`.
            FixupGeneratedCodeAndEnsureExcludeFromCoverage(member.CustomAttributes);
        }
 
        protected override void Visit(CodeAttributeDeclaration attr)
        {
            base.Visit(attr);
 
            string[] propsToRemove = null;
            if (CodeDomHelpers.MatchType(attr.AttributeType, typeof(ServiceContractAttribute)))
            {
                propsToRemove = s_serviceContractPropsToRemove;
            }
            else if (CodeDomHelpers.MatchType(attr.AttributeType, typeof(OperationContractAttribute)))
            {
                propsToRemove = s_operationContractPropsToRemove;
            }
            else if (CodeDomHelpers.MatchType(attr.AttributeType, typeof(FaultContractAttribute)))
            {
                propsToRemove = s_faultContractPropsToRemove;
            }
 
            if (propsToRemove != null)
            {
                CollectionHelpers.MapList<CodeAttributeArgument>(attr.Arguments,
                    delegate (CodeAttributeArgument arg)
                    {
                        return IsValidProperty(propsToRemove, arg.Name);
                    },
                    null
                );
            }
        }
 
        protected override void FinishVisit(CodeCompileUnit cu)
        {
        }
 
        private static bool IsValidProperty(string[] propsToRemove, string prop)
        {
            for (int i = 0; i < propsToRemove.Length; i++)
            {
                if (propsToRemove[i] == prop)
                    return false;
            }
            return true;
        }
 
        private static bool IsValidAttribute(CodeAttributeDeclaration attr)
        {
            for (int i = 0; i < s_attrsToRemove.Length; i++)
            {
                if (CodeDomHelpers.MatchType(attr.AttributeType, s_attrsToRemove[i]))
                    return false;
            }
            return true;
        }
 
        private static void FixupGeneratedCodeAndEnsureExcludeFromCoverage(CodeAttributeDeclarationCollection attributes)
        {
            if (attributes == null || attributes.Count == 0)
            {
                return;
            }
 
            bool hasGeneratedCode = false;
            bool hasExcludeFromCoverage = false;
 
            for (int i = 0; i < attributes.Count; i++)
            {
                CodeAttributeDeclaration attr = attributes[i];
                if (attr == null)
                {
                    continue;
                }
                if (CodeDomHelpers.MatchType(attr.AttributeType, typeof(GeneratedCodeAttribute)))
                {
                    hasGeneratedCode = true;
                    attr.Arguments.Clear();
                    attr.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(Tool.ToolName)));
                    attr.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(Tool.PackageVersion)));
                }
                else if (IsExcludeFromCodeCoverageAttribute(attr.AttributeType))
                {
                    hasExcludeFromCoverage = true;
                }
 
                if (hasGeneratedCode && hasExcludeFromCoverage)
                {
                    break;
                }
            }
 
            if (hasGeneratedCode && !hasExcludeFromCoverage)
            {
                attributes.Add(new CodeAttributeDeclaration(new CodeTypeReference(s_excludeFromCodeCoverageAttributeFullName)));
            }
        }
 
        private static bool IsExcludeFromCodeCoverageAttribute(CodeTypeReference attributeType)
        {
            if (attributeType == null)
            {
                return false;
            }
 
            string baseType = attributeType.BaseType;
            if (string.IsNullOrEmpty(baseType))
            {
                return false;
            }
 
            return baseType == s_excludeFromCodeCoverageAttributeFullName ||
                   baseType == "System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage" ||
                   baseType == "ExcludeFromCodeCoverageAttribute" ||
                   baseType == "ExcludeFromCodeCoverage" ||
                   baseType.EndsWith(".ExcludeFromCodeCoverageAttribute", StringComparison.Ordinal) ||
                   baseType.EndsWith(".ExcludeFromCodeCoverage", StringComparison.Ordinal);
        }
    }
}