File: Symbols\Source\SourceMethodSymbolWithAttributes.cs
Web Access
Project: src\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp)
// 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.
 
#nullable disable
 
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Collections;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
    /// <summary>
    /// A source method that can have attributes, including a member method, accessor, or local function.
    /// </summary>
    internal abstract partial class SourceMethodSymbol : IAttributeTargetSymbol
    {
        private CustomAttributesBag<CSharpAttributeData> _lazyCustomAttributesBag;
        private CustomAttributesBag<CSharpAttributeData> _lazyReturnTypeCustomAttributesBag;
 
        // some symbols may not have a syntax (e.g. lambdas, synthesized event accessors)
        protected readonly SyntaxReference syntaxReferenceOpt;
        protected SourceMethodSymbol(SyntaxReference syntaxReferenceOpt)
        {
            this.syntaxReferenceOpt = syntaxReferenceOpt;
        }
 
#nullable enable
        /// <summary>
        /// Gets the syntax node used for the in-method binder.
        /// </summary>
        protected CSharpSyntaxNode? GetInMethodSyntaxNode()
        {
            switch (SyntaxNode)
            {
                case ConstructorDeclarationSyntax constructor:
                    return constructor.Initializer ?? (CSharpSyntaxNode?)constructor.Body ?? constructor.ExpressionBody;
                case BaseMethodDeclarationSyntax method:
                    return (CSharpSyntaxNode?)method.Body ?? method.ExpressionBody;
                case AccessorDeclarationSyntax accessor:
                    return (CSharpSyntaxNode?)accessor.Body ?? accessor.ExpressionBody;
                case ArrowExpressionClauseSyntax arrowExpression:
                    Debug.Assert(arrowExpression.Parent!.Kind() == SyntaxKind.PropertyDeclaration ||
                                 arrowExpression.Parent.Kind() == SyntaxKind.IndexerDeclaration);
                    return arrowExpression;
                case LocalFunctionStatementSyntax localFunction:
                    return (CSharpSyntaxNode?)localFunction.Body ?? localFunction.ExpressionBody;
                case CompilationUnitSyntax _ when this is SynthesizedSimpleProgramEntryPointSymbol entryPoint:
                    return (CSharpSyntaxNode)entryPoint.ReturnTypeSyntax;
                case RecordDeclarationSyntax recordDecl:
                    Debug.Assert(recordDecl.IsKind(SyntaxKind.RecordDeclaration));
                    return recordDecl;
                case ClassDeclarationSyntax classDecl:
                    return classDecl;
                default:
                    return null;
            }
        }
 
        internal virtual Binder? OuterBinder => null;
 
        internal virtual Binder? WithTypeParametersBinder => null;
 
#nullable disable
 
        internal SyntaxReference SyntaxRef
        {
            get
            {
                return this.syntaxReferenceOpt;
            }
        }
 
        internal CSharpSyntaxNode SyntaxNode
        {
            get
            {
                return (this.syntaxReferenceOpt == null) ? null : (CSharpSyntaxNode)this.syntaxReferenceOpt.GetSyntax();
            }
        }
 
        internal SyntaxTree SyntaxTree
        {
            get
            {
                return this.syntaxReferenceOpt == null ? null : this.syntaxReferenceOpt.SyntaxTree;
            }
        }
 
        public override ImmutableArray<SyntaxReference> DeclaringSyntaxReferences
        {
            get
            {
                return (this.syntaxReferenceOpt == null) ? ImmutableArray<SyntaxReference>.Empty : ImmutableArray.Create(this.syntaxReferenceOpt);
            }
        }
 
        public override FlowAnalysisAnnotations ReturnTypeFlowAnalysisAnnotations =>
            DecodeReturnTypeAnnotationAttributes(GetDecodedReturnTypeWellKnownAttributeData());
 
        public override ImmutableHashSet<string> ReturnNotNullIfParameterNotNull
            => GetDecodedReturnTypeWellKnownAttributeData()?.NotNullIfParameterNotNull ?? ImmutableHashSet<string>.Empty;
 
        /// <summary>
        /// Symbol to copy bound attributes from, or null if the attributes are not shared among multiple source method symbols.
        /// </summary>
        /// <remarks>
        /// Used for example for event accessors. The "remove" method delegates attribute binding to the "add" method.
        /// The bound attribute data are then applied to both accessors.
        /// </remarks>
        protected virtual SourceMemberMethodSymbol BoundAttributesSource
        {
            get
            {
                return null;
            }
        }
 
        protected virtual IAttributeTargetSymbol AttributeOwner
        {
            get { return this; }
        }
 
        protected virtual AttributeLocation AttributeLocationForLoadAndValidateAttributes
        {
            get { return AttributeLocation.None; }
        }
 
        IAttributeTargetSymbol IAttributeTargetSymbol.AttributesOwner
        {
            get { return this.AttributeOwner; }
        }
 
        AttributeLocation IAttributeTargetSymbol.DefaultAttributeLocation
        {
            get { return AttributeLocation.Method; }
        }
 
        AttributeLocation IAttributeTargetSymbol.AllowedAttributeLocations
        {
            get
            {
                switch (MethodKind)
                {
                    case MethodKind.Constructor:
                    case MethodKind.Destructor:
                    case MethodKind.StaticConstructor:
                        return AttributeLocation.Method;
 
                    case MethodKind.PropertySet:
                    case MethodKind.EventRemove:
                    case MethodKind.EventAdd:
                        return AttributeLocation.Method | AttributeLocation.Return | AttributeLocation.Parameter;
 
                    default:
                        return AttributeLocation.Method | AttributeLocation.Return;
                }
            }
        }
 
        /// <summary>
        /// Gets the syntax list of custom attributes that declares attributes for this method symbol.
        /// </summary>
        internal virtual OneOrMany<SyntaxList<AttributeListSyntax>> GetAttributeDeclarations()
        {
            return OneOrMany.Create(default(SyntaxList<AttributeListSyntax>));
        }
 
        /// <summary>
        /// Gets the syntax list of custom attributes that declares attributes for return type of this method.
        /// </summary>
        internal virtual OneOrMany<SyntaxList<AttributeListSyntax>> GetReturnTypeAttributeDeclarations()
        {
            // Usually the same list as other attributes applied on the method, but e.g.
            // constructors and destructors do not allow return-type attributes, so this is empty.
            return GetAttributeDeclarations();
        }
 
        /// <summary>
        /// Returns data decoded from special early bound well-known attributes applied to the symbol or null if there are no applied attributes.
        /// </summary>
        /// <remarks>
        /// Forces binding and decoding of attributes.
        /// </remarks>
        internal MethodEarlyWellKnownAttributeData GetEarlyDecodedWellKnownAttributeData()
        {
            var attributesBag = _lazyCustomAttributesBag;
            if (attributesBag == null || !attributesBag.IsEarlyDecodedWellKnownAttributeDataComputed)
            {
                attributesBag = this.GetAttributesBag();
            }
 
            return (MethodEarlyWellKnownAttributeData)attributesBag.EarlyDecodedWellKnownAttributeData;
        }
 
        /// <summary>
        /// Returns data decoded from well-known attributes applied to the symbol or null if there are no applied attributes.
        /// </summary>
        /// <remarks>
        /// Forces binding and decoding of attributes.
        /// </remarks>
        protected MethodWellKnownAttributeData GetDecodedWellKnownAttributeData()
        {
            var attributesBag = _lazyCustomAttributesBag;
            if (attributesBag == null || !attributesBag.IsDecodedWellKnownAttributeDataComputed)
            {
                attributesBag = this.GetAttributesBag();
            }
 
            return (MethodWellKnownAttributeData)attributesBag.DecodedWellKnownAttributeData;
        }
 
        /// <summary>
        /// Returns information retrieved from custom attributes on return type in source, or null if the symbol is not source symbol or there are none.
        /// </summary>
        /// <remarks>
        /// Forces binding and decoding of attributes.
        /// </remarks>
        internal ReturnTypeWellKnownAttributeData GetDecodedReturnTypeWellKnownAttributeData()
        {
            var attributesBag = _lazyReturnTypeCustomAttributesBag;
            if (attributesBag == null || !attributesBag.IsDecodedWellKnownAttributeDataComputed)
            {
                attributesBag = this.GetReturnTypeAttributesBag();
            }
 
            return (ReturnTypeWellKnownAttributeData)attributesBag.DecodedWellKnownAttributeData;
        }
 
        /// <summary>
        /// Returns a bag of applied custom attributes and data decoded from well-known attributes. Returns null if there are no attributes applied on the symbol.
        /// </summary>
        /// <remarks>
        /// Forces binding and decoding of attributes.
        /// </remarks>
        private CustomAttributesBag<CSharpAttributeData> GetAttributesBag()
        {
            var bag = _lazyCustomAttributesBag;
            if (bag != null && bag.IsSealed)
            {
                return bag;
            }
 
            return GetAttributesBag(ref _lazyCustomAttributesBag, forReturnType: false);
        }
 
        /// <summary>
        /// Returns a bag of custom attributes applied on the method return value and data decoded from well-known attributes. Returns null if there are no attributes.
        /// </summary>
        /// <remarks>
        /// Forces binding and decoding of attributes.
        /// </remarks>
        private CustomAttributesBag<CSharpAttributeData> GetReturnTypeAttributesBag()
        {
            var bag = _lazyReturnTypeCustomAttributesBag;
            if (bag != null && bag.IsSealed)
            {
                return bag;
            }
 
            return GetAttributesBag(ref _lazyReturnTypeCustomAttributesBag, forReturnType: true);
        }
 
        private CustomAttributesBag<CSharpAttributeData> GetAttributesBag(ref CustomAttributesBag<CSharpAttributeData> lazyCustomAttributesBag, bool forReturnType)
        {
            var copyFrom = this.BoundAttributesSource;
 
            // prevent infinite recursion:
            Debug.Assert(!ReferenceEquals(copyFrom, this));
 
            bool bagCreatedOnThisThread;
            if ((object)copyFrom != null)
            {
                var attributesBag = forReturnType ? copyFrom.GetReturnTypeAttributesBag() : copyFrom.GetAttributesBag();
                bagCreatedOnThisThread = Interlocked.CompareExchange(ref lazyCustomAttributesBag, attributesBag, null) == null;
            }
            else
            {
                var (declarations, symbolPart) = forReturnType
                    ? (GetReturnTypeAttributeDeclarations(), AttributeLocation.Return)
                    : (GetAttributeDeclarations(), AttributeLocationForLoadAndValidateAttributes);
                bagCreatedOnThisThread = LoadAndValidateAttributes(
                    declarations,
                    ref lazyCustomAttributesBag,
                    symbolPart,
                    binderOpt: OuterBinder);
            }
 
            if (bagCreatedOnThisThread)
            {
                NoteAttributesComplete(forReturnType);
            }
 
            return lazyCustomAttributesBag;
        }
 
        /// <summary>
        /// Called when this thread loaded the method's attributes. For method symbols with completion state.
        /// </summary>
        protected abstract void NoteAttributesComplete(bool forReturnType);
 
        /// <summary>
        /// Gets the attributes applied on this symbol.
        /// Returns an empty array if there are no attributes.
        /// </summary>
        public override ImmutableArray<CSharpAttributeData> GetAttributes()
        {
            return this.GetAttributesBag().Attributes;
        }
 
        /// <summary>
        /// Gets the attributes applied on the return value of this method symbol.
        /// Returns an empty array if there are no attributes.
        /// </summary>
        public override ImmutableArray<CSharpAttributeData> GetReturnTypeAttributes()
        {
            return this.GetReturnTypeAttributesBag().Attributes;
        }
 
#nullable enable
        internal override (CSharpAttributeData?, BoundAttribute?) EarlyDecodeWellKnownAttribute(ref EarlyDecodeWellKnownAttributeArguments<EarlyWellKnownAttributeBinder, NamedTypeSymbol, AttributeSyntax, AttributeLocation> arguments)
        {
            Debug.Assert(arguments.SymbolPart == AttributeLocation.None || arguments.SymbolPart == AttributeLocation.Return);
 
            bool hasAnyDiagnostics;
 
            if (arguments.SymbolPart == AttributeLocation.None)
            {
                if (CSharpAttributeData.IsTargetEarlyAttribute(arguments.AttributeType, arguments.AttributeSyntax, AttributeDescription.ConditionalAttribute))
                {
                    var (attributeData, boundAttribute) = arguments.Binder.GetAttribute(arguments.AttributeSyntax, arguments.AttributeType, beforeAttributePartBound: null, afterAttributePartBound: null, out hasAnyDiagnostics);
                    if (!attributeData.HasErrors)
                    {
                        string? name = attributeData.GetConstructorArgument<string>(0, SpecialType.System_String);
                        arguments.GetOrCreateData<MethodEarlyWellKnownAttributeData>().AddConditionalSymbol(name);
                        if (!hasAnyDiagnostics)
                        {
                            return (attributeData, boundAttribute);
                        }
                    }
 
                    return (null, null);
                }
                else if (EarlyDecodeDeprecatedOrExperimentalOrObsoleteAttribute(ref arguments, out CSharpAttributeData? attributeData, out BoundAttribute? boundAttribute, out ObsoleteAttributeData? obsoleteData))
                {
                    if (obsoleteData != null)
                    {
                        arguments.GetOrCreateData<MethodEarlyWellKnownAttributeData>().ObsoleteAttributeData = obsoleteData;
                    }
 
                    return (attributeData, boundAttribute);
                }
                else if (CSharpAttributeData.IsTargetEarlyAttribute(arguments.AttributeType, arguments.AttributeSyntax, AttributeDescription.UnmanagedCallersOnlyAttribute))
                {
                    arguments.GetOrCreateData<MethodEarlyWellKnownAttributeData>().UnmanagedCallersOnlyAttributePresent = true;
                    // We can't actually decode this attribute yet: CallConvs is an array, and it cannot be bound yet or we could hit a cycle
                    // in error cases. We only detect whether or not the attribute is present for use in ensuring that we create as few lazily-computed
                    // diagnostics that might later get thrown away as possible when binding method calls.
                    return (null, null);
                }
                else if (CSharpAttributeData.IsTargetEarlyAttribute(arguments.AttributeType, arguments.AttributeSyntax, AttributeDescription.OverloadResolutionPriorityAttribute))
                {
                    if (!CanHaveOverloadResolutionPriority)
                    {
                        // Cannot use 'OverloadResolutionPriorityAttribute' on this member.
                        return (null, null);
                    }
 
                    (attributeData, boundAttribute) = arguments.Binder.GetAttribute(arguments.AttributeSyntax, arguments.AttributeType, beforeAttributePartBound: null, afterAttributePartBound: null, out hasAnyDiagnostics);
 
                    if (attributeData.CommonConstructorArguments is [{ ValueInternal: int priority }])
                    {
                        arguments.GetOrCreateData<MethodEarlyWellKnownAttributeData>().OverloadResolutionPriority = priority;
 
                        if (!hasAnyDiagnostics)
                        {
                            return (attributeData, boundAttribute);
                        }
                    }
 
                    return (null, null);
                }
            }
 
            return base.EarlyDecodeWellKnownAttribute(ref arguments);
        }
 
        /// <summary>
        /// Binds attributes applied to this method.
        /// </summary>
        public ImmutableArray<(CSharpAttributeData, BoundAttribute)> BindMethodAttributes()
        {
            return BindAttributes(GetAttributeDeclarations(), OuterBinder);
        }
#nullable disable
 
        public override bool AreLocalsZeroed
        {
            get
            {
                var data = this.GetDecodedWellKnownAttributeData();
                return data?.HasSkipLocalsInitAttribute != true && AreContainingSymbolLocalsZeroed;
            }
        }
 
        /// <summary>
        /// Returns data decoded from Obsolete attribute or null if there is no Obsolete attribute.
        /// This property returns ObsoleteAttributeData.Uninitialized if attribute arguments haven't been decoded yet.
        /// </summary>
        internal sealed override ObsoleteAttributeData ObsoleteAttributeData
        {
            get
            {
                if (ContainingSymbol is SourceMemberContainerTypeSymbol { AnyMemberHasAttributes: false })
                {
                    return null;
                }
 
                var lazyCustomAttributesBag = _lazyCustomAttributesBag;
                if (lazyCustomAttributesBag != null && lazyCustomAttributesBag.IsEarlyDecodedWellKnownAttributeDataComputed)
                {
                    var data = (MethodEarlyWellKnownAttributeData)lazyCustomAttributesBag.EarlyDecodedWellKnownAttributeData;
                    return data != null ? data.ObsoleteAttributeData : null;
                }
 
                if (syntaxReferenceOpt is null)
                {
                    // no references -> no attributes
                    return null;
                }
 
                return ObsoleteAttributeData.Uninitialized;
            }
        }
 
#nullable enable
        internal sealed override UnmanagedCallersOnlyAttributeData? GetUnmanagedCallersOnlyAttributeData(bool forceComplete)
        {
            if (syntaxReferenceOpt is null)
            {
                // no references -> no attributes
                return null;
            }
 
            if (forceComplete)
            {
                _ = this.GetAttributes();
            }
 
            var lazyCustomAttributesBag = _lazyCustomAttributesBag;
            if (lazyCustomAttributesBag is null || !lazyCustomAttributesBag.IsEarlyDecodedWellKnownAttributeDataComputed)
            {
                Debug.Assert(!forceComplete);
                return UnmanagedCallersOnlyAttributeData.Uninitialized;
            }
 
            if (lazyCustomAttributesBag.IsDecodedWellKnownAttributeDataComputed)
            {
                var lateData = (MethodWellKnownAttributeData?)lazyCustomAttributesBag.DecodedWellKnownAttributeData;
 
#if DEBUG
                verifyDataConsistent((MethodEarlyWellKnownAttributeData?)lazyCustomAttributesBag.EarlyDecodedWellKnownAttributeData, lateData);
#endif
 
                return lateData?.UnmanagedCallersOnlyAttributeData;
            }
 
            var earlyData = (MethodEarlyWellKnownAttributeData?)lazyCustomAttributesBag.EarlyDecodedWellKnownAttributeData;
            Debug.Assert(!forceComplete);
            return earlyData?.UnmanagedCallersOnlyAttributePresent == true
                ? UnmanagedCallersOnlyAttributeData.AttributePresentDataNotBound
                : null;
 
#if DEBUG // Can remove ifdefs and replace with Conditional after https://github.com/dotnet/roslyn/issues/47463 is fixed
            static void verifyDataConsistent(MethodEarlyWellKnownAttributeData? earlyData, MethodWellKnownAttributeData? lateData)
            {
                if (lateData is { UnmanagedCallersOnlyAttributeData: not null })
                {
                    // We can't verify the symmetric case here. Error conditions (such as if a bad expression was provided to the array initializer)
                    // can cause the attribute to be skipped during regular attribute binding. Early binding doesn't know that though, so
                    // it still gets marked as present.
                    Debug.Assert(earlyData is { UnmanagedCallersOnlyAttributePresent: true });
                }
                else if (earlyData is null or { UnmanagedCallersOnlyAttributePresent: false })
                {
                    Debug.Assert(lateData is null or { UnmanagedCallersOnlyAttributeData: null });
                }
            }
#endif
        }
#nullable disable
 
        internal sealed override ImmutableArray<string> GetAppliedConditionalSymbols()
        {
            MethodEarlyWellKnownAttributeData data = this.GetEarlyDecodedWellKnownAttributeData();
            return data != null ? data.ConditionalSymbols : ImmutableArray<string>.Empty;
        }
 
        protected override void DecodeWellKnownAttributeImpl(ref DecodeWellKnownAttributeArguments<AttributeSyntax, CSharpAttributeData, AttributeLocation> arguments)
        {
            Debug.Assert(!arguments.Attribute.HasErrors);
            Debug.Assert(arguments.SymbolPart == AttributeLocation.None || arguments.SymbolPart == AttributeLocation.Return);
 
            if (arguments.SymbolPart == AttributeLocation.None)
            {
                DecodeWellKnownAttributeAppliedToMethod(ref arguments);
            }
            else
            {
                DecodeWellKnownAttributeAppliedToReturnValue(ref arguments);
            }
        }
 
        private void DecodeWellKnownAttributeAppliedToMethod(ref DecodeWellKnownAttributeArguments<AttributeSyntax, CSharpAttributeData, AttributeLocation> arguments)
        {
            Debug.Assert((object)arguments.AttributeSyntaxOpt != null);
 
            var attribute = arguments.Attribute;
            var diagnostics = (BindingDiagnosticBag)arguments.Diagnostics;
            Debug.Assert(!attribute.HasErrors);
 
            if (attribute.IsTargetAttribute(AttributeDescription.PreserveSigAttribute))
            {
                arguments.GetOrCreateData<MethodWellKnownAttributeData>().SetPreserveSignature(arguments.Index);
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.MethodImplAttribute))
            {
                AttributeData.DecodeMethodImplAttribute<MethodWellKnownAttributeData, AttributeSyntax, CSharpAttributeData, AttributeLocation>(ref arguments, MessageProvider.Instance);
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.DllImportAttribute))
            {
                DecodeDllImportAttribute(ref arguments);
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.SpecialNameAttribute))
            {
                arguments.GetOrCreateData<MethodWellKnownAttributeData>().HasSpecialNameAttribute = true;
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.ExcludeFromCodeCoverageAttribute))
            {
                arguments.GetOrCreateData<MethodWellKnownAttributeData>().HasExcludeFromCodeCoverageAttribute = true;
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.ConditionalAttribute))
            {
                ValidateConditionalAttribute(attribute, arguments.AttributeSyntaxOpt, diagnostics);
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.SuppressUnmanagedCodeSecurityAttribute))
            {
                arguments.GetOrCreateData<MethodWellKnownAttributeData>().HasSuppressUnmanagedCodeSecurityAttribute = true;
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.DynamicSecurityMethodAttribute))
            {
                arguments.GetOrCreateData<MethodWellKnownAttributeData>().HasDynamicSecurityMethodAttribute = true;
            }
            else if (VerifyObsoleteAttributeAppliedToMethod(ref arguments, AttributeDescription.ObsoleteAttribute))
            {
            }
            else if (VerifyObsoleteAttributeAppliedToMethod(ref arguments, AttributeDescription.DeprecatedAttribute))
            {
            }
            else if (ReportExplicitUseOfReservedAttributes(in arguments,
                ReservedAttributes.IsReadOnlyAttribute |
                ReservedAttributes.RequiresLocationAttribute |
                ReservedAttributes.IsUnmanagedAttribute |
                ReservedAttributes.IsByRefLikeAttribute |
                ReservedAttributes.NullableContextAttribute |
                ReservedAttributes.CaseSensitiveExtensionAttribute))
            {
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.SecurityCriticalAttribute)
                || attribute.IsTargetAttribute(AttributeDescription.SecuritySafeCriticalAttribute))
            {
                if (IsAsync)
                {
                    diagnostics.Add(ErrorCode.ERR_SecurityCriticalOrSecuritySafeCriticalOnAsync, arguments.AttributeSyntaxOpt.Location, arguments.AttributeSyntaxOpt.GetErrorDisplayName());
                }
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.SkipLocalsInitAttribute))
            {
                CSharpAttributeData.DecodeSkipLocalsInitAttribute<MethodWellKnownAttributeData>(DeclaringCompilation, ref arguments);
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.DoesNotReturnAttribute))
            {
                arguments.GetOrCreateData<MethodWellKnownAttributeData>().HasDoesNotReturnAttribute = true;
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.MemberNotNullAttribute))
            {
                MessageID.IDS_FeatureMemberNotNull.CheckFeatureAvailability(diagnostics, arguments.AttributeSyntaxOpt);
                CSharpAttributeData.DecodeMemberNotNullAttribute<MethodWellKnownAttributeData>(ContainingType, ref arguments);
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.MemberNotNullWhenAttribute))
            {
                MessageID.IDS_FeatureMemberNotNull.CheckFeatureAvailability(diagnostics, arguments.AttributeSyntaxOpt);
                CSharpAttributeData.DecodeMemberNotNullWhenAttribute<MethodWellKnownAttributeData>(ContainingType, ref arguments);
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.ModuleInitializerAttribute))
            {
                MessageID.IDS_FeatureModuleInitializers.CheckFeatureAvailability(diagnostics, arguments.AttributeSyntaxOpt);
                DecodeModuleInitializerAttribute(arguments);
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.UnmanagedCallersOnlyAttribute))
            {
                DecodeUnmanagedCallersOnlyAttribute(ref arguments);
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.UnscopedRefAttribute))
            {
                if (!this.UseUpdatedEscapeRules)
                {
                    diagnostics.Add(ErrorCode.WRN_UnscopedRefAttributeOldRules, arguments.AttributeSyntaxOpt.Location);
                }
 
                if (this.IsValidUnscopedRefAttributeTarget())
                {
                    arguments.GetOrCreateData<MethodWellKnownAttributeData>().HasUnscopedRefAttribute = true;
 
                    if (ContainingType.IsInterface || IsExplicitInterfaceImplementation)
                    {
                        MessageID.IDS_FeatureRefStructInterfaces.CheckFeatureAvailability(diagnostics, arguments.AttributeSyntaxOpt);
                    }
                }
                else
                {
                    diagnostics.Add(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, arguments.AttributeSyntaxOpt.Location);
                }
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.InterceptsLocationAttribute))
            {
                DecodeInterceptsLocationAttribute(arguments);
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.OverloadResolutionPriorityAttribute))
            {
                MessageID.IDS_FeatureOverloadResolutionPriority.CheckFeatureAvailability(diagnostics, arguments.AttributeSyntaxOpt);
 
                if (!CanHaveOverloadResolutionPriority)
                {
                    diagnostics.Add(IsOverride
                            // Cannot use 'OverloadResolutionPriorityAttribute' on an overriding member.
                            ? ErrorCode.ERR_CannotApplyOverloadResolutionPriorityToOverride
                            // Cannot use 'OverloadResolutionPriorityAttribute' on this member.
                            : ErrorCode.ERR_CannotApplyOverloadResolutionPriorityToMember,
                        arguments.AttributeSyntaxOpt);
                }
            }
            else
            {
                var compilation = this.DeclaringCompilation;
                if (attribute.IsSecurityAttribute(compilation))
                {
                    attribute.DecodeSecurityAttribute<MethodWellKnownAttributeData>(this, compilation, ref arguments);
                }
            }
        }
 
        internal override ImmutableArray<string> NotNullMembers =>
            GetDecodedWellKnownAttributeData()?.NotNullMembers ?? ImmutableArray<string>.Empty;
 
        internal override ImmutableArray<string> NotNullWhenTrueMembers =>
            GetDecodedWellKnownAttributeData()?.NotNullWhenTrueMembers ?? ImmutableArray<string>.Empty;
 
        internal override ImmutableArray<string> NotNullWhenFalseMembers =>
            GetDecodedWellKnownAttributeData()?.NotNullWhenFalseMembers ?? ImmutableArray<string>.Empty;
 
        public override FlowAnalysisAnnotations FlowAnalysisAnnotations
        {
            get
            {
                return DecodeFlowAnalysisAttributes(GetDecodedWellKnownAttributeData());
            }
        }
 
        private static FlowAnalysisAnnotations DecodeFlowAnalysisAttributes(MethodWellKnownAttributeData attributeData)
            => attributeData?.HasDoesNotReturnAttribute == true ? FlowAnalysisAnnotations.DoesNotReturn : FlowAnalysisAnnotations.None;
 
        internal sealed override bool HasUnscopedRefAttribute => GetDecodedWellKnownAttributeData()?.HasUnscopedRefAttribute == true;
 
        private bool VerifyObsoleteAttributeAppliedToMethod(
            ref DecodeWellKnownAttributeArguments<AttributeSyntax, CSharpAttributeData, AttributeLocation> arguments,
            AttributeDescription description)
        {
            if (arguments.Attribute.IsTargetAttribute(description))
            {
                if (this.IsAccessor())
                {
                    var diagnostics = (BindingDiagnosticBag)arguments.Diagnostics;
 
                    if (this is SourceEventAccessorSymbol)
                    {
                        // CS1667: Attribute '{0}' is not valid on event accessors. It is only valid on '{1}' declarations.
                        AttributeUsageInfo attributeUsage = arguments.Attribute.AttributeClass.GetAttributeUsageInfo();
                        diagnostics.Add(ErrorCode.ERR_AttributeNotOnEventAccessor, arguments.AttributeSyntaxOpt.Name.Location, description.FullName, attributeUsage.GetValidTargetsErrorArgument());
                    }
                    else
                    {
                        MessageID.IDS_FeatureObsoleteOnPropertyAccessor.CheckFeatureAvailability(diagnostics, arguments.AttributeSyntaxOpt);
                    }
                }
 
                return true;
            }
 
            return false;
        }
 
        private void ValidateConditionalAttribute(CSharpAttributeData attribute, AttributeSyntax node, BindingDiagnosticBag diagnostics)
        {
            Debug.Assert(this.IsConditional);
 
            if (this.IsAccessor())
            {
                // CS1667: Attribute '{0}' is not valid on property or event accessors. It is only valid on '{1}' declarations.
                AttributeUsageInfo attributeUsage = attribute.AttributeClass.GetAttributeUsageInfo();
                diagnostics.Add(ErrorCode.ERR_AttributeNotOnAccessor, node.Name.Location, node.GetErrorDisplayName(), attributeUsage.GetValidTargetsErrorArgument());
            }
            else if (this.ContainingType.IsInterfaceType())
            {
                // CS0582: The Conditional attribute is not valid on interface members
                diagnostics.Add(ErrorCode.ERR_ConditionalOnInterfaceMethod, node.Location);
            }
            else if (this.IsOverride)
            {
                // CS0243: The Conditional attribute is not valid on '{0}' because it is an override method
                diagnostics.Add(ErrorCode.ERR_ConditionalOnOverride, node.Location, this);
            }
            else if (!this.CanBeReferencedByName || this.MethodKind == MethodKind.Destructor)
            {
                // CS0577: The Conditional attribute is not valid on '{0}' because it is a constructor, destructor, operator, lambda expression, or explicit interface implementation
                diagnostics.Add(ErrorCode.ERR_ConditionalOnSpecialMethod, node.Location, this);
            }
            else if (!this.ReturnsVoid)
            {
                // CS0578: The Conditional attribute is not valid on '{0}' because its return type is not void
                diagnostics.Add(ErrorCode.ERR_ConditionalMustReturnVoid, node.Location, this);
            }
            else if (this.HasAnyOutParameter())
            {
                // CS0685: Conditional member '{0}' cannot have an out parameter
                diagnostics.Add(ErrorCode.ERR_ConditionalWithOutParam, node.Location, this);
            }
            else if (this is { MethodKind: MethodKind.LocalFunction, IsStatic: false })
            {
                diagnostics.Add(ErrorCode.ERR_ConditionalOnLocalFunction, node.Location, this);
            }
            else
            {
                string name = attribute.GetConstructorArgument<string>(0, SpecialType.System_String);
 
                if (name == null || !SyntaxFacts.IsValidIdentifier(name))
                {
                    // CS0633: The argument to the '{0}' attribute must be a valid identifier
                    diagnostics.Add(ErrorCode.ERR_BadArgumentToAttribute, attribute.GetAttributeArgumentLocation(0), node.GetErrorDisplayName());
                }
            }
        }
 
        private bool HasAnyOutParameter()
        {
            foreach (var param in this.Parameters)
            {
                if (param.RefKind == RefKind.Out)
                {
                    return true;
                }
            }
 
            return false;
        }
 
        private void DecodeWellKnownAttributeAppliedToReturnValue(ref DecodeWellKnownAttributeArguments<AttributeSyntax, CSharpAttributeData, AttributeLocation> arguments)
        {
            Debug.Assert((object)arguments.AttributeSyntaxOpt != null);
 
            var attribute = arguments.Attribute;
            var diagnostics = (BindingDiagnosticBag)arguments.Diagnostics;
            Debug.Assert(!attribute.HasErrors);
 
            if (attribute.IsTargetAttribute(AttributeDescription.MarshalAsAttribute))
            {
                // MarshalAs applied to the return value:
                MarshalAsAttributeDecoder<ReturnTypeWellKnownAttributeData, AttributeSyntax, CSharpAttributeData, AttributeLocation>.Decode(ref arguments, AttributeTargets.ReturnValue, MessageProvider.Instance);
            }
            else if (ReportExplicitUseOfReservedAttributes(in arguments,
                ReservedAttributes.DynamicAttribute |
                ReservedAttributes.IsUnmanagedAttribute |
                ReservedAttributes.IsReadOnlyAttribute |
                ReservedAttributes.RequiresLocationAttribute |
                ReservedAttributes.IsByRefLikeAttribute |
                ReservedAttributes.TupleElementNamesAttribute |
                ReservedAttributes.NullableAttribute |
                ReservedAttributes.NativeIntegerAttribute))
            {
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.MaybeNullAttribute))
            {
                arguments.GetOrCreateData<ReturnTypeWellKnownAttributeData>().HasMaybeNullAttribute = true;
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.NotNullAttribute))
            {
                arguments.GetOrCreateData<ReturnTypeWellKnownAttributeData>().HasNotNullAttribute = true;
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.NotNullIfNotNullAttribute))
            {
                arguments.GetOrCreateData<ReturnTypeWellKnownAttributeData>().AddNotNullIfParameterNotNull(attribute.DecodeNotNullIfNotNullAttribute());
            }
        }
 
#nullable enable
        private void DecodeDllImportAttribute(ref DecodeWellKnownAttributeArguments<AttributeSyntax, CSharpAttributeData, AttributeLocation> arguments)
        {
            RoslynDebug.Assert(arguments.AttributeSyntaxOpt?.ArgumentList is object);
 
            var attribute = arguments.Attribute;
            var diagnostics = (BindingDiagnosticBag)arguments.Diagnostics;
            Debug.Assert(!attribute.HasErrors);
            bool hasErrors = false;
 
            var implementationPart = this.PartialImplementationPart ?? this;
            if (!implementationPart.IsExtern || !implementationPart.IsStatic)
            {
                diagnostics.Add(ErrorCode.ERR_DllImportOnInvalidMethod, arguments.AttributeSyntaxOpt.Name.Location);
                hasErrors = true;
            }
 
            var isAnyNestedMethodGeneric = false;
            for (MethodSymbol? current = this; current is object; current = current.ContainingSymbol as MethodSymbol)
            {
                if (current.IsGenericMethod)
                {
                    isAnyNestedMethodGeneric = true;
                    break;
                }
            }
 
            if (isAnyNestedMethodGeneric || ContainingType?.IsGenericType == true)
            {
                diagnostics.Add(ErrorCode.ERR_DllImportOnGenericMethod, arguments.AttributeSyntaxOpt.Name.Location);
                hasErrors = true;
            }
 
            string? moduleName = attribute.GetConstructorArgument<string>(0, SpecialType.System_String);
            if (!MetadataHelpers.IsValidMetadataIdentifier(moduleName))
            {
                // Dev10 reports CS0647: "Error emitting attribute ..."
                diagnostics.Add(ErrorCode.ERR_InvalidAttributeArgument, attribute.GetAttributeArgumentLocation(0), arguments.AttributeSyntaxOpt.GetErrorDisplayName());
                hasErrors = true;
                moduleName = null;
            }
 
            // Default value of charset is inherited from the module (only if specified).
            // This might be different from ContainingType.DefaultMarshallingCharSet. If the charset is not specified on module
            // ContainingType.DefaultMarshallingCharSet would be Ansi (the class is emitted with "Ansi" charset metadata flag) 
            // while the charset in P/Invoke metadata should be "None".
            CharSet charSet = this.GetEffectiveDefaultMarshallingCharSet() ?? Cci.Constants.CharSet_None;
 
            string? importName = null;
            bool preserveSig = true;
            CallingConvention callingConvention = System.Runtime.InteropServices.CallingConvention.Winapi;
            bool setLastError = false;
            bool exactSpelling = false;  // C#: ExactSpelling=false for any charset
            bool? bestFitMapping = null;
            bool? throwOnUnmappable = null;
 
            int position = 1;
            foreach (var namedArg in attribute.CommonNamedArguments)
            {
                switch (namedArg.Key)
                {
                    case "EntryPoint":
                        importName = namedArg.Value.ValueInternal as string;
                        if (!MetadataHelpers.IsValidMetadataIdentifier(importName))
                        {
                            // Dev10 reports CS0647: "Error emitting attribute ..."
                            diagnostics.Add(ErrorCode.ERR_InvalidNamedArgument, arguments.AttributeSyntaxOpt.ArgumentList.Arguments[position].Location, namedArg.Key);
                            hasErrors = true;
                            importName = null;
                        }
 
                        break;
 
                    case "CharSet":
                        // invalid values will be ignored
                        charSet = namedArg.Value.DecodeValue<CharSet>(SpecialType.System_Enum);
                        break;
 
                    case "SetLastError":
                        // invalid values will be ignored
                        setLastError = namedArg.Value.DecodeValue<bool>(SpecialType.System_Boolean);
                        break;
 
                    case "ExactSpelling":
                        // invalid values will be ignored
                        exactSpelling = namedArg.Value.DecodeValue<bool>(SpecialType.System_Boolean);
                        break;
 
                    case "PreserveSig":
                        preserveSig = namedArg.Value.DecodeValue<bool>(SpecialType.System_Boolean);
                        break;
 
                    case "CallingConvention":
                        // invalid values will be ignored
                        callingConvention = namedArg.Value.DecodeValue<CallingConvention>(SpecialType.System_Enum);
                        break;
 
                    case "BestFitMapping":
                        bestFitMapping = namedArg.Value.DecodeValue<bool>(SpecialType.System_Boolean);
                        break;
 
                    case "ThrowOnUnmappableChar":
                        throwOnUnmappable = namedArg.Value.DecodeValue<bool>(SpecialType.System_Boolean);
                        break;
                }
 
                position++;
            }
 
            if (!hasErrors)
            {
                arguments.GetOrCreateData<MethodWellKnownAttributeData>().SetDllImport(
                    arguments.Index,
                    moduleName,
                    importName ?? Name,
                    DllImportData.MakeFlags(
                        exactSpelling,
                        charSet,
                        setLastError,
                        callingConvention,
                        bestFitMapping,
                        throwOnUnmappable),
                    preserveSig);
            }
        }
 
        private void DecodeModuleInitializerAttribute(DecodeWellKnownAttributeArguments<AttributeSyntax, CSharpAttributeData, AttributeLocation> arguments)
        {
            Debug.Assert(arguments.AttributeSyntaxOpt is object);
            var diagnostics = (BindingDiagnosticBag)arguments.Diagnostics;
 
            if (MethodKind != MethodKind.Ordinary)
            {
                diagnostics.Add(ErrorCode.ERR_ModuleInitializerMethodMustBeOrdinary, arguments.AttributeSyntaxOpt.Location);
                return;
            }
 
            Debug.Assert(ContainingType is object);
            var hasError = false;
 
            var useSiteInfo = new CompoundUseSiteInfo<AssemblySymbol>(diagnostics, ContainingAssembly);
            if (!AccessCheck.IsSymbolAccessible(this, ContainingAssembly, ref useSiteInfo))
            {
                diagnostics.Add(ErrorCode.ERR_ModuleInitializerMethodMustBeAccessibleOutsideTopLevelType, arguments.AttributeSyntaxOpt.Location, Name);
                hasError = true;
            }
 
            diagnostics.Add(arguments.AttributeSyntaxOpt, useSiteInfo);
 
            if (!IsStatic || ParameterCount > 0 || !ReturnsVoid || IsAbstract || IsVirtual)
            {
                diagnostics.Add(ErrorCode.ERR_ModuleInitializerMethodMustBeStaticParameterlessVoid, arguments.AttributeSyntaxOpt.Location, Name);
                hasError = true;
            }
 
            if (IsGenericMethod || ContainingType.IsGenericType)
            {
                diagnostics.Add(ErrorCode.ERR_ModuleInitializerMethodAndContainingTypesMustNotBeGeneric, arguments.AttributeSyntaxOpt.Location, Name);
                hasError = true;
            }
 
            // If this is an UnmanagedCallersOnly method, it means that this cannot be called by managed code, including the attempt by the CLR
            // to run the module initializer.
            if (_lazyCustomAttributesBag.EarlyDecodedWellKnownAttributeData is MethodEarlyWellKnownAttributeData { UnmanagedCallersOnlyAttributePresent: true })
            {
                diagnostics.Add(ErrorCode.ERR_ModuleInitializerCannotBeUnmanagedCallersOnly, arguments.AttributeSyntaxOpt.Location);
                hasError = true;
            }
 
            if (!hasError && !CallsAreOmitted(arguments.AttributeSyntaxOpt.SyntaxTree))
            {
                DeclaringCompilation.AddModuleInitializerMethod(this);
            }
        }
 
        private void DecodeInterceptsLocationAttribute(DecodeWellKnownAttributeArguments<AttributeSyntax, CSharpAttributeData, AttributeLocation> arguments)
        {
            Debug.Assert(!arguments.Attribute.HasErrors);
            var constructorArguments = arguments.Attribute.CommonConstructorArguments;
            if (constructorArguments is [
                { Type.SpecialType: SpecialType.System_String },
                { Kind: not TypedConstantKind.Array, Value: int lineNumberOneBased },
                { Kind: not TypedConstantKind.Array, Value: int characterNumberOneBased }])
            {
                DecodeInterceptsLocationAttributeExperimentalCompat(arguments, attributeFilePath: (string?)constructorArguments[0].Value, lineNumberOneBased, characterNumberOneBased);
            }
            else
            {
                Debug.Assert(arguments.Attribute.AttributeConstructor.Parameters is [{ Type.SpecialType: SpecialType.System_Int32 }, { Type.SpecialType: SpecialType.System_String }]);
                DecodeInterceptsLocationChecksumBased(arguments, version: (int)constructorArguments[0].Value!, data: (string?)constructorArguments[1].Value);
            }
        }
 
        private void DecodeInterceptsLocationChecksumBased(DecodeWellKnownAttributeArguments<AttributeSyntax, CSharpAttributeData, AttributeLocation> arguments, int version, string? data)
        {
            var diagnostics = (BindingDiagnosticBag)arguments.Diagnostics;
            Debug.Assert(arguments.AttributeSyntaxOpt is not null);
            var attributeNameSyntax = arguments.AttributeSyntaxOpt.Name; // used for reporting diagnostics
            var attributeLocation = attributeNameSyntax.Location;
 
            if (version != 1)
            {
                diagnostics.Add(ErrorCode.ERR_InterceptsLocationUnsupportedVersion, attributeLocation, version);
                return;
            }
 
            if (InterceptableLocation1.Decode(data) is not var (hash, position, displayFileName))
            {
                diagnostics.Add(ErrorCode.ERR_InterceptsLocationDataInvalidFormat, attributeLocation);
                return;
            }
 
            var interceptorsNamespaces = ((CSharpParseOptions)attributeNameSyntax.SyntaxTree.Options).InterceptorsNamespaces;
            var thisNamespaceNames = getNamespaceNames(this);
            var foundAnyMatch = interceptorsNamespaces.Any(static (ns, thisNamespaceNames) => isDeclaredInNamespace(thisNamespaceNames, ns), thisNamespaceNames);
            if (!foundAnyMatch)
            {
                reportFeatureNotEnabled(diagnostics, attributeLocation, thisNamespaceNames);
                thisNamespaceNames.Free();
                return;
            }
            thisNamespaceNames.Free();
 
            if (ContainingType.IsGenericType)
            {
                diagnostics.Add(ErrorCode.ERR_InterceptorContainingTypeCannotBeGeneric, attributeLocation, this);
                return;
            }
 
            if (MethodKind != MethodKind.Ordinary)
            {
                diagnostics.Add(ErrorCode.ERR_InterceptorMethodMustBeOrdinary, attributeLocation);
                return;
            }
 
            Debug.Assert(_lazyCustomAttributesBag.IsEarlyDecodedWellKnownAttributeDataComputed);
            var unmanagedCallersOnly = this.GetUnmanagedCallersOnlyAttributeData(forceComplete: false);
            if (unmanagedCallersOnly != null)
            {
                diagnostics.Add(ErrorCode.ERR_InterceptorCannotUseUnmanagedCallersOnly, attributeLocation);
                return;
            }
 
            var matchingTrees = DeclaringCompilation.GetSyntaxTreesByContentHash(hash);
            if (matchingTrees.Count > 1)
            {
                diagnostics.Add(ErrorCode.ERR_InterceptsLocationDuplicateFile, attributeLocation, displayFileName);
                return;
            }
 
            if (matchingTrees.Count == 0)
            {
                diagnostics.Add(ErrorCode.ERR_InterceptsLocationFileNotFound, attributeLocation, displayFileName);
                return;
            }
 
            Debug.Assert(matchingTrees.Count == 1);
            SyntaxTree? matchingTree = matchingTrees[0];
 
            var root = matchingTree.GetRoot();
            if (position < 0 || position > root.EndPosition)
            {
                diagnostics.Add(ErrorCode.ERR_InterceptsLocationDataInvalidPosition, attributeLocation, displayFileName);
                return;
            }
 
            var referencedLines = matchingTree.GetText().Lines;
            var referencedLineCount = referencedLines.Count;
            var referencedToken = root.FindToken(position);
            switch (referencedToken)
            {
                case { Parent: SimpleNameSyntax { Parent: MemberAccessExpressionSyntax { Parent: InvocationExpressionSyntax } memberAccess } rhs } when memberAccess.Name == rhs:
                case { Parent: SimpleNameSyntax { Parent: MemberBindingExpressionSyntax { Parent: InvocationExpressionSyntax } memberBinding } rhs1 } when memberBinding.Name == rhs1:
                case { Parent: SimpleNameSyntax { Parent: InvocationExpressionSyntax invocation } simpleName } when invocation.Expression == simpleName:
                    // happy case
                    break;
                case { Parent: SimpleNameSyntax { Parent: not (MemberAccessExpressionSyntax or MemberBindingExpressionSyntax) } }:
                case { Parent: SimpleNameSyntax { Parent: MemberAccessExpressionSyntax memberAccess } rhs } when memberAccess.Name == rhs:
                case { Parent: SimpleNameSyntax { Parent: MemberBindingExpressionSyntax memberBinding } rhs1 } when memberBinding.Name == rhs1:
                    // NB: there are all sorts of places "simple names" can appear in syntax. With these checks we are trying to
                    // minimize confusion about why the name being used is not *interceptable*, but it's done on a best-effort basis.
 
                    diagnostics.Add(ErrorCode.ERR_InterceptorNameNotInvoked, attributeLocation, referencedToken.Text);
                    return;
                default:
                    diagnostics.Add(ErrorCode.ERR_InterceptorPositionBadToken, attributeLocation, referencedToken.Text);
                    return;
            }
 
            if (position != referencedToken.Position)
            {
                diagnostics.Add(ErrorCode.ERR_InterceptsLocationDataInvalidPosition, attributeLocation, displayFileName);
                return;
            }
 
            DeclaringCompilation.AddInterception(matchingTree.GetText().GetContentHash(), position, attributeLocation, this);
 
            // Caller must free the returned builder.
            static ArrayBuilder<string> getNamespaceNames(SourceMethodSymbol @this)
            {
                var namespaceNames = ArrayBuilder<string>.GetInstance();
                for (var containingNamespace = @this.ContainingNamespace; containingNamespace?.IsGlobalNamespace == false; containingNamespace = containingNamespace.ContainingNamespace)
                    namespaceNames.Add(containingNamespace.Name);
                // order outermost->innermost
                // e.g. for method MyApp.Generated.Interceptors.MyInterceptor(): ["MyApp", "Generated", "Interceptors"]
                namespaceNames.ReverseContents();
                return namespaceNames;
            }
 
            static bool isDeclaredInNamespace(ArrayBuilder<string> thisNamespaceNames, ImmutableArray<string> namespaceSegments)
            {
                Debug.Assert(namespaceSegments.Length > 0);
                if (namespaceSegments is ["global"])
                {
                    return true;
                }
 
                if (namespaceSegments.Length > thisNamespaceNames.Count)
                {
                    // the enabled NS has more components than interceptor's NS, so it will never match.
                    return false;
                }
 
                for (var i = 0; i < namespaceSegments.Length; i++)
                {
                    if (namespaceSegments[i] != thisNamespaceNames[i])
                    {
                        return false;
                    }
                }
                return true;
            }
 
            static void reportFeatureNotEnabled(BindingDiagnosticBag diagnostics, Location attributeLocation, ArrayBuilder<string> namespaceNames)
            {
                if (namespaceNames.Count == 0)
                {
                    diagnostics.Add(ErrorCode.ERR_InterceptorGlobalNamespace, attributeLocation);
                }
                else
                {
                    var recommendedProperty = $"<InterceptorsNamespaces>$(InterceptorsNamespaces);{string.Join(".", namespaceNames)}</InterceptorsNamespaces>";
                    diagnostics.Add(ErrorCode.ERR_InterceptorsFeatureNotEnabled, attributeLocation, recommendedProperty);
                }
            }
        }
 
        private void DecodeInterceptsLocationAttributeExperimentalCompat(
            DecodeWellKnownAttributeArguments<AttributeSyntax, CSharpAttributeData, AttributeLocation> arguments,
            string? attributeFilePath,
            int lineNumberOneBased,
            int characterNumberOneBased)
        {
            var diagnostics = (BindingDiagnosticBag)arguments.Diagnostics;
            var attributeSyntax = arguments.AttributeSyntaxOpt;
            Debug.Assert(attributeSyntax is object);
            var attributeLocation = attributeSyntax.Location;
            diagnostics.Add(ErrorCode.WRN_InterceptsLocationAttributeUnsupportedSignature, attributeLocation);
 
            const int filePathParameterIndex = 0;
            const int lineNumberParameterIndex = 1;
            const int characterNumberParameterIndex = 2;
 
            var interceptorsNamespaces = ((CSharpParseOptions)attributeSyntax.SyntaxTree.Options).InterceptorsNamespaces;
            var thisNamespaceNames = getNamespaceNames();
            var foundAnyMatch = interceptorsNamespaces.Any(ns => isDeclaredInNamespace(thisNamespaceNames, ns));
            if (!foundAnyMatch)
            {
                reportFeatureNotEnabled(diagnostics, attributeSyntax, thisNamespaceNames);
                thisNamespaceNames.Free();
                return;
            }
            thisNamespaceNames.Free();
 
            var attributeData = arguments.Attribute;
            if (attributeFilePath is null)
            {
                diagnostics.Add(ErrorCode.ERR_InterceptorFilePathCannotBeNull, attributeData.GetAttributeArgumentLocation(filePathParameterIndex));
                return;
            }
 
            if (ContainingType.IsGenericType)
            {
                diagnostics.Add(ErrorCode.ERR_InterceptorContainingTypeCannotBeGeneric, attributeLocation, this);
                return;
            }
 
            if (MethodKind != MethodKind.Ordinary)
            {
                diagnostics.Add(ErrorCode.ERR_InterceptorMethodMustBeOrdinary, attributeLocation);
                return;
            }
 
            Debug.Assert(_lazyCustomAttributesBag.IsEarlyDecodedWellKnownAttributeDataComputed);
            var unmanagedCallersOnly = this.GetUnmanagedCallersOnlyAttributeData(forceComplete: false);
            if (unmanagedCallersOnly != null)
            {
                diagnostics.Add(ErrorCode.ERR_InterceptorCannotUseUnmanagedCallersOnly, attributeLocation);
                return;
            }
 
            var normalizedPath = FileUtilities.GetNormalizedPathOrOriginalPath(attributeFilePath, basePath: SyntaxTree.FilePath);
            var matchingTrees = DeclaringCompilation.GetSyntaxTreesByPath(normalizedPath);
            if (matchingTrees.Count > 1)
            {
                diagnostics.Add(ErrorCode.ERR_InterceptorNonUniquePath, attributeData.GetAttributeArgumentLocation(filePathParameterIndex), normalizedPath);
                return;
            }
 
            if (matchingTrees.Count == 0)
            {
                // Temporary compat behavior: check if 'attributeFilePath' is equivalent to a mapped path of one of the syntax trees in the compilation.
                matchingTrees = DeclaringCompilation.GetSyntaxTreesByMappedPath(attributeFilePath);
 
                if (matchingTrees.Count > 1)
                {
                    diagnostics.Add(ErrorCode.ERR_InterceptorNonUniquePath, attributeData.GetAttributeArgumentLocation(filePathParameterIndex), attributeFilePath);
                    return;
                }
            }
 
            // Neither the primary or compat methods of resolving the file path found any match.
            if (matchingTrees.Count == 0)
            {
                // if we expect '/src/Program.cs':
                // we might get: 'Program.cs' <-- suffix match
                var syntaxTrees = DeclaringCompilation.SyntaxTrees;
                var suffixMatch = syntaxTrees.FirstOrDefault(static (tree, attributeFilePathWithForwardSlashes)
                    => tree.FilePath
                        .Replace('\\', '/')
                        .EndsWith(attributeFilePathWithForwardSlashes),
                    attributeFilePath.Replace('\\', '/'));
                if (suffixMatch != null)
                {
                    var recommendedPath = PathUtilities.IsAbsolute(SyntaxTree.FilePath)
                        ? PathUtilities.GetRelativePath(PathUtilities.GetDirectoryName(SyntaxTree.FilePath), suffixMatch.FilePath)
                        : suffixMatch.FilePath;
                    diagnostics.Add(
                        ErrorCode.ERR_InterceptorPathNotInCompilationWithCandidate,
                        attributeData.GetAttributeArgumentLocation(filePathParameterIndex),
                        attributeFilePath,
                        recommendedPath);
                    return;
                }
 
                diagnostics.Add(ErrorCode.ERR_InterceptorPathNotInCompilation, attributeData.GetAttributeArgumentLocation(filePathParameterIndex), normalizedPath);
 
                return;
            }
 
            Debug.Assert(matchingTrees.Count == 1);
            SyntaxTree? matchingTree = matchingTrees[0];
            // Internally, line and character numbers are 0-indexed, but when they appear in code or diagnostic messages, they are 1-indexed.
            int lineNumberZeroBased = lineNumberOneBased - 1;
            int characterNumberZeroBased = characterNumberOneBased - 1;
 
            if (lineNumberZeroBased < 0 || characterNumberZeroBased < 0)
            {
                var location = attributeData.GetAttributeArgumentLocation(lineNumberZeroBased < 0 ? lineNumberParameterIndex : characterNumberParameterIndex);
                diagnostics.Add(ErrorCode.ERR_InterceptorLineCharacterMustBePositive, location);
                return;
            }
 
            var referencedLines = matchingTree.GetText().Lines;
            var referencedLineCount = referencedLines.Count;
 
            if (lineNumberZeroBased >= referencedLineCount)
            {
                diagnostics.Add(ErrorCode.ERR_InterceptorLineOutOfRange, attributeData.GetAttributeArgumentLocation(lineNumberParameterIndex), referencedLineCount, lineNumberOneBased);
                return;
            }
 
            var line = referencedLines[lineNumberZeroBased];
            var lineLength = line.End - line.Start;
            if (characterNumberZeroBased >= lineLength)
            {
                diagnostics.Add(ErrorCode.ERR_InterceptorCharacterOutOfRange, attributeData.GetAttributeArgumentLocation(characterNumberParameterIndex), lineLength, characterNumberOneBased);
                return;
            }
 
            var referencedPosition = line.Start + characterNumberZeroBased;
            var root = matchingTree.GetRoot();
            var referencedToken = root.FindToken(referencedPosition);
            switch (referencedToken)
            {
                case { Parent: SimpleNameSyntax { Parent: MemberAccessExpressionSyntax { Parent: InvocationExpressionSyntax } memberAccess } rhs } when memberAccess.Name == rhs:
                case { Parent: SimpleNameSyntax { Parent: InvocationExpressionSyntax invocation } simpleName } when invocation.Expression == simpleName:
                    // happy case
                    break;
                case { Parent: SimpleNameSyntax { Parent: not MemberAccessExpressionSyntax } }:
                case { Parent: SimpleNameSyntax { Parent: MemberAccessExpressionSyntax memberAccess } rhs } when memberAccess.Name == rhs:
                    // NB: there are all sorts of places "simple names" can appear in syntax. With these checks we are trying to
                    // minimize confusion about why the name being used is not *interceptable*, but it's done on a best-effort basis.
                    diagnostics.Add(ErrorCode.ERR_InterceptorNameNotInvoked, attributeLocation, referencedToken.Text);
                    return;
                default:
                    diagnostics.Add(ErrorCode.ERR_InterceptorPositionBadToken, attributeLocation, referencedToken.Text);
                    return;
            }
 
            // Did they actually refer to the start of the token, not the middle, or in trivia?
            // NB: here we don't want the provided position to refer to the start of token's leading trivia, in the checksum-based way we *do* want it to refer to the start of leading trivia (i.e. the Position)
            if (referencedPosition != referencedToken.SpanStart)
            {
                var linePositionZeroBased = referencedToken.GetLocation().GetLineSpan().StartLinePosition;
                diagnostics.Add(ErrorCode.ERR_InterceptorMustReferToStartOfTokenPosition, attributeLocation, referencedToken.Text, linePositionZeroBased.Line + 1, linePositionZeroBased.Character + 1);
                return;
            }
 
            DeclaringCompilation.AddInterception(matchingTree.GetText().GetContentHash(), referencedToken.Position, attributeLocation, this);
 
            // Caller must free the returned builder.
            ArrayBuilder<string> getNamespaceNames()
            {
                var namespaceNames = ArrayBuilder<string>.GetInstance();
                for (var containingNamespace = ContainingNamespace; containingNamespace?.IsGlobalNamespace == false; containingNamespace = containingNamespace.ContainingNamespace)
                    namespaceNames.Add(containingNamespace.Name);
                // order outermost->innermost
                // e.g. for method MyApp.Generated.Interceptors.MyInterceptor(): ["MyApp", "Generated", "Interceptors"]
                namespaceNames.ReverseContents();
                return namespaceNames;
            }
 
            static bool isDeclaredInNamespace(ArrayBuilder<string> thisNamespaceNames, ImmutableArray<string> namespaceSegments)
            {
                Debug.Assert(namespaceSegments.Length > 0);
                if (namespaceSegments is ["global"])
                {
                    return true;
                }
 
                if (namespaceSegments.Length > thisNamespaceNames.Count)
                {
                    // the enabled NS has more components than interceptor's NS, so it will never match.
                    return false;
                }
 
                for (var i = 0; i < namespaceSegments.Length; i++)
                {
                    if (namespaceSegments[i] != thisNamespaceNames[i])
                    {
                        return false;
                    }
                }
                return true;
            }
 
            static void reportFeatureNotEnabled(BindingDiagnosticBag diagnostics, AttributeSyntax attributeSyntax, ArrayBuilder<string> namespaceNames)
            {
                if (namespaceNames.Count == 0)
                {
                    diagnostics.Add(ErrorCode.ERR_InterceptorGlobalNamespace, attributeSyntax);
                }
                else
                {
                    var recommendedProperty = $"<InterceptorsNamespaces>$(InterceptorsNamespaces);{string.Join(".", namespaceNames)}</InterceptorsNamespaces>";
                    diagnostics.Add(ErrorCode.ERR_InterceptorsFeatureNotEnabled, attributeSyntax, recommendedProperty);
                }
            }
        }
 
        private void DecodeUnmanagedCallersOnlyAttribute(ref DecodeWellKnownAttributeArguments<AttributeSyntax, CSharpAttributeData, AttributeLocation> arguments)
        {
            Debug.Assert(arguments.AttributeSyntaxOpt != null);
            var diagnostics = (BindingDiagnosticBag)arguments.Diagnostics;
 
            arguments.GetOrCreateData<MethodWellKnownAttributeData>().UnmanagedCallersOnlyAttributeData =
                DecodeUnmanagedCallersOnlyAttributeData(this, arguments.Attribute, arguments.AttributeSyntaxOpt.Location, diagnostics);
 
            bool reportedError = CheckAndReportValidUnmanagedCallersOnlyTarget(arguments.AttributeSyntaxOpt.Name, diagnostics);
 
            var returnTypeSyntax = this.ExtractReturnTypeSyntax();
 
            // If there is no return type (such as a property definition), Dummy.GetRoot() is returned.
            if (ReferenceEquals(returnTypeSyntax, CSharpSyntaxTree.Dummy.GetRoot()))
            {
                // If there's no syntax for the return type, then we already errored because this isn't a valid
                // unmanagedcallersonly target (it's a property getter/setter or some other non-regular-method).
                // Any more errors would just be noise.
                Debug.Assert(reportedError);
                return;
            }
 
            checkAndReportManagedTypes(ReturnType, this.RefKind, returnTypeSyntax, isParam: false, diagnostics);
            foreach (var param in Parameters)
            {
                checkAndReportManagedTypes(param.Type, param.RefKind, param.GetNonNullSyntaxNode(), isParam: true, diagnostics);
            }
 
            static void checkAndReportManagedTypes(TypeSymbol type, RefKind refKind, SyntaxNode syntax, bool isParam, BindingDiagnosticBag diagnostics)
            {
                if (refKind != RefKind.None)
                {
                    diagnostics.Add(ErrorCode.ERR_CannotUseRefInUnmanagedCallersOnly, syntax.Location);
                }
 
                // use-site diagnostics will be reported at actual parameter declaration site, we're only interested
                // in reporting managed types being used
                switch (type.ManagedKindNoUseSiteDiagnostics)
                {
                    case ManagedKind.Unmanaged:
                    case ManagedKind.UnmanagedWithGenerics:
                        // Note that this will let through some things that are technically unmanaged, but not
                        // actually blittable. However, we don't have a formal concept of blittable in C#
                        // itself, so checking for purely unmanaged types is the best we can do here.
                        return;
 
                    case ManagedKind.Managed:
                        // Cannot use '{0}' as a {1} type on a method attributed with 'UnmanagedCallersOnly.
                        diagnostics.Add(ErrorCode.ERR_CannotUseManagedTypeInUnmanagedCallersOnly, syntax.Location, type, (isParam ? MessageID.IDS_Parameter : MessageID.IDS_Return).Localize());
                        return;
 
                    default:
                        throw ExceptionUtilities.UnexpectedValue(type.ManagedKindNoUseSiteDiagnostics);
                }
            }
 
            static UnmanagedCallersOnlyAttributeData DecodeUnmanagedCallersOnlyAttributeData(SourceMethodSymbol @this, CSharpAttributeData attribute, Location location, BindingDiagnosticBag diagnostics)
            {
                Debug.Assert(attribute.AttributeClass is not null);
                ImmutableHashSet<CodeAnalysis.Symbols.INamedTypeSymbolInternal>? callingConventionTypes = null;
                if (attribute.CommonNamedArguments is { IsDefaultOrEmpty: false } namedArgs)
                {
                    var systemType = @this.DeclaringCompilation.GetWellKnownType(WellKnownType.System_Type);
 
                    foreach (var (key, value) in attribute.CommonNamedArguments)
                    {
                        // Technically, CIL can define a field and a property with the same name. However, such a
                        // member results in an Ambiguous Member error, and we never get to this piece of code at all.
                        // See UnmanagedCallersOnly_PropertyAndFieldNamedCallConvs for an example
                        bool isField = attribute.AttributeClass.GetMembers(key).Any(
                            static (m, systemType) => m is FieldSymbol { Type: ArrayTypeSymbol { ElementType: NamedTypeSymbol elementType } } && elementType.Equals(systemType, TypeCompareKind.ConsiderEverything),
                            systemType);
 
                        var namedArgumentDecoded = TryDecodeUnmanagedCallersOnlyCallConvsField(key, value, isField, location, diagnostics);
 
                        if (namedArgumentDecoded.IsCallConvs)
                        {
                            callingConventionTypes = namedArgumentDecoded.CallConvs;
                        }
                    }
                }
 
                return UnmanagedCallersOnlyAttributeData.Create(callingConventionTypes);
            }
        }
 
        internal override void PostDecodeWellKnownAttributes(ImmutableArray<CSharpAttributeData> boundAttributes, ImmutableArray<AttributeSyntax> allAttributeSyntaxNodes, BindingDiagnosticBag diagnostics, AttributeLocation symbolPart, WellKnownAttributeData decodedData)
        {
            Debug.Assert(!boundAttributes.IsDefault);
            Debug.Assert(!allAttributeSyntaxNodes.IsDefault);
            Debug.Assert(boundAttributes.Length == allAttributeSyntaxNodes.Length);
            Debug.Assert(symbolPart == AttributeLocation.None || symbolPart == AttributeLocation.Return);
 
            if (symbolPart != AttributeLocation.Return)
            {
                Debug.Assert(_lazyCustomAttributesBag != null);
                Debug.Assert(_lazyCustomAttributesBag.IsDecodedWellKnownAttributeDataComputed);
 
                if (ContainingSymbol is NamedTypeSymbol { IsComImport: true, TypeKind: TypeKind.Class or TypeKind.Interface })
                {
                    switch (this.MethodKind)
                    {
                        case MethodKind.Constructor:
                        case MethodKind.StaticConstructor:
                            if (!this.IsImplicitlyDeclared)
                            {
                                // CS0669: A class with the ComImport attribute cannot have a user-defined constructor
                                diagnostics.Add(ErrorCode.ERR_ComImportWithUserCtor, this.GetFirstLocation());
                            }
 
                            break;
 
                        default:
                            if (!this.IsAbstract && !this.IsExtern)
                            {
                                // CS0423: Since '{1}' has the ComImport attribute, '{0}' must be extern or abstract
                                diagnostics.Add(ErrorCode.ERR_ComImportWithImpl, this.GetFirstLocation(), this, ContainingType);
                            }
 
                            break;
                    }
                }
 
                if (IsExtern
                    && !IsAbstract
                    && !this.IsPartialMember()
                    && GetInMethodSyntaxNode() is null
                    && boundAttributes.IsEmpty
                    && !this.ContainingType.IsComImport)
                {
                    var errorCode = (this.MethodKind == MethodKind.Constructor || this.MethodKind == MethodKind.StaticConstructor) ?
                        ErrorCode.WRN_ExternCtorNoImplementation :
                        ErrorCode.WRN_ExternMethodNoImplementation;
                    diagnostics.Add(errorCode, this.GetFirstLocation(), this);
                }
            }
 
            base.PostDecodeWellKnownAttributes(boundAttributes, allAttributeSyntaxNodes, diagnostics, symbolPart, decodedData);
        }
 
        protected void AsyncMethodChecks(BindingDiagnosticBag diagnostics)
        {
            AsyncMethodChecks(verifyReturnType: true, this.GetFirstLocation(), diagnostics);
        }
 
        protected void AsyncMethodChecks(bool verifyReturnType, Location errorLocation, BindingDiagnosticBag diagnostics)
        {
            if (IsAsync)
            {
                bool hasErrors = false;
 
                if (verifyReturnType)
                {
                    if (this.RefKind != RefKind.None)
                    {
                        var returnTypeSyntax = this.SyntaxNode switch
                        {
                            MethodDeclarationSyntax { ReturnType: var methodReturnType } => methodReturnType,
                            LocalFunctionStatementSyntax { ReturnType: var localReturnType } => localReturnType,
                            ParenthesizedLambdaExpressionSyntax { ReturnType: { } lambdaReturnType } => lambdaReturnType,
                            var unexpected => throw ExceptionUtilities.UnexpectedValue(unexpected)
                        };
 
                        ReportBadRefToken(returnTypeSyntax, diagnostics);
                        hasErrors = true;
                    }
                    else if (isBadAsyncReturn(this))
                    {
                        diagnostics.Add(ErrorCode.ERR_BadAsyncReturn, errorLocation);
                        hasErrors = true;
                    }
                }
 
                if (this.HasAsyncMethodBuilderAttribute(out _))
                {
                    MessageID.IDS_AsyncMethodBuilderOverride.CheckFeatureAvailability(diagnostics, this.DeclaringCompilation, errorLocation);
                }
 
                // Avoid checking attributes on containing types to avoid a potential cycle when a lambda
                // is used in an attribute argument - see https://github.com/dotnet/roslyn/issues/54074.
                // (ERR_SecurityCriticalOrSecuritySafeCriticalOnAsyncInClassOrStruct was never reported
                // for lambda expressions and is not a .NET Core scenario so it's not necessary to handle.)
                if (this.MethodKind != MethodKind.LambdaMethod)
                {
                    for (NamedTypeSymbol curr = this.ContainingType; (object)curr != null; curr = curr.ContainingType)
                    {
                        if (curr is SourceNamedTypeSymbol { HasSecurityCriticalAttributes: true })
                        {
                            diagnostics.Add(ErrorCode.ERR_SecurityCriticalOrSecuritySafeCriticalOnAsyncInClassOrStruct, errorLocation);
                            hasErrors = true;
                            break;
                        }
                    }
                }
 
                if ((this.ImplementationAttributes & System.Reflection.MethodImplAttributes.Synchronized) != 0)
                {
                    diagnostics.Add(ErrorCode.ERR_SynchronizedAsyncMethod, errorLocation);
                    hasErrors = true;
                }
 
                if (!hasErrors)
                {
                    ReportAsyncParameterErrors(diagnostics, errorLocation);
                }
 
                var iAsyncEnumerableType = DeclaringCompilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T);
                if (ReturnType.OriginalDefinition.Equals(iAsyncEnumerableType) &&
                    GetInMethodSyntaxNode() is object)
                {
                    var cancellationTokenType = DeclaringCompilation.GetWellKnownType(WellKnownType.System_Threading_CancellationToken);
                    var enumeratorCancellationCount = Parameters.Count(p => p.IsSourceParameterWithEnumeratorCancellationAttribute());
                    if (enumeratorCancellationCount == 0 &&
                        ParameterTypesWithAnnotations.Any(static (p, cancellationTokenType) => p.Type.Equals(cancellationTokenType), cancellationTokenType))
                    {
                        // Warn for CancellationToken parameters in async-iterators with no parameter decorated with [EnumeratorCancellation]
                        // There could be more than one parameter that could be decorated with [EnumeratorCancellation] so we warn on the method instead
                        diagnostics.Add(ErrorCode.WRN_UndecoratedCancellationTokenParameter, errorLocation, this);
                    }
 
                    if (enumeratorCancellationCount > 1)
                    {
                        // The [EnumeratorCancellation] attribute can only be used on one parameter
                        diagnostics.Add(ErrorCode.ERR_MultipleEnumeratorCancellationAttributes, errorLocation);
                    }
                }
            }
 
            static bool isBadAsyncReturn(MethodSymbol methodSymbol)
            {
                var returnType = methodSymbol.ReturnType;
                var declaringCompilation = methodSymbol.DeclaringCompilation;
                return !returnType.IsErrorType() &&
                    !returnType.IsVoidType() &&
                    !returnType.IsIAsyncEnumerableType(declaringCompilation) &&
                    !returnType.IsIAsyncEnumeratorType(declaringCompilation) &&
                    !methodSymbol.IsAsyncEffectivelyReturningTask(declaringCompilation) &&
                    !methodSymbol.IsAsyncEffectivelyReturningGenericTask(declaringCompilation);
            }
        }
 
        private static FlowAnalysisAnnotations DecodeReturnTypeAnnotationAttributes(ReturnTypeWellKnownAttributeData attributeData)
        {
            FlowAnalysisAnnotations annotations = FlowAnalysisAnnotations.None;
            if (attributeData != null)
            {
                if (attributeData.HasMaybeNullAttribute)
                {
                    annotations |= FlowAnalysisAnnotations.MaybeNull;
                }
                if (attributeData.HasNotNullAttribute)
                {
                    annotations |= FlowAnalysisAnnotations.NotNull;
                }
            }
            return annotations;
        }
 
        public sealed override bool HidesBaseMethodsByName
        {
            get
            {
                return false;
            }
        }
 
        internal sealed override bool HasRuntimeSpecialName
        {
            get
            {
                return base.HasRuntimeSpecialName || IsVtableGapInterfaceMethod();
            }
        }
 
        private bool IsVtableGapInterfaceMethod()
        {
            return this.ContainingType.IsInterface &&
                   ModuleExtensions.GetVTableGapSize(this.MetadataName) > 0;
        }
 
        internal override bool HasSpecialName
        {
            get
            {
                switch (this.MethodKind)
                {
                    case MethodKind.Constructor:
                    case MethodKind.StaticConstructor:
                    case MethodKind.PropertyGet:
                    case MethodKind.PropertySet:
                    case MethodKind.EventAdd:
                    case MethodKind.EventRemove:
                    case MethodKind.UserDefinedOperator:
                    case MethodKind.Conversion:
                        return true;
                }
 
                if (IsVtableGapInterfaceMethod())
                {
                    return true;
                }
 
                var data = GetDecodedWellKnownAttributeData();
                return data != null && data.HasSpecialNameAttribute;
            }
        }
 
        internal sealed override bool IsDirectlyExcludedFromCodeCoverage =>
            GetDecodedWellKnownAttributeData()?.HasExcludeFromCodeCoverageAttribute == true;
 
        internal override bool RequiresSecurityObject
        {
            get
            {
                var data = GetDecodedWellKnownAttributeData();
                return data != null && data.HasDynamicSecurityMethodAttribute;
            }
        }
 
        internal override bool HasDeclarativeSecurity
        {
            get
            {
                var data = this.GetDecodedWellKnownAttributeData();
                return data != null && data.HasDeclarativeSecurity;
            }
        }
 
        internal override IEnumerable<Cci.SecurityAttribute> GetSecurityInformation()
        {
            var attributesBag = this.GetAttributesBag();
            var wellKnownData = (MethodWellKnownAttributeData)attributesBag.DecodedWellKnownAttributeData;
            if (wellKnownData != null)
            {
                SecurityWellKnownAttributeData? securityData = wellKnownData.SecurityInformation;
                if (securityData != null)
                {
                    return securityData.GetSecurityAttributes(attributesBag.Attributes);
                }
            }
 
            return SpecializedCollections.EmptyEnumerable<Cci.SecurityAttribute>();
        }
 
        public override DllImportData? GetDllImportData()
        {
            var data = this.GetDecodedWellKnownAttributeData();
            return data != null ? data.DllImportPlatformInvokeData : null;
        }
 
        internal override MarshalPseudoCustomAttributeData? ReturnValueMarshallingInformation
        {
            get
            {
                var data = this.GetDecodedReturnTypeWellKnownAttributeData();
                return data != null ? data.MarshallingInformation : null;
            }
        }
 
        internal override System.Reflection.MethodImplAttributes ImplementationAttributes
        {
            get
            {
                var data = GetDecodedWellKnownAttributeData();
                var result = (data != null) ? data.MethodImplAttributes : default(System.Reflection.MethodImplAttributes);
 
                if (this.ContainingType.IsComImport && this.MethodKind == MethodKind.Constructor)
                {
                    // Synthesized constructor of ComImport types is marked as Runtime implemented and InternalCall
                    result |= (System.Reflection.MethodImplAttributes.Runtime | System.Reflection.MethodImplAttributes.InternalCall);
                }
 
                return result;
            }
        }
 
        internal override int TryGetOverloadResolutionPriority()
            => GetEarlyDecodedWellKnownAttributeData()?.OverloadResolutionPriority ?? 0;
    }
}