File: Symbols\Source\SourceComplexParameterSymbol.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.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
    /// <summary>
    /// A source parameter, potentially with a default value, attributes, etc.
    /// </summary>
    internal abstract class SourceComplexParameterSymbolBase : SourceParameterSymbol, IAttributeTargetSymbol
    {
        [Flags]
        private enum ParameterFlags : byte
        {
            None = 0,
            HasParamsModifier = 0x1,
            ParamsParameter = 0x02, // Value of this flag is either derived from HasParamsModifier, or inherited from overridden member 
            ExtensionThisParameter = 0x04,
            DefaultParameter = 0x08,
        }
 
        private readonly SyntaxReference _syntaxRef;
        private readonly ParameterFlags _parameterSyntaxKind;
 
        private ThreeState _lazyHasOptionalAttribute;
        private CustomAttributesBag<CSharpAttributeData> _lazyCustomAttributesBag;
        protected ConstantValue _lazyDefaultSyntaxValue;
 
        protected SourceComplexParameterSymbolBase(
            Symbol owner,
            int ordinal,
            RefKind refKind,
            string name,
            Location location,
            SyntaxReference syntaxRef,
            bool hasParamsModifier,
            bool isParams,
            bool isExtensionMethodThis,
            ScopedKind scope)
            : base(owner, ordinal, refKind, scope, name, location)
        {
            Debug.Assert((syntaxRef == null) || (syntaxRef.GetSyntax().IsKind(SyntaxKind.Parameter)));
 
            _lazyHasOptionalAttribute = ThreeState.Unknown;
            _syntaxRef = syntaxRef;
 
            if (hasParamsModifier)
            {
                _parameterSyntaxKind |= ParameterFlags.HasParamsModifier;
            }
 
            if (isParams)
            {
                _parameterSyntaxKind |= ParameterFlags.ParamsParameter;
            }
 
            if (isExtensionMethodThis)
            {
                _parameterSyntaxKind |= ParameterFlags.ExtensionThisParameter;
            }
 
            var parameterSyntax = this.ParameterSyntax;
            if (parameterSyntax != null && parameterSyntax.Default != null)
            {
                _parameterSyntaxKind |= ParameterFlags.DefaultParameter;
            }
 
            _lazyDefaultSyntaxValue = ConstantValue.Unset;
        }
 
        private Binder WithTypeParametersBinderOpt => (ContainingSymbol as SourceMethodSymbol)?.WithTypeParametersBinder;
 
        internal sealed override SyntaxReference SyntaxReference => _syntaxRef;
 
        private ParameterSyntax ParameterSyntax => (ParameterSyntax)_syntaxRef?.GetSyntax();
 
        public override bool IsDiscard => false;
 
        internal sealed override ConstantValue ExplicitDefaultConstantValue
        {
            get
            {
                // Parameter has either default argument syntax or DefaultParameterValue attribute, but not both.
                // We separate these since in some scenarios (delegate Invoke methods) we need to suppress syntactic 
                // default value but use value from pseudo-custom attribute. 
                //
                // For example:
                // public delegate void D([Optional, DefaultParameterValue(1)]int a, int b = 2);
                //
                // Dev11 emits the first parameter as option with default value and the second as regular parameter.
                // The syntactic default value is suppressed since additional synthesized parameters are added at the end of the signature.
 
                return DefaultSyntaxValue ?? DefaultValueFromAttributes;
            }
        }
 
        internal sealed override ConstantValue DefaultValueFromAttributes
        {
            get
            {
                ParameterEarlyWellKnownAttributeData data = GetEarlyDecodedWellKnownAttributeData();
                return (data != null && data.DefaultParameterValue != ConstantValue.Unset) ? data.DefaultParameterValue : ConstantValue.NotAvailable;
            }
        }
 
        internal sealed override bool IsIDispatchConstant
            => GetDecodedWellKnownAttributeData()?.HasIDispatchConstantAttribute == true;
 
        internal override bool IsIUnknownConstant
            => GetDecodedWellKnownAttributeData()?.HasIUnknownConstantAttribute == true;
 
        internal override bool IsCallerLineNumber => GetEarlyDecodedWellKnownAttributeData()?.HasCallerLineNumberAttribute == true;
 
        internal override bool IsCallerFilePath => GetEarlyDecodedWellKnownAttributeData()?.HasCallerFilePathAttribute == true;
 
        internal override bool IsCallerMemberName => GetEarlyDecodedWellKnownAttributeData()?.HasCallerMemberNameAttribute == true;
 
        internal override int CallerArgumentExpressionParameterIndex
        {
            get
            {
                return GetEarlyDecodedWellKnownAttributeData()?.CallerArgumentExpressionParameterIndex ?? -1;
            }
        }
 
        internal override ImmutableArray<int> InterpolatedStringHandlerArgumentIndexes
            => (GetDecodedWellKnownAttributeData()?.InterpolatedStringHandlerArguments).NullToEmpty();
 
        internal override bool HasInterpolatedStringHandlerArgumentError
            => GetDecodedWellKnownAttributeData()?.InterpolatedStringHandlerArguments.IsDefault ?? false;
 
        internal override FlowAnalysisAnnotations FlowAnalysisAnnotations
        {
            get
            {
                return DecodeFlowAnalysisAttributes(GetDecodedWellKnownAttributeData());
            }
        }
 
        private static FlowAnalysisAnnotations DecodeFlowAnalysisAttributes(ParameterWellKnownAttributeData attributeData)
        {
            if (attributeData == null)
            {
                return FlowAnalysisAnnotations.None;
            }
            FlowAnalysisAnnotations annotations = FlowAnalysisAnnotations.None;
            if (attributeData.HasAllowNullAttribute) annotations |= FlowAnalysisAnnotations.AllowNull;
            if (attributeData.HasDisallowNullAttribute) annotations |= FlowAnalysisAnnotations.DisallowNull;
 
            if (attributeData.HasMaybeNullAttribute)
            {
                annotations |= FlowAnalysisAnnotations.MaybeNull;
            }
            else
            {
                if (attributeData.MaybeNullWhenAttribute is bool when)
                {
                    annotations |= (when ? FlowAnalysisAnnotations.MaybeNullWhenTrue : FlowAnalysisAnnotations.MaybeNullWhenFalse);
                }
            }
 
            if (attributeData.HasNotNullAttribute)
            {
                annotations |= FlowAnalysisAnnotations.NotNull;
            }
            else
            {
                if (attributeData.NotNullWhenAttribute is bool when)
                {
                    annotations |= (when ? FlowAnalysisAnnotations.NotNullWhenTrue : FlowAnalysisAnnotations.NotNullWhenFalse);
                }
            }
 
            if (attributeData.DoesNotReturnIfAttribute is bool condition)
            {
                annotations |= (condition ? FlowAnalysisAnnotations.DoesNotReturnIfTrue : FlowAnalysisAnnotations.DoesNotReturnIfFalse);
            }
 
            return annotations;
        }
 
        internal override ImmutableHashSet<string> NotNullIfParameterNotNull
            => GetDecodedWellKnownAttributeData()?.NotNullIfParameterNotNull ?? ImmutableHashSet<string>.Empty;
 
        internal bool HasEnumeratorCancellationAttribute
        {
            get
            {
                ParameterWellKnownAttributeData attributeData = GetDecodedWellKnownAttributeData();
                return attributeData?.HasEnumeratorCancellationAttribute == true;
            }
        }
 
#nullable enable
 
        internal sealed override ScopedKind EffectiveScope
        {
            get
            {
                var scope = CalculateEffectiveScopeIgnoringAttributes();
                if (scope != ScopedKind.None &&
                    HasUnscopedRefAttribute &&
                    UseUpdatedEscapeRules)
                {
                    return ScopedKind.None;
                }
                return scope;
            }
        }
 
        internal override bool HasUnscopedRefAttribute => GetEarlyDecodedWellKnownAttributeData()?.HasUnscopedRefAttribute == true;
 
        internal static SyntaxNode? GetDefaultValueSyntaxForIsNullableAnalysisEnabled(ParameterSyntax? parameterSyntax) =>
            parameterSyntax?.Default?.Value;
 
        /// <summary>
        /// Returns the bound default value syntax from the parameter, if it exists.
        /// Note that this method will only return a non-null value if the
        /// default value was supplied in syntax. If the value is supplied through the DefaultParameterValue
        /// attribute, then ExplicitDefaultValue will be non-null but this method will return null.
        /// However, if ExplicitDefaultValue is null, this method should always return null.
        /// </summary>
        public BoundParameterEqualsValue? BindParameterEqualsValue()
        {
            // Rebind default value expression, ignoring any diagnostics, in order to produce
            // a bound node that can be used for passes such as definite assignment.
            MakeDefaultExpression(BindingDiagnosticBag.Discarded, out var _, out var parameterEqualsValue);
            return parameterEqualsValue;
        }
 
        private ConstantValue DefaultSyntaxValue
        {
            get
            {
                if (state.NotePartComplete(CompletionPart.StartDefaultSyntaxValue))
                {
                    var diagnostics = BindingDiagnosticBag.GetInstance();
                    Debug.Assert(diagnostics.DiagnosticBag != null);
                    var previousValue = Interlocked.CompareExchange(
                        ref _lazyDefaultSyntaxValue,
                        MakeDefaultExpression(diagnostics, out var binder, out var parameterEqualsValue),
                        ConstantValue.Unset);
                    Debug.Assert(previousValue == ConstantValue.Unset);
 
                    var completedOnThisThread = state.NotePartComplete(CompletionPart.EndDefaultSyntaxValue);
                    Debug.Assert(completedOnThisThread);
 
                    if (parameterEqualsValue is not null)
                    {
                        if (binder is not null &&
                            GetDefaultValueSyntaxForIsNullableAnalysisEnabled(ParameterSyntax) is { } valueSyntax)
                        {
                            NullableWalker.AnalyzeIfNeeded(binder, parameterEqualsValue, valueSyntax, diagnostics.DiagnosticBag);
                        }
                        if (!_lazyDefaultSyntaxValue.IsBad)
                        {
                            VerifyParamDefaultValueMatchesAttributeIfAny(_lazyDefaultSyntaxValue, parameterEqualsValue.Value.Syntax, diagnostics);
 
                            // Ensure availability of `DecimalConstantAttribute`.
                            if (_lazyDefaultSyntaxValue.IsDecimal &&
                                DefaultValueFromAttributes == ConstantValue.NotAvailable)
                            {
                                Binder.ReportUseSiteDiagnosticForSynthesizedAttribute(DeclaringCompilation,
                                    WellKnownMember.System_Runtime_CompilerServices_DecimalConstantAttribute__ctor,
                                    diagnostics,
                                    parameterEqualsValue.Value.Syntax.Location);
                            }
                        }
                    }
 
                    AddDeclarationDiagnostics(diagnostics);
                    diagnostics.Free();
 
                    completedOnThisThread = state.NotePartComplete(CompletionPart.EndDefaultSyntaxValueDiagnostics);
                    Debug.Assert(completedOnThisThread);
                }
 
                state.SpinWaitComplete(CompletionPart.EndDefaultSyntaxValue, default(CancellationToken));
                return _lazyDefaultSyntaxValue;
            }
        }
 
        private Binder GetDefaultParameterValueBinder(SyntaxNode syntax)
        {
            var binder = WithTypeParametersBinderOpt;
 
            // If binder is null, then get it from the compilation. Otherwise use the provided binder.
            // Don't always get it from the compilation because we might be in a speculative context (local function parameter),
            // in which case the declaring compilation is the wrong one.
            if (binder == null)
            {
                var compilation = this.DeclaringCompilation;
                var binderFactory = compilation.GetBinderFactory(syntax.SyntaxTree);
                binder = binderFactory.GetBinder(syntax);
            }
            Debug.Assert(binder.GetBinder(syntax) == null);
            return binder;
        }
 
        private void NullableAnalyzeParameterDefaultValueFromAttributes()
        {
            var parameterSyntax = this.ParameterSyntax;
            if (parameterSyntax == null)
            {
                // If there is no syntax at all for the parameter, it means we are in a situation like
                // a property setter whose 'value' parameter has a default value from attributes.
                // There isn't a sensible use for this in the language, so we just bail in such scenarios.
                return;
            }
 
            // The syntax span used to determine whether the attribute value is in a nullable-enabled
            // context is larger than necessary - it includes the entire attribute list rather than the specific
            // default value attribute which is used in AttributeSemanticModel.IsNullableAnalysisEnabled().
            var attributes = parameterSyntax.AttributeLists.Node;
            if (attributes is null || !NullableWalker.NeedsAnalysis(DeclaringCompilation, attributes))
            {
                return;
            }
 
            var defaultValue = DefaultValueFromAttributes;
            if (defaultValue == null || defaultValue.IsBad)
            {
                return;
            }
 
            var binder = GetDefaultParameterValueBinder(parameterSyntax);
 
            // Nullable warnings *within* the attribute argument (such as a W-warning for `(string)null`)
            // are reported when we nullable-analyze attribute arguments separately from here.
            // However, this analysis of the constant value's compatibility with the parameter
            // needs to wait until the attributes are populated on the parameter symbol.
            var parameterEqualsValue = new BoundParameterEqualsValue(
                parameterSyntax,
                this,
                ImmutableArray<LocalSymbol>.Empty,
                // note that if the parameter type conflicts with the default value from attributes,
                // we will just get a bad constant value above and return early.
                new BoundLiteral(parameterSyntax, defaultValue, Type));
 
            var diagnostics = BindingDiagnosticBag.GetInstance(withDiagnostics: true, withDependencies: false);
            Debug.Assert(diagnostics.DiagnosticBag != null);
            NullableWalker.AnalyzeIfNeeded(binder, parameterEqualsValue, parameterSyntax, diagnostics.DiagnosticBag);
            AddDeclarationDiagnostics(diagnostics);
            diagnostics.Free();
        }
 
        // This method *must not* depend on attributes on the parameter symbol.
        // Otherwise we will have cycles when binding usage of attributes whose constructors have optional parameters
        private ConstantValue MakeDefaultExpression(BindingDiagnosticBag diagnostics, out Binder? binder, out BoundParameterEqualsValue? parameterEqualsValue)
        {
            binder = null;
            parameterEqualsValue = null;
 
            var parameterSyntax = this.ParameterSyntax;
            if (parameterSyntax == null)
            {
                return ConstantValue.NotAvailable;
            }
 
            var defaultSyntax = parameterSyntax.Default;
            if (defaultSyntax == null)
            {
                return ConstantValue.NotAvailable;
            }
 
            MessageID.IDS_FeatureOptionalParameter.CheckFeatureAvailability(diagnostics, defaultSyntax.EqualsToken);
 
            binder = GetDefaultParameterValueBinder(defaultSyntax);
            binder = binder.CreateBinderForParameterDefaultValue(this, defaultSyntax);
            Debug.Assert(binder.InParameterDefaultValue);
            Debug.Assert(binder.ContainingMemberOrLambda == ContainingSymbol);
 
            parameterEqualsValue = binder.BindParameterDefaultValue(defaultSyntax, this, diagnostics, out var valueBeforeConversion);
            if (valueBeforeConversion.HasErrors)
            {
                return ConstantValue.Bad;
            }
 
            BoundExpression convertedExpression = parameterEqualsValue.Value;
            bool hasErrors = ParameterHelpers.ReportDefaultParameterErrors(binder, ContainingSymbol, parameterSyntax, this, valueBeforeConversion, convertedExpression, diagnostics);
            if (hasErrors)
            {
                return ConstantValue.Bad;
            }
 
            // If we have something like M(double? x = 1) then the expression we'll get is (double?)1, which
            // does not have a constant value. The constant value we want is (double)1.
            // The default literal conversion is an exception: (double)default would give the wrong value for M(double? x = default).
            if (convertedExpression.ConstantValueOpt == null && convertedExpression.Kind == BoundKind.Conversion &&
                ((BoundConversion)convertedExpression).ConversionKind != ConversionKind.DefaultLiteral)
            {
                if (Type.IsNullableType())
                {
                    convertedExpression = binder.GenerateConversionForAssignment(Type.GetNullableUnderlyingType(),
                        valueBeforeConversion, diagnostics, Binder.ConversionForAssignmentFlags.DefaultParameter);
                }
            }
 
            // represent default(struct) by a Null constant:
            var value = convertedExpression.ConstantValueOpt ?? ConstantValue.Null;
            return value;
        }
 
#nullable disable
 
        public override string MetadataName
        {
            get
            {
                // The metadata parameter name should be the name used in the partial definition.
 
                var sourceMethod = this.ContainingSymbol as SourceOrdinaryMethodSymbol;
                if ((object)sourceMethod == null)
                {
                    return base.MetadataName;
                }
 
                var definition = sourceMethod.SourcePartialDefinition;
                if ((object)definition == null)
                {
                    return base.MetadataName;
                }
 
                return definition.Parameters[this.Ordinal].MetadataName;
            }
        }
 
        protected virtual IAttributeTargetSymbol AttributeOwner => this;
 
        IAttributeTargetSymbol IAttributeTargetSymbol.AttributesOwner => AttributeOwner;
 
        AttributeLocation IAttributeTargetSymbol.DefaultAttributeLocation => AttributeLocation.Parameter;
 
        AttributeLocation IAttributeTargetSymbol.AllowedAttributeLocations
        {
            get
            {
                if (SynthesizedRecordPropertySymbol.HaveCorrespondingSynthesizedRecordPropertySymbol(this))
                {
                    return AttributeLocation.Parameter | AttributeLocation.Property | AttributeLocation.Field;
                }
 
                return AttributeLocation.Parameter;
            }
        }
 
#nullable enable
        /// <summary>
        /// Symbol to copy bound attributes from, or null if the attributes are not shared among multiple source parameter symbols.
        /// </summary>
        /// <remarks>
        /// This is inconsistent with analogous 'BoundAttributesSource' on other symbols.
        /// Usually the definition part is the source, but for parameters the implementation part is the source.
        /// This affects the location of diagnostics among other things.
        /// </remarks>
        private SourceParameterSymbol? BoundAttributesSource
            => PartialImplementationPart;
 
        protected SourceParameterSymbol? PartialImplementationPart
        {
            get
            {
                ImmutableArray<ParameterSymbol> implParameters = this.ContainingSymbol switch
                {
                    SourceMemberMethodSymbol { PartialImplementationPart.Parameters: { } parameters } => parameters,
                    SourcePropertySymbol { PartialImplementationPart.Parameters: { } parameters } => parameters,
                    _ => default
                };
 
                if (implParameters.IsDefault)
                {
                    return null;
                }
 
                Debug.Assert(!this.ContainingSymbol.IsPartialImplementation());
                return (SourceParameterSymbol)implParameters[this.Ordinal];
            }
        }
 
        protected SourceParameterSymbol? PartialDefinitionPart
        {
            get
            {
                ImmutableArray<ParameterSymbol> defParameters = this.ContainingSymbol switch
                {
                    SourceMemberMethodSymbol { PartialDefinitionPart.Parameters: { } parameters } => parameters,
                    SourcePropertySymbol { PartialDefinitionPart.Parameters: { } parameters } => parameters,
                    _ => default
                };
 
                if (defParameters.IsDefault)
                {
                    return null;
                }
 
                Debug.Assert(!this.ContainingSymbol.IsPartialDefinition());
                return (SourceParameterSymbol)defParameters[this.Ordinal];
            }
        }
 
        internal sealed override SyntaxList<AttributeListSyntax> AttributeDeclarationList
        {
            get
            {
                var syntax = this.ParameterSyntax;
                return (syntax != null) ? syntax.AttributeLists : default(SyntaxList<AttributeListSyntax>);
            }
        }
 
        /// <summary>
        /// Gets the syntax list of custom attributes that declares attributes for this parameter symbol.
        /// </summary>
        internal virtual OneOrMany<SyntaxList<AttributeListSyntax>> GetAttributeDeclarations()
        {
            // Attributes on parameters in partial members are owned by the parameter in the implementation part.
            // If this symbol has a non-null PartialImplementationPart, we should have accessed this method through that implementation symbol.
            Debug.Assert(PartialImplementationPart is null);
 
            if (PartialDefinitionPart is { } definitionPart)
            {
                return OneOrMany.Create(
                    AttributeDeclarationList,
                    definitionPart.AttributeDeclarationList);
            }
            else
            {
                return OneOrMany.Create(AttributeDeclarationList);
            }
        }
#nullable disable
 
        /// <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>
        internal ParameterWellKnownAttributeData GetDecodedWellKnownAttributeData()
        {
            var attributesBag = _lazyCustomAttributesBag;
            if (attributesBag == null || !attributesBag.IsDecodedWellKnownAttributeDataComputed)
            {
                attributesBag = this.GetAttributesBag();
            }
 
            return (ParameterWellKnownAttributeData)attributesBag.DecodedWellKnownAttributeData;
        }
 
        /// <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 ParameterEarlyWellKnownAttributeData GetEarlyDecodedWellKnownAttributeData()
        {
            var attributesBag = _lazyCustomAttributesBag;
            if (attributesBag == null || !attributesBag.IsEarlyDecodedWellKnownAttributeDataComputed)
            {
                attributesBag = this.GetAttributesBag();
            }
 
            return (ParameterEarlyWellKnownAttributeData)attributesBag.EarlyDecodedWellKnownAttributeData;
        }
 
        /// <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>
        internal sealed override CustomAttributesBag<CSharpAttributeData> GetAttributesBag()
        {
            if (_lazyCustomAttributesBag == null || !_lazyCustomAttributesBag.IsSealed)
            {
                SourceParameterSymbol copyFrom = this.BoundAttributesSource;
 
                // prevent infinite recursion:
                Debug.Assert(!ReferenceEquals(copyFrom, this));
 
                bool bagCreatedOnThisThread;
                if ((object)copyFrom != null)
                {
                    var attributesBag = copyFrom.GetAttributesBag();
                    bagCreatedOnThisThread = Interlocked.CompareExchange(ref _lazyCustomAttributesBag, attributesBag, null) == null;
                }
                else
                {
                    var attributeSyntax = this.GetAttributeDeclarations();
                    bagCreatedOnThisThread = LoadAndValidateAttributes(attributeSyntax, ref _lazyCustomAttributesBag, binderOpt: WithTypeParametersBinderOpt);
                }
 
                if (bagCreatedOnThisThread)
                {
                    NullableAnalyzeParameterDefaultValueFromAttributes();
                    state.NotePartComplete(CompletionPart.Attributes);
                }
            }
 
            return _lazyCustomAttributesBag;
        }
 
        /// <summary>
        /// Binds attributes applied to this parameter.
        /// </summary>
        public ImmutableArray<(CSharpAttributeData, BoundAttribute)> BindParameterAttributes()
        {
            return BindAttributes(GetAttributeDeclarations(), WithTypeParametersBinderOpt);
        }
 
        internal override void EarlyDecodeWellKnownAttributeType(NamedTypeSymbol attributeType, AttributeSyntax attributeSyntax)
        {
            Debug.Assert(!attributeType.IsErrorType());
 
            // NOTE: OptionalAttribute is decoded specially before any of the other attributes and stored in the parameter
            // symbol (rather than in the EarlyWellKnownAttributeData) because it is needed during overload resolution.
            if (CSharpAttributeData.IsTargetEarlyAttribute(attributeType, attributeSyntax, AttributeDescription.OptionalAttribute))
            {
                _lazyHasOptionalAttribute = ThreeState.True;
            }
        }
 
        internal override void PostEarlyDecodeWellKnownAttributeTypes()
        {
            if (_lazyHasOptionalAttribute == ThreeState.Unknown)
            {
                _lazyHasOptionalAttribute = ThreeState.False;
            }
 
            base.PostEarlyDecodeWellKnownAttributeTypes();
        }
 
#nullable enable
        internal override (CSharpAttributeData?, BoundAttribute?) EarlyDecodeWellKnownAttribute(ref EarlyDecodeWellKnownAttributeArguments<EarlyWellKnownAttributeBinder, NamedTypeSymbol, AttributeSyntax, AttributeLocation> arguments)
        {
            if (CSharpAttributeData.IsTargetEarlyAttribute(arguments.AttributeType, arguments.AttributeSyntax, AttributeDescription.DefaultParameterValueAttribute))
            {
                return EarlyDecodeAttributeForDefaultParameterValue(AttributeDescription.DefaultParameterValueAttribute, ref arguments);
            }
            else if (CSharpAttributeData.IsTargetEarlyAttribute(arguments.AttributeType, arguments.AttributeSyntax, AttributeDescription.DecimalConstantAttribute))
            {
                return EarlyDecodeAttributeForDefaultParameterValue(AttributeDescription.DecimalConstantAttribute, ref arguments);
            }
            else if (CSharpAttributeData.IsTargetEarlyAttribute(arguments.AttributeType, arguments.AttributeSyntax, AttributeDescription.DateTimeConstantAttribute))
            {
                return EarlyDecodeAttributeForDefaultParameterValue(AttributeDescription.DateTimeConstantAttribute, ref arguments);
            }
            else if (CSharpAttributeData.IsTargetEarlyAttribute(arguments.AttributeType, arguments.AttributeSyntax, AttributeDescription.UnscopedRefAttribute))
            {
                // We can't bind the attribute here because that might lead to a cycle.
                // Instead, simply record that the attribute exists and bind later.
                arguments.GetOrCreateData<ParameterEarlyWellKnownAttributeData>().HasUnscopedRefAttribute = true;
                return (null, null);
            }
            else if (!IsOnPartialImplementation(arguments.AttributeSyntax))
            {
                if (CSharpAttributeData.IsTargetEarlyAttribute(arguments.AttributeType, arguments.AttributeSyntax, AttributeDescription.CallerLineNumberAttribute))
                {
                    arguments.GetOrCreateData<ParameterEarlyWellKnownAttributeData>().HasCallerLineNumberAttribute = true;
                }
                else if (CSharpAttributeData.IsTargetEarlyAttribute(arguments.AttributeType, arguments.AttributeSyntax, AttributeDescription.CallerFilePathAttribute))
                {
                    arguments.GetOrCreateData<ParameterEarlyWellKnownAttributeData>().HasCallerFilePathAttribute = true;
                }
                else if (CSharpAttributeData.IsTargetEarlyAttribute(arguments.AttributeType, arguments.AttributeSyntax, AttributeDescription.CallerMemberNameAttribute))
                {
                    arguments.GetOrCreateData<ParameterEarlyWellKnownAttributeData>().HasCallerMemberNameAttribute = true;
                }
                else if (CSharpAttributeData.IsTargetEarlyAttribute(arguments.AttributeType, arguments.AttributeSyntax, AttributeDescription.CallerArgumentExpressionAttribute))
                {
                    var index = -1;
                    var (attributeData, _) = arguments.Binder.GetAttribute(arguments.AttributeSyntax, arguments.AttributeType, beforeAttributePartBound: null, afterAttributePartBound: null, out _);
                    if (!attributeData.HasErrors)
                    {
                        var constructorArguments = attributeData.CommonConstructorArguments;
                        Debug.Assert(constructorArguments.Length == 1);
                        if (constructorArguments[0].TryDecodeValue(SpecialType.System_String, out string? parameterName))
                        {
                            var parameters = ContainingSymbol.GetParameters();
                            for (int i = 0; i < parameters.Length; i++)
                            {
                                if (parameters[i].Name.Equals(parameterName, StringComparison.Ordinal))
                                {
                                    index = i;
                                    break;
                                }
                            }
                        }
                    }
 
                    arguments.GetOrCreateData<ParameterEarlyWellKnownAttributeData>().CallerArgumentExpressionParameterIndex = index;
                }
            }
 
            return base.EarlyDecodeWellKnownAttribute(ref arguments);
        }
 
        private (CSharpAttributeData?, BoundAttribute?) EarlyDecodeAttributeForDefaultParameterValue(AttributeDescription description, ref EarlyDecodeWellKnownAttributeArguments<EarlyWellKnownAttributeBinder, NamedTypeSymbol, AttributeSyntax, AttributeLocation> arguments)
        {
            Debug.Assert(description.Equals(AttributeDescription.DefaultParameterValueAttribute) ||
                description.Equals(AttributeDescription.DecimalConstantAttribute) ||
                description.Equals(AttributeDescription.DateTimeConstantAttribute));
 
            bool hasAnyDiagnostics;
            var (attributeData, boundAttribute) = arguments.Binder.GetAttribute(arguments.AttributeSyntax, arguments.AttributeType, beforeAttributePartBound: null, afterAttributePartBound: null, out hasAnyDiagnostics);
            ConstantValue value;
            if (attributeData.HasErrors)
            {
                value = ConstantValue.Bad;
                hasAnyDiagnostics = true;
            }
            else
            {
                value = DecodeDefaultParameterValueAttribute(description, attributeData, arguments.AttributeSyntax, diagnose: false, diagnosticsOpt: null);
            }
 
            var paramData = arguments.GetOrCreateData<ParameterEarlyWellKnownAttributeData>();
            if (paramData.DefaultParameterValue == ConstantValue.Unset)
            {
                paramData.DefaultParameterValue = value;
            }
 
            return !hasAnyDiagnostics ? (attributeData, boundAttribute) : (null, null);
        }
#nullable disable
 
        protected override void DecodeWellKnownAttributeImpl(ref DecodeWellKnownAttributeArguments<AttributeSyntax, CSharpAttributeData, AttributeLocation> arguments)
        {
            Debug.Assert((object)arguments.AttributeSyntaxOpt != null);
 
            var attribute = arguments.Attribute;
            Debug.Assert(!attribute.HasErrors);
            Debug.Assert(arguments.SymbolPart == AttributeLocation.None);
            Debug.Assert(AttributeDescription.InterpolatedStringHandlerArgumentAttribute.Signatures.Length == 2);
            var diagnostics = (BindingDiagnosticBag)arguments.Diagnostics;
 
            if (attribute.IsTargetAttribute(AttributeDescription.DefaultParameterValueAttribute))
            {
                // Attribute decoded and constant value stored during EarlyDecodeWellKnownAttribute.
                DecodeDefaultParameterValueAttribute(AttributeDescription.DefaultParameterValueAttribute, ref arguments);
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.DecimalConstantAttribute))
            {
                // Attribute decoded and constant value stored during EarlyDecodeWellKnownAttribute.
                DecodeDefaultParameterValueAttribute(AttributeDescription.DecimalConstantAttribute, ref arguments);
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.DateTimeConstantAttribute))
            {
                // Attribute decoded and constant value stored during EarlyDecodeWellKnownAttribute.
                DecodeDefaultParameterValueAttribute(AttributeDescription.DateTimeConstantAttribute, ref arguments);
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.OptionalAttribute))
            {
                Debug.Assert(_lazyHasOptionalAttribute == ThreeState.True);
 
                if (HasDefaultArgumentSyntax)
                {
                    // error CS1745: Cannot specify default parameter value in conjunction with DefaultParameterAttribute or OptionalAttribute
                    diagnostics.Add(ErrorCode.ERR_DefaultValueUsedWithAttributes, arguments.AttributeSyntaxOpt.Name.Location);
                }
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.ParamArrayAttribute) || attribute.IsTargetAttribute(AttributeDescription.ParamCollectionAttribute))
            {
                // error CS0674: Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Use the 'params' keyword instead.
                diagnostics.Add(ErrorCode.ERR_ExplicitParamArrayOrCollection, arguments.AttributeSyntaxOpt.Name.Location);
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.InAttribute))
            {
                arguments.GetOrCreateData<ParameterWellKnownAttributeData>().HasInAttribute = true;
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.OutAttribute))
            {
                arguments.GetOrCreateData<ParameterWellKnownAttributeData>().HasOutAttribute = true;
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.MarshalAsAttribute))
            {
                MarshalAsAttributeDecoder<ParameterWellKnownAttributeData, AttributeSyntax, CSharpAttributeData, AttributeLocation>.Decode(ref arguments, AttributeTargets.Parameter, MessageProvider.Instance);
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.IDispatchConstantAttribute))
            {
                arguments.GetOrCreateData<ParameterWellKnownAttributeData>().HasIDispatchConstantAttribute = true;
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.IUnknownConstantAttribute))
            {
                arguments.GetOrCreateData<ParameterWellKnownAttributeData>().HasIUnknownConstantAttribute = true;
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.CallerLineNumberAttribute))
            {
                ValidateCallerLineNumberAttribute(arguments.AttributeSyntaxOpt, diagnostics);
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.CallerFilePathAttribute))
            {
                ValidateCallerFilePathAttribute(arguments.AttributeSyntaxOpt, diagnostics);
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.CallerMemberNameAttribute))
            {
                ValidateCallerMemberNameAttribute(arguments.AttributeSyntaxOpt, diagnostics);
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.CallerArgumentExpressionAttribute))
            {
                ValidateCallerArgumentExpressionAttribute(arguments.AttributeSyntaxOpt, attribute, diagnostics);
            }
            else if (ReportExplicitUseOfReservedAttributes(in arguments,
                ReservedAttributes.DynamicAttribute |
                ReservedAttributes.IsReadOnlyAttribute |
                ReservedAttributes.RequiresLocationAttribute |
                ReservedAttributes.IsUnmanagedAttribute |
                ReservedAttributes.IsByRefLikeAttribute |
                ReservedAttributes.TupleElementNamesAttribute |
                ReservedAttributes.NullableAttribute |
                ReservedAttributes.NativeIntegerAttribute |
                ReservedAttributes.ScopedRefAttribute))
            {
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.AllowNullAttribute))
            {
                arguments.GetOrCreateData<ParameterWellKnownAttributeData>().HasAllowNullAttribute = true;
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.DisallowNullAttribute))
            {
                arguments.GetOrCreateData<ParameterWellKnownAttributeData>().HasDisallowNullAttribute = true;
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.MaybeNullAttribute))
            {
                arguments.GetOrCreateData<ParameterWellKnownAttributeData>().HasMaybeNullAttribute = true;
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.MaybeNullWhenAttribute))
            {
                arguments.GetOrCreateData<ParameterWellKnownAttributeData>().MaybeNullWhenAttribute = DecodeMaybeNullWhenOrNotNullWhenOrDoesNotReturnIfAttribute(attribute);
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.NotNullAttribute))
            {
                arguments.GetOrCreateData<ParameterWellKnownAttributeData>().HasNotNullAttribute = true;
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.NotNullWhenAttribute))
            {
                arguments.GetOrCreateData<ParameterWellKnownAttributeData>().NotNullWhenAttribute = DecodeMaybeNullWhenOrNotNullWhenOrDoesNotReturnIfAttribute(attribute);
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.DoesNotReturnIfAttribute))
            {
                arguments.GetOrCreateData<ParameterWellKnownAttributeData>().DoesNotReturnIfAttribute = DecodeMaybeNullWhenOrNotNullWhenOrDoesNotReturnIfAttribute(attribute);
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.NotNullIfNotNullAttribute))
            {
                arguments.GetOrCreateData<ParameterWellKnownAttributeData>().AddNotNullIfParameterNotNull(attribute.DecodeNotNullIfNotNullAttribute());
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.EnumeratorCancellationAttribute))
            {
                arguments.GetOrCreateData<ParameterWellKnownAttributeData>().HasEnumeratorCancellationAttribute = true;
                ValidateCancellationTokenAttribute(arguments.AttributeSyntaxOpt, (BindingDiagnosticBag)arguments.Diagnostics);
            }
            else if (attribute.GetTargetAttributeSignatureIndex(AttributeDescription.InterpolatedStringHandlerArgumentAttribute) is (0 or 1) and var index)
            {
                DecodeInterpolatedStringHandlerArgumentAttribute(ref arguments, diagnostics, index);
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.UnscopedRefAttribute))
            {
                if (!this.UseUpdatedEscapeRules)
                {
                    diagnostics.Add(ErrorCode.WRN_UnscopedRefAttributeOldRules, arguments.AttributeSyntaxOpt.Location);
                }
 
                if (!this.IsValidUnscopedRefAttributeTarget())
                {
                    diagnostics.Add(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, arguments.AttributeSyntaxOpt.Location);
                }
                else if (DeclaredScope != ScopedKind.None)
                {
                    diagnostics.Add(ErrorCode.ERR_UnscopedScoped, arguments.AttributeSyntaxOpt.Location);
                }
            }
        }
 
        private bool IsValidUnscopedRefAttributeTarget()
        {
            return RefKind != RefKind.None || (HasParamsModifier && Type.IsRefLikeOrAllowsRefLikeType());
        }
 
        private static bool? DecodeMaybeNullWhenOrNotNullWhenOrDoesNotReturnIfAttribute(CSharpAttributeData attribute)
        {
            var arguments = attribute.CommonConstructorArguments;
            return arguments.Length == 1 && arguments[0].TryDecodeValue(SpecialType.System_Boolean, out bool value) ?
                (bool?)value :
                null;
        }
 
        private void DecodeDefaultParameterValueAttribute(AttributeDescription description, ref DecodeWellKnownAttributeArguments<AttributeSyntax, CSharpAttributeData, AttributeLocation> arguments)
        {
            var attribute = arguments.Attribute;
            var syntax = arguments.AttributeSyntaxOpt;
            var diagnostics = (BindingDiagnosticBag)arguments.Diagnostics;
 
            Debug.Assert(syntax != null);
            Debug.Assert(diagnostics != null);
 
            var value = DecodeDefaultParameterValueAttribute(description, attribute, syntax, diagnose: true, diagnosticsOpt: diagnostics);
            if (!value.IsBad)
            {
                VerifyParamDefaultValueMatchesAttributeIfAny(value, syntax, diagnostics);
 
                if (this.RefKind == RefKind.RefReadOnlyParameter && this.IsOptional && this.ParameterSyntax.Default is null)
                {
                    // A default value is specified for 'ref readonly' parameter '{0}', but 'ref readonly' should be used only for references. Consider declaring the parameter as 'in'.
                    diagnostics.Add(ErrorCode.WRN_RefReadonlyParameterDefaultValue, syntax, this.Name);
                }
            }
        }
 
        /// <summary>
        /// Verify the default value matches the default value from any earlier attribute
        /// (DefaultParameterValueAttribute, DateTimeConstantAttribute or DecimalConstantAttribute).
        /// If not, report ERR_ParamDefaultValueDiffersFromAttribute.
        /// </summary>
        private void VerifyParamDefaultValueMatchesAttributeIfAny(ConstantValue value, SyntaxNode syntax, BindingDiagnosticBag diagnostics)
        {
            var data = GetEarlyDecodedWellKnownAttributeData();
            if (data != null)
            {
                var attrValue = data.DefaultParameterValue;
                if ((attrValue != ConstantValue.Unset) &&
                    (value != attrValue))
                {
                    // CS8017: The parameter has multiple distinct default values.
                    diagnostics.Add(ErrorCode.ERR_ParamDefaultValueDiffersFromAttribute, syntax.Location);
                }
            }
        }
 
        private ConstantValue DecodeDefaultParameterValueAttribute(AttributeDescription description, CSharpAttributeData attribute, AttributeSyntax node, bool diagnose, BindingDiagnosticBag diagnosticsOpt)
        {
            Debug.Assert(!attribute.HasErrors);
 
            if (description.Equals(AttributeDescription.DefaultParameterValueAttribute))
            {
                return DecodeDefaultParameterValueAttribute(attribute, node, diagnose, diagnosticsOpt);
            }
            else if (description.Equals(AttributeDescription.DecimalConstantAttribute))
            {
                return attribute.DecodeDecimalConstantValue();
            }
            else
            {
                Debug.Assert(description.Equals(AttributeDescription.DateTimeConstantAttribute));
                return attribute.DecodeDateTimeConstantValue();
            }
        }
 
        private ConstantValue DecodeDefaultParameterValueAttribute(CSharpAttributeData attribute, AttributeSyntax node, bool diagnose, BindingDiagnosticBag diagnosticsOpt)
        {
            Debug.Assert(!diagnose || diagnosticsOpt != null);
 
            if (HasDefaultArgumentSyntax)
            {
                // error CS1745: Cannot specify default parameter value in conjunction with DefaultParameterAttribute or OptionalAttribute
                if (diagnose)
                {
                    diagnosticsOpt.Add(ErrorCode.ERR_DefaultValueUsedWithAttributes, node.Name.Location);
                }
                return ConstantValue.Bad;
            }
 
            // BREAK: In dev10, DefaultParameterValueAttribute could not be applied to System.Type or array parameters.
            // When this was attempted, dev10 produced CS1909, ERR_DefaultValueBadParamType.  Roslyn takes a different
            // approach: instead of looking at the parameter type, we look at the argument type.  There's nothing wrong
            // with providing a default value for a System.Type or array parameter, as long as the default parameter
            // is not a System.Type or an array (i.e. null is fine).  Since we are no longer interested in the type of
            // the parameter, all occurrences of CS1909 have been replaced with CS1910, ERR_DefaultValueBadValueType,
            // to indicate that the argument type, rather than the parameter type, is the source of the problem.
 
            Debug.Assert(attribute.CommonConstructorArguments.Length == 1);
 
            // the type of the value is the type of the expression in the attribute:
            var arg = attribute.CommonConstructorArguments[0];
 
            SpecialType specialType = arg.Kind == TypedConstantKind.Enum ?
                ((NamedTypeSymbol)arg.TypeInternal).EnumUnderlyingType.SpecialType :
                arg.TypeInternal.SpecialType;
 
            var compilation = this.DeclaringCompilation;
            var constantValueDiscriminator = ConstantValue.GetDiscriminator(specialType);
            var useSiteInfo = new CompoundUseSiteInfo<AssemblySymbol>(diagnosticsOpt, ContainingAssembly);
            if (constantValueDiscriminator == ConstantValueTypeDiscriminator.Bad)
            {
                if (arg.Kind != TypedConstantKind.Array && arg.ValueInternal == null)
                {
                    if (this.Type.IsReferenceType)
                    {
                        constantValueDiscriminator = ConstantValueTypeDiscriminator.Null;
                    }
                    else
                    {
                        // error CS1908: The type of the argument to the DefaultParameterValue attribute must match the parameter type
                        if (diagnose)
                        {
                            diagnosticsOpt.Add(ErrorCode.ERR_DefaultValueTypeMustMatch, node.Name.Location);
                        }
                        return ConstantValue.Bad;
                    }
                }
                else
                {
                    // error CS1910: Argument of type '{0}' is not applicable for the DefaultParameterValue attribute
                    if (diagnose)
                    {
                        diagnosticsOpt.Add(ErrorCode.ERR_DefaultValueBadValueType, node.Name.Location, arg.TypeInternal);
                    }
                    return ConstantValue.Bad;
                }
            }
            else if (!compilation.Conversions.ClassifyConversionFromType((TypeSymbol)arg.TypeInternal, this.Type, isChecked: false, ref useSiteInfo).Kind.IsImplicitConversion())
            {
                // error CS1908: The type of the argument to the DefaultParameterValue attribute must match the parameter type
                if (diagnose)
                {
                    diagnosticsOpt.Add(ErrorCode.ERR_DefaultValueTypeMustMatch, node.Name.Location);
                    diagnosticsOpt.Add(node.Name, useSiteInfo);
                }
                return ConstantValue.Bad;
            }
 
            if (diagnose)
            {
                diagnosticsOpt.Add(node.Name, useSiteInfo);
            }
 
            return ConstantValue.Create(arg.ValueInternal, constantValueDiscriminator);
        }
 
        private bool IsValidCallerInfoContext(AttributeSyntax node) => !ContainingSymbol.IsExplicitInterfaceImplementation()
                                                                    && !ContainingSymbol.IsOperator()
                                                                    && !IsOnPartialImplementation(node);
 
#nullable enable
        /// <summary>
        /// Is the attribute syntax appearing on a parameter of a partial method implementation part?
        /// Since attributes are merged between the parts of a partial, we need to look at the syntax where the
        /// attribute appeared in the source to see if it corresponds to a partial method implementation part.
        /// </summary>
        private bool IsOnPartialImplementation(AttributeSyntax node)
        {
            // If we are asking this, the candidate attribute had better be contained in *some* attribute associated with this parameter syntactically
            Debug.Assert(this.GetAttributeDeclarations().Any(attrLists => attrLists.Any(attrList => attrList.Contains(node))));
 
            var implParameter = this.ContainingSymbol.IsPartialImplementation() ? this : PartialImplementationPart;
            if (implParameter?.AttributeDeclarationList is not { } implParameterAttributeList)
            {
                return false;
            }
 
            return implParameterAttributeList.Any(attrList => attrList.Attributes.Contains(node));
        }
#nullable disable
 
        private void ValidateCallerLineNumberAttribute(AttributeSyntax node, BindingDiagnosticBag diagnostics)
        {
            CSharpCompilation compilation = this.DeclaringCompilation;
            var useSiteInfo = new CompoundUseSiteInfo<AssemblySymbol>(diagnostics, ContainingAssembly);
 
            if (!IsValidCallerInfoContext(node))
            {
                // CS4024: The CallerLineNumberAttribute applied to parameter '{0}' will have no effect because it applies to a
                //         member that is used in contexts that do not allow optional arguments
                diagnostics.Add(ErrorCode.WRN_CallerLineNumberParamForUnconsumedLocation, node.Name.Location, ParameterSyntax.Identifier.ValueText);
            }
            else if (!compilation.Conversions.HasCallerLineNumberConversion(TypeWithAnnotations.Type, ref useSiteInfo))
            {
                // CS4017: CallerLineNumberAttribute cannot be applied because there are no standard conversions from type '{0}' to type '{1}'
                TypeSymbol intType = compilation.GetSpecialType(SpecialType.System_Int32);
                diagnostics.Add(ErrorCode.ERR_NoConversionForCallerLineNumberParam, node.Name.Location, intType, TypeWithAnnotations.Type);
            }
            else if (!HasExplicitDefaultValue && !ContainingSymbol.IsPartialImplementation()) // attribute applied to parameter without default
            {
                // Unconsumed location checks happen first, so we require a default value.
 
                // CS4020: The CallerLineNumberAttribute may only be applied to parameters with default values
                diagnostics.Add(ErrorCode.ERR_BadCallerLineNumberParamWithoutDefaultValue, node.Name.Location);
            }
 
            diagnostics.Add(node.Name, useSiteInfo);
        }
 
        private void ValidateCallerFilePathAttribute(AttributeSyntax node, BindingDiagnosticBag diagnostics)
        {
            CSharpCompilation compilation = this.DeclaringCompilation;
            var useSiteInfo = new CompoundUseSiteInfo<AssemblySymbol>(diagnostics, ContainingAssembly);
 
            if (!IsValidCallerInfoContext(node))
            {
                // CS4025: The CallerFilePathAttribute applied to parameter '{0}' will have no effect because it applies to a
                //         member that is used in contexts that do not allow optional arguments
                diagnostics.Add(ErrorCode.WRN_CallerFilePathParamForUnconsumedLocation, node.Name.Location, ParameterSyntax.Identifier.ValueText);
            }
            else if (!compilation.Conversions.HasCallerInfoStringConversion(TypeWithAnnotations.Type, ref useSiteInfo))
            {
                // CS4018: CallerFilePathAttribute cannot be applied because there are no standard conversions from type '{0}' to type '{1}'
                TypeSymbol stringType = compilation.GetSpecialType(SpecialType.System_String);
                diagnostics.Add(ErrorCode.ERR_NoConversionForCallerFilePathParam, node.Name.Location, stringType, TypeWithAnnotations.Type);
            }
            else if (!HasExplicitDefaultValue && !ContainingSymbol.IsPartialImplementation()) // attribute applied to parameter without default
            {
                // Unconsumed location checks happen first, so we require a default value.
 
                // CS4021: The CallerFilePathAttribute may only be applied to parameters with default values
                diagnostics.Add(ErrorCode.ERR_BadCallerFilePathParamWithoutDefaultValue, node.Name.Location);
            }
            else if (IsCallerLineNumber)
            {
                // CS7082: The CallerFilePathAttribute applied to parameter '{0}' will have no effect. It is overridden by the CallerLineNumberAttribute.
                diagnostics.Add(ErrorCode.WRN_CallerLineNumberPreferredOverCallerFilePath, node.Name.Location, ParameterSyntax.Identifier.ValueText);
            }
 
            diagnostics.Add(node.Name, useSiteInfo);
        }
 
        private void ValidateCallerMemberNameAttribute(AttributeSyntax node, BindingDiagnosticBag diagnostics)
        {
            CSharpCompilation compilation = this.DeclaringCompilation;
            var useSiteInfo = new CompoundUseSiteInfo<AssemblySymbol>(diagnostics, ContainingAssembly);
 
            if (!IsValidCallerInfoContext(node))
            {
                // CS4026: The CallerMemberNameAttribute applied to parameter '{0}' will have no effect because it applies to a
                //         member that is used in contexts that do not allow optional arguments
                diagnostics.Add(ErrorCode.WRN_CallerMemberNameParamForUnconsumedLocation, node.Name.Location, ParameterSyntax.Identifier.ValueText);
            }
            else if (!compilation.Conversions.HasCallerInfoStringConversion(TypeWithAnnotations.Type, ref useSiteInfo))
            {
                // CS4019: CallerMemberNameAttribute cannot be applied because there are no standard conversions from type '{0}' to type '{1}'
                TypeSymbol stringType = compilation.GetSpecialType(SpecialType.System_String);
                diagnostics.Add(ErrorCode.ERR_NoConversionForCallerMemberNameParam, node.Name.Location, stringType, TypeWithAnnotations.Type);
            }
            else if (!HasExplicitDefaultValue && !ContainingSymbol.IsPartialImplementation()) // attribute applied to parameter without default
            {
                // Unconsumed location checks happen first, so we require a default value.
 
                // CS4022: The CallerMemberNameAttribute may only be applied to parameters with default values
                diagnostics.Add(ErrorCode.ERR_BadCallerMemberNameParamWithoutDefaultValue, node.Name.Location);
            }
            else if (IsCallerLineNumber)
            {
                // CS7081: The CallerMemberNameAttribute applied to parameter '{0}' will have no effect. It is overridden by the CallerLineNumberAttribute.
                diagnostics.Add(ErrorCode.WRN_CallerLineNumberPreferredOverCallerMemberName, node.Name.Location, ParameterSyntax.Identifier.ValueText);
            }
            else if (IsCallerFilePath)
            {
                // CS7080: The CallerMemberNameAttribute applied to parameter '{0}' will have no effect. It is overridden by the CallerFilePathAttribute.
                diagnostics.Add(ErrorCode.WRN_CallerFilePathPreferredOverCallerMemberName, node.Name.Location, ParameterSyntax.Identifier.ValueText);
            }
 
            diagnostics.Add(node.Name, useSiteInfo);
        }
 
        private void ValidateCallerArgumentExpressionAttribute(AttributeSyntax node, CSharpAttributeData attribute, BindingDiagnosticBag diagnostics)
        {
            // We intentionally don't report an error for earlier language versions here. The attribute already existed
            // before the feature was developed. The error is only reported when the binder supplies a value
            // based on the attribute.
            CSharpCompilation compilation = this.DeclaringCompilation;
            var useSiteInfo = new CompoundUseSiteInfo<AssemblySymbol>(diagnostics, ContainingAssembly);
 
            if (!IsValidCallerInfoContext(node))
            {
                // CS8966: The CallerArgumentExpressionAttribute applied to parameter '{0}' will have no effect because it applies to a
                //         member that is used in contexts that do not allow optional arguments
                diagnostics.Add(ErrorCode.WRN_CallerArgumentExpressionParamForUnconsumedLocation, node.Name.Location, ParameterSyntax.Identifier.ValueText);
            }
            else if (!compilation.Conversions.HasCallerInfoStringConversion(TypeWithAnnotations.Type, ref useSiteInfo))
            {
                // CS8959: CallerArgumentExpressionAttribute cannot be applied because there are no standard conversions from type '{0}' to type '{1}'
                TypeSymbol stringType = compilation.GetSpecialType(SpecialType.System_String);
                diagnostics.Add(ErrorCode.ERR_NoConversionForCallerArgumentExpressionParam, node.Name.Location, stringType, TypeWithAnnotations.Type);
            }
            else if (!HasExplicitDefaultValue && !ContainingSymbol.IsPartialImplementation()) // attribute applied to parameter without default
            {
                // Unconsumed location checks happen first, so we require a default value.
 
                // CS8964: The CallerArgumentExpressionAttribute may only be applied to parameters with default values
                diagnostics.Add(ErrorCode.ERR_BadCallerArgumentExpressionParamWithoutDefaultValue, node.Name.Location);
            }
            else if (IsCallerLineNumber)
            {
                // CS8960: The CallerArgumentExpressionAttribute applied to parameter '{0}' will have no effect. It is overridden by the CallerLineNumberAttribute.
                diagnostics.Add(ErrorCode.WRN_CallerLineNumberPreferredOverCallerArgumentExpression, node.Name.Location, ParameterSyntax.Identifier.ValueText);
            }
            else if (IsCallerFilePath)
            {
                // CS8961: The CallerArgumentExpressionAttribute applied to parameter '{0}' will have no effect. It is overridden by the CallerFilePathAttribute.
                diagnostics.Add(ErrorCode.WRN_CallerFilePathPreferredOverCallerArgumentExpression, node.Name.Location, ParameterSyntax.Identifier.ValueText);
            }
            else if (IsCallerMemberName)
            {
                // CS8962: The CallerArgumentExpressionAttribute applied to parameter '{0}' will have no effect. It is overridden by the CallerMemberNameAttribute.
                diagnostics.Add(ErrorCode.WRN_CallerMemberNamePreferredOverCallerArgumentExpression, node.Name.Location, ParameterSyntax.Identifier.ValueText);
            }
            else if (attribute.CommonConstructorArguments.Length == 1 &&
                GetEarlyDecodedWellKnownAttributeData()?.CallerArgumentExpressionParameterIndex == -1)
            {
                // CS8963: The CallerArgumentExpressionAttribute applied to parameter '{0}' will have no effect. It is applied with an invalid parameter name.
                diagnostics.Add(ErrorCode.WRN_CallerArgumentExpressionAttributeHasInvalidParameterName, node.Name.Location, ParameterSyntax.Identifier.ValueText);
            }
            else if (GetEarlyDecodedWellKnownAttributeData()?.CallerArgumentExpressionParameterIndex == Ordinal)
            {
                // CS8965: The CallerArgumentExpressionAttribute applied to parameter '{0}' will have no effect because it's self-referential.
                diagnostics.Add(ErrorCode.WRN_CallerArgumentExpressionAttributeSelfReferential, node.Name.Location, ParameterSyntax.Identifier.ValueText);
            }
 
            diagnostics.Add(node.Name, useSiteInfo);
        }
 
        private void ValidateCancellationTokenAttribute(AttributeSyntax node, BindingDiagnosticBag diagnostics)
        {
            if (needsReporting())
            {
                diagnostics.Add(ErrorCode.WRN_UnconsumedEnumeratorCancellationAttributeUsage, node.Name.Location, ParameterSyntax.Identifier.ValueText);
            }
 
            bool needsReporting()
            {
                if (!Type.Equals(this.DeclaringCompilation.GetWellKnownType(WellKnownType.System_Threading_CancellationToken)))
                {
                    return true;
                }
                else if (this.ContainingSymbol is MethodSymbol method &&
                    method.IsAsync &&
                    method.ReturnType.OriginalDefinition.Equals(this.DeclaringCompilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T)))
                {
                    // Note: async methods that return this type must be iterators. This is enforced elsewhere
                    return false;
                }
 
                return true;
            }
        }
 
#nullable enable
        private void DecodeInterpolatedStringHandlerArgumentAttribute(ref DecodeWellKnownAttributeArguments<AttributeSyntax, CSharpAttributeData, AttributeLocation> arguments, BindingDiagnosticBag diagnostics, int attributeIndex)
        {
            Debug.Assert(attributeIndex is 0 or 1);
            Debug.Assert(arguments.Attribute.IsTargetAttribute(AttributeDescription.InterpolatedStringHandlerArgumentAttribute) && arguments.Attribute.CommonConstructorArguments.Length == 1);
            Debug.Assert(arguments.AttributeSyntaxOpt is not null);
 
            if (Type is not NamedTypeSymbol { IsInterpolatedStringHandlerType: true } handlerType)
            {
                // '{0}' is not an interpolated string handler type.
                diagnostics.Add(ErrorCode.ERR_TypeIsNotAnInterpolatedStringHandlerType, arguments.AttributeSyntaxOpt.Location, Type);
                setInterpolatedStringHandlerAttributeError(ref arguments);
                return;
            }
 
            if (this is LambdaParameterSymbol)
            {
                // Lambda parameters will ignore this attribute at usage
                diagnostics.Add(ErrorCode.WRN_InterpolatedStringHandlerArgumentAttributeIgnoredOnLambdaParameters, arguments.AttributeSyntaxOpt.Location);
            }
 
            TypedConstant constructorArgument = arguments.Attribute.CommonConstructorArguments[0];
 
            ImmutableArray<ParameterSymbol> containingSymbolParameters = ContainingSymbol.GetParameters();
 
            ImmutableArray<int> parameterOrdinals;
            ArrayBuilder<ParameterSymbol?> parameters;
            if (attributeIndex == 0)
            {
                if (decodeName(constructorArgument, ref arguments) is not (int ordinal, var parameter))
                {
                    // If an error needs to be reported, it will already have been reported by another step.
                    setInterpolatedStringHandlerAttributeError(ref arguments);
                    return;
                }
 
                parameterOrdinals = ImmutableArray.Create(ordinal);
                parameters = ArrayBuilder<ParameterSymbol?>.GetInstance(1);
                parameters.Add(parameter);
            }
            else if (attributeIndex == 1)
            {
                if (constructorArgument.IsNull)
                {
                    setInterpolatedStringHandlerAttributeError(ref arguments);
                    // null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name.
                    diagnostics.Add(ErrorCode.ERR_NullInvalidInterpolatedStringHandlerArgumentName, arguments.AttributeSyntaxOpt!.Location);
                    return;
                }
 
                bool hadError = false;
                parameters = ArrayBuilder<ParameterSymbol?>.GetInstance(constructorArgument.Values.Length);
                var ordinalsBuilder = ArrayBuilder<int>.GetInstance(constructorArgument.Values.Length);
                foreach (var nestedArgument in constructorArgument.Values)
                {
                    if (decodeName(nestedArgument, ref arguments) is (int ordinal, var parameter) && !hadError)
                    {
                        parameters.Add(parameter);
                        ordinalsBuilder.Add(ordinal);
                    }
                    else
                    {
                        hadError = true;
                    }
                }
 
                if (hadError)
                {
                    parameters.Free();
                    ordinalsBuilder.Free();
                    setInterpolatedStringHandlerAttributeError(ref arguments);
                    return;
                }
 
                parameterOrdinals = ordinalsBuilder.ToImmutableAndFree();
            }
            else
            {
                throw ExceptionUtilities.Unreachable();
            }
 
            var parameterWellKnownAttributeData = arguments.GetOrCreateData<ParameterWellKnownAttributeData>();
            parameterWellKnownAttributeData.InterpolatedStringHandlerArguments = parameterOrdinals;
 
            (int Ordinal, ParameterSymbol? Parameter)? decodeName(TypedConstant constant, ref DecodeWellKnownAttributeArguments<AttributeSyntax, CSharpAttributeData, AttributeLocation> arguments)
            {
                Debug.Assert(arguments.AttributeSyntaxOpt is not null);
                if (constant.IsNull)
                {
                    // null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name.
                    diagnostics.Add(ErrorCode.ERR_NullInvalidInterpolatedStringHandlerArgumentName, arguments.AttributeSyntaxOpt.Location);
                    return null;
                }
 
                if (constant.TypeInternal is not { SpecialType: SpecialType.System_String })
                {
                    // There has already been an error reported. Just return null.
                    return null;
                }
 
                var name = constant.DecodeValue<string>(SpecialType.System_String);
                Debug.Assert(name != null);
                if (name == "")
                {
                    // Name refers to the "this" instance parameter.
                    if (!ContainingSymbol.RequiresInstanceReceiver() || ContainingSymbol is MethodSymbol { MethodKind: MethodKind.Constructor or MethodKind.DelegateInvoke or MethodKind.LambdaMethod })
                    {
                        // '{0}' is not an instance method, the receiver cannot be an interpolated string handler argument.
                        diagnostics.Add(ErrorCode.ERR_NotInstanceInvalidInterpolatedStringHandlerArgumentName, arguments.AttributeSyntaxOpt.Location, ContainingSymbol);
                        return null;
                    }
 
                    return (BoundInterpolatedStringArgumentPlaceholder.InstanceParameter, null);
                }
 
                var parameter = containingSymbolParameters.FirstOrDefault(static (param, name) => string.Equals(param.Name, name, StringComparison.Ordinal), name);
                if (parameter is null)
                {
                    // '{0}' is not a valid parameter name from '{1}'.
                    diagnostics.Add(ErrorCode.ERR_InvalidInterpolatedStringHandlerArgumentName, arguments.AttributeSyntaxOpt.Location, name, ContainingSymbol);
                    return null;
                }
 
                if ((object)parameter == this)
                {
                    // InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on.
                    diagnostics.Add(ErrorCode.ERR_CannotUseSelfAsInterpolatedStringHandlerArgument, arguments.AttributeSyntaxOpt.Location);
                    return null;
                }
 
                if (parameter.Ordinal > Ordinal)
                {
                    // Parameter '{0}' occurs after '{1}' in the parameter list, but is used as an argument for interpolated string handler conversions.
                    // This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated
                    // string handler parameter after all arguments involved.
                    diagnostics.Add(ErrorCode.WRN_ParameterOccursAfterInterpolatedStringHandlerParameter, arguments.AttributeSyntaxOpt.Location, parameter.Name, this.Name);
                }
 
                return (parameter.Ordinal, parameter);
            }
 
            static void setInterpolatedStringHandlerAttributeError(ref DecodeWellKnownAttributeArguments<AttributeSyntax, CSharpAttributeData, AttributeLocation> arguments)
            {
                arguments.GetOrCreateData<ParameterWellKnownAttributeData>().InterpolatedStringHandlerArguments = default;
            }
        }
#nullable disable
 
        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(_lazyCustomAttributesBag != null);
            Debug.Assert(_lazyCustomAttributesBag.IsDecodedWellKnownAttributeDataComputed);
            Debug.Assert(symbolPart == AttributeLocation.None);
 
            var data = (ParameterWellKnownAttributeData)decodedData;
            if (data != null)
            {
                switch (RefKind)
                {
                    case RefKind.Ref:
                        if (data.HasOutAttribute && !data.HasInAttribute)
                        {
                            // error CS0662: Cannot specify the Out attribute on a ref parameter without also specifying the In attribute.
                            diagnostics.Add(ErrorCode.ERR_OutAttrOnRefParam, this.GetFirstLocation());
                        }
                        break;
                    case RefKind.Out:
                        if (data.HasInAttribute)
                        {
                            // error CS0036: An out parameter cannot have the In attribute.
                            diagnostics.Add(ErrorCode.ERR_InAttrOnOutParam, this.GetFirstLocation());
                        }
                        break;
                    case RefKind.In:
                        if (data.HasOutAttribute)
                        {
                            // error CS8355: An in parameter cannot have the Out attribute.
                            diagnostics.Add(ErrorCode.ERR_OutAttrOnInParam, this.GetFirstLocation());
                        }
                        break;
                    case RefKind.RefReadOnlyParameter:
                        if (data.HasOutAttribute)
                        {
                            // error: A ref readonly parameter cannot have the Out attribute.
                            diagnostics.Add(ErrorCode.ERR_OutAttrOnRefReadonlyParam, this.GetFirstLocation());
                        }
                        break;
                }
            }
 
            base.PostDecodeWellKnownAttributes(boundAttributes, allAttributeSyntaxNodes, diagnostics, symbolPart, decodedData);
        }
 
        /// <summary>
        /// True if the parameter has default argument syntax.
        /// </summary>
        internal override bool HasDefaultArgumentSyntax
        {
            get
            {
                return (_parameterSyntaxKind & ParameterFlags.DefaultParameter) != 0;
            }
        }
 
        /// <summary>
        /// True if the parameter is marked by <see cref="System.Runtime.InteropServices.OptionalAttribute"/>.
        /// </summary>
        internal sealed override bool HasOptionalAttribute
        {
            get
            {
                if (_lazyHasOptionalAttribute == ThreeState.Unknown)
                {
                    SourceParameterSymbol copyFrom = this.BoundAttributesSource;
 
                    // prevent infinite recursion:
                    Debug.Assert(!ReferenceEquals(copyFrom, this));
 
                    if ((object)copyFrom != null)
                    {
                        // Parameter of partial implementation.
                        // We bind the attributes only on the definition part and copy them over to the implementation.
                        _lazyHasOptionalAttribute = copyFrom.HasOptionalAttribute.ToThreeState();
                    }
                    else
                    {
                        // lazyHasOptionalAttribute is decoded early, hence we cannot reach here when binding attributes for this symbol.
                        // So it is fine to force complete attributes here.
 
                        var attributes = GetAttributes();
 
                        if (!attributes.Any())
                        {
                            _lazyHasOptionalAttribute = ThreeState.False;
                        }
                    }
                }
 
                Debug.Assert(_lazyHasOptionalAttribute.HasValue());
 
                return _lazyHasOptionalAttribute.Value();
            }
        }
 
        internal override bool IsMetadataOptional
        {
            get
            {
                // NOTE: IsMetadataOptional property can be invoked during overload resolution.
                // NOTE: Optional attribute is decoded very early in attribute binding phase, see method EarlyDecodeOptionalAttribute
                // NOTE: If you update the below check to look for any more attributes, make sure that they are decoded early.
                return HasDefaultArgumentSyntax || HasOptionalAttribute;
            }
        }
 
        internal sealed override bool IsMetadataIn
            => base.IsMetadataIn || GetDecodedWellKnownAttributeData()?.HasInAttribute == true;
 
        internal sealed override bool IsMetadataOut
            => base.IsMetadataOut || GetDecodedWellKnownAttributeData()?.HasOutAttribute == true;
 
        internal sealed override MarshalPseudoCustomAttributeData MarshallingInformation
            => GetDecodedWellKnownAttributeData()?.MarshallingInformation;
 
        protected sealed override bool HasParamsModifier => (_parameterSyntaxKind & ParameterFlags.HasParamsModifier) != 0;
 
        public sealed override bool IsParamsArray => (_parameterSyntaxKind & ParameterFlags.ParamsParameter) != 0 && this.Type.IsSZArray();
 
        public sealed override bool IsParamsCollection => (_parameterSyntaxKind & ParameterFlags.ParamsParameter) != 0 && !this.Type.IsSZArray();
 
        internal override bool IsExtensionMethodThis => (_parameterSyntaxKind & ParameterFlags.ExtensionThisParameter) != 0;
 
        public abstract override ImmutableArray<CustomModifier> RefCustomModifiers { get; }
 
        internal override void ForceComplete(SourceLocation locationOpt, Predicate<Symbol> filter, CancellationToken cancellationToken)
        {
            Debug.Assert(filter == null);
            _ = this.GetAttributes();
            _ = this.ExplicitDefaultConstantValue;
            DoMiscValidation();
            state.SpinWaitComplete(CompletionPart.ComplexParameterSymbolAll, cancellationToken);
        }
 
#nullable enable
 
        private void DoMiscValidation()
        {
            if (state.NotePartComplete(CompletionPart.StartMiscValidation))
            {
                var diagnostics = BindingDiagnosticBag.GetInstance();
 
                if (IsParams && ParameterSyntax?.Modifiers.Any(SyntaxKind.ParamsKeyword) == true)
                {
                    validateParamsType(diagnostics);
                }
 
                if (DeclaredScope == ScopedKind.ScopedValue && !Type.IsErrorOrRefLikeOrAllowsRefLikeType())
                {
                    Debug.Assert(ParameterSyntax is not null);
                    diagnostics.Add(ErrorCode.ERR_ScopedRefAndRefStructOnly, ParameterSyntax);
                }
 
                AddDeclarationDiagnostics(diagnostics);
                diagnostics.Free();
 
                bool completedOnThisThread = state.NotePartComplete(CompletionPart.EndMiscValidation);
                Debug.Assert(completedOnThisThread);
            }
 
            state.SpinWaitComplete(CompletionPart.EndMiscValidation, default(CancellationToken));
 
            void validateParamsType(BindingDiagnosticBag diagnostics)
            {
                var collectionTypeKind = ConversionsBase.GetCollectionExpressionTypeKind(DeclaringCompilation, Type, out TypeWithAnnotations elementTypeWithAnnotations);
 
                var elementType = elementTypeWithAnnotations.Type;
                switch (collectionTypeKind)
                {
                    case CollectionExpressionTypeKind.None:
                        reportInvalidParams(diagnostics);
                        return;
 
                    case CollectionExpressionTypeKind.ImplementsIEnumerable:
                        {
                            var syntax = ParameterSyntax;
                            var binder = GetDefaultParameterValueBinder(syntax).WithContainingMemberOrLambda(ContainingSymbol); // this binder is good for our purpose
 
                            binder.TryGetCollectionIterationType(syntax, Type, out elementTypeWithAnnotations);
                            elementType = elementTypeWithAnnotations.Type;
                            if (elementType is null)
                            {
                                reportInvalidParams(diagnostics);
                                return;
                            }
 
                            if (!binder.HasCollectionExpressionApplicableConstructor(syntax, Type, out MethodSymbol? constructor, isExpanded: out _, diagnostics, isParamsModifierValidation: true))
                            {
                                return;
                            }
 
                            if (constructor is not null)
                            {
                                checkIsAtLeastAsVisible(syntax, binder, constructor, diagnostics);
                            }
 
                            if (!binder.HasCollectionExpressionApplicableAddMethod(syntax, Type, out ImmutableArray<MethodSymbol> addMethods, diagnostics))
                            {
                                return;
                            }
 
                            Debug.Assert(!addMethods.IsDefaultOrEmpty);
 
                            if (addMethods[0].IsStatic) // No need to check other methods, extensions are never mixed with instance methods
                            {
                                Debug.Assert(addMethods[0].IsExtensionMethod);
                                diagnostics.Add(ErrorCode.ERR_ParamsCollectionExtensionAddMethod, syntax, Type);
                                return;
                            }
 
                            MethodSymbol? reportAsLessVisible = null;
 
                            foreach (var addMethod in addMethods)
                            {
                                if (isAtLeastAsVisible(syntax, binder, addMethod, diagnostics))
                                {
                                    reportAsLessVisible = null;
                                    break;
                                }
                                else
                                {
                                    reportAsLessVisible ??= addMethod;
                                }
                            }
 
                            if (reportAsLessVisible is not null)
                            {
                                diagnostics.Add(ErrorCode.ERR_ParamsMemberCannotBeLessVisibleThanDeclaringMember, syntax, reportAsLessVisible, ContainingSymbol);
                            }
                        }
                        break;
 
                    case CollectionExpressionTypeKind.CollectionBuilder:
                        {
                            var syntax = ParameterSyntax;
                            var binder = GetDefaultParameterValueBinder(syntax).WithContainingMemberOrLambda(ContainingSymbol); // this binder is good for our purpose
 
                            binder.TryGetCollectionIterationType(syntax, Type, out elementTypeWithAnnotations);
                            elementType = elementTypeWithAnnotations.Type;
                            if (elementType is null)
                            {
                                reportInvalidParams(diagnostics);
                                return;
                            }
 
                            MethodSymbol? collectionBuilderMethod = binder.GetAndValidateCollectionBuilderMethod(syntax, (NamedTypeSymbol)Type, diagnostics, elementType: out _);
                            if (collectionBuilderMethod is null)
                            {
                                return;
                            }
 
                            if (ContainingSymbol.ContainingSymbol is NamedTypeSymbol) // No need to check for lambdas or local function
                            {
                                checkIsAtLeastAsVisible(syntax, binder, collectionBuilderMethod, diagnostics);
                            }
                        }
                        break;
                }
 
                Debug.Assert(elementType is { });
 
                if (collectionTypeKind != CollectionExpressionTypeKind.Array)
                {
                    MessageID.IDS_FeatureParamsCollections.CheckFeatureAvailability(diagnostics, ParameterSyntax);
                }
            }
 
            bool isAtLeastAsVisible(ParameterSyntax syntax, Binder binder, MethodSymbol method, BindingDiagnosticBag diagnostics)
            {
                var useSiteInfo = binder.GetNewCompoundUseSiteInfo(diagnostics);
 
                bool result = method.IsAsRestrictive(ContainingSymbol, ref useSiteInfo) &&
                              method.ContainingType.IsAtLeastAsVisibleAs(ContainingSymbol, ref useSiteInfo);
 
                diagnostics.Add(syntax.Location, useSiteInfo);
                return result;
            }
 
            void checkIsAtLeastAsVisible(ParameterSyntax syntax, Binder binder, MethodSymbol method, BindingDiagnosticBag diagnostics)
            {
                if (!isAtLeastAsVisible(syntax, binder, method, diagnostics))
                {
                    diagnostics.Add(ErrorCode.ERR_ParamsMemberCannotBeLessVisibleThanDeclaringMember, syntax, method, ContainingSymbol);
                }
            }
 
            void reportInvalidParams(BindingDiagnosticBag diagnostics)
            {
                diagnostics.Add(ErrorCode.ERR_ParamsMustBeCollection, ParameterSyntax.Modifiers.First(static m => m.IsKind(SyntaxKind.ParamsKeyword)).GetLocation());
            }
        }
 
#nullable disable
    }
 
    internal sealed class SourceComplexParameterSymbol : SourceComplexParameterSymbolBase
    {
        private readonly TypeWithAnnotations _parameterType;
 
        internal SourceComplexParameterSymbol(
            Symbol owner,
            int ordinal,
            TypeWithAnnotations parameterType,
            RefKind refKind,
            string name,
            Location location,
            SyntaxReference syntaxRef,
            bool hasParamsModifier,
            bool isParams,
            bool isExtensionMethodThis,
            ScopedKind scope)
            : base(owner, ordinal, refKind, name, location, syntaxRef, hasParamsModifier: hasParamsModifier, isParams: isParams, isExtensionMethodThis, scope)
        {
            _parameterType = parameterType;
        }
 
        public override TypeWithAnnotations TypeWithAnnotations => _parameterType;
        public override ImmutableArray<CustomModifier> RefCustomModifiers => ImmutableArray<CustomModifier>.Empty;
    }
 
    internal sealed class SourceComplexParameterSymbolWithCustomModifiersPrecedingRef : SourceComplexParameterSymbolBase
    {
        private readonly ImmutableArray<CustomModifier> _refCustomModifiers;
        private readonly TypeWithAnnotations _parameterType;
 
        internal SourceComplexParameterSymbolWithCustomModifiersPrecedingRef(
            Symbol owner,
            int ordinal,
            TypeWithAnnotations parameterType,
            RefKind refKind,
            ImmutableArray<CustomModifier> refCustomModifiers,
            string name,
            Location location,
            SyntaxReference syntaxRef,
            bool hasParamsModifier,
            bool isParams,
            bool isExtensionMethodThis,
            ScopedKind scope)
            : base(owner, ordinal, refKind, name, location, syntaxRef, hasParamsModifier: hasParamsModifier, isParams: isParams, isExtensionMethodThis, scope)
        {
            Debug.Assert(!refCustomModifiers.IsEmpty);
 
            _parameterType = parameterType;
            _refCustomModifiers = refCustomModifiers;
 
            Debug.Assert(refKind != RefKind.None || _refCustomModifiers.IsEmpty);
        }
 
        public override TypeWithAnnotations TypeWithAnnotations => _parameterType;
        public override ImmutableArray<CustomModifier> RefCustomModifiers => _refCustomModifiers;
    }
}