File: Symbols\Source\SourceOrdinaryMethodSymbol.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.Globalization;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
    internal abstract class SourceOrdinaryMethodSymbol : SourceOrdinaryMethodSymbolBase
    {
        public static SourceOrdinaryMethodSymbol CreateMethodSymbol(
            NamedTypeSymbol containingType,
            Binder bodyBinder,
            MethodDeclarationSyntax syntax,
            bool isNullableAnalysisEnabled,
            BindingDiagnosticBag diagnostics)
        {
            var interfaceSpecifier = syntax.ExplicitInterfaceSpecifier;
            var nameToken = syntax.Identifier;
 
            TypeSymbol explicitInterfaceType;
            var name = ExplicitInterfaceHelpers.GetMemberNameAndInterfaceSymbol(bodyBinder, interfaceSpecifier, nameToken.ValueText, diagnostics, out explicitInterfaceType, aliasQualifierOpt: out _);
            var location = new SourceLocation(nameToken);
 
            var methodKind = interfaceSpecifier == null
                ? MethodKind.Ordinary
                : MethodKind.ExplicitInterfaceImplementation;
 
            // Use a smaller type for the common case of non-generic, non-partial, non-explicit-impl methods.
 
            return explicitInterfaceType is null && !syntax.Modifiers.Any(SyntaxKind.PartialKeyword) && syntax.Arity == 0
                ? new SourceOrdinaryMethodSymbolSimple(containingType, name, location, syntax, methodKind, isNullableAnalysisEnabled, diagnostics)
                : new SourceOrdinaryMethodSymbolComplex(containingType, explicitInterfaceType, name, location, syntax, methodKind, isNullableAnalysisEnabled, diagnostics);
        }
 
        private SourceOrdinaryMethodSymbol(
            NamedTypeSymbol containingType,
            string name,
            Location location,
            MethodDeclarationSyntax syntax,
            MethodKind methodKind,
            bool isNullableAnalysisEnabled,
            BindingDiagnosticBag diagnostics) :
            base(containingType,
                 name,
                 location,
                 syntax,
                 isIterator: SyntaxFacts.HasYieldOperations(syntax.Body),
                 MakeModifiersAndFlags(containingType, location, syntax, methodKind, isNullableAnalysisEnabled, diagnostics, out bool hasExplicitAccessMod))
        {
            Debug.Assert(diagnostics.DiagnosticBag is object);
 
            this.CheckUnsafeModifier(DeclarationModifiers, diagnostics);
 
            HasExplicitAccessModifier = hasExplicitAccessMod;
            bool hasAnyBody = syntax.HasAnyBody();
 
            CheckFeatureAvailabilityAndRuntimeSupport(syntax, location, hasAnyBody, diagnostics);
 
            if (hasAnyBody)
            {
                CheckModifiersForBody(location, diagnostics);
            }
 
            ModifierUtils.CheckAccessibility(this.DeclarationModifiers, this, isExplicitInterfaceImplementation: methodKind == MethodKind.ExplicitInterfaceImplementation, diagnostics, location);
 
            Debug.Assert(syntax.ReturnType is not ScopedTypeSyntax);
 
            if (syntax.Arity == 0)
            {
                ReportErrorIfHasConstraints(syntax.ConstraintClauses, diagnostics.DiagnosticBag);
            }
 
            CheckForBlockAndExpressionBody(
                syntax.Body, syntax.ExpressionBody, syntax, diagnostics);
        }
 
        private static (DeclarationModifiers, Flags) MakeModifiersAndFlags(
            NamedTypeSymbol containingType, Location location, MethodDeclarationSyntax syntax, MethodKind methodKind,
            bool isNullableAnalysisEnabled, BindingDiagnosticBag diagnostics, out bool hasExplicitAccessMod)
        {
            (DeclarationModifiers declarationModifiers, hasExplicitAccessMod) = MakeModifiers(syntax, containingType, methodKind, hasBody: syntax.HasAnyBody(), location, diagnostics);
            Flags flags = new Flags(
                                    methodKind, refKind: syntax.ReturnType.SkipScoped(out _).GetRefKindInLocalOrReturn(diagnostics),
                                    declarationModifiers,
                                    returnsVoid: false, // The correct value will be computed lazily later and then the flags will be fixed up.
                                    returnsVoidIsSet: false,
                                    hasAnyBody: syntax.HasAnyBody(), isExpressionBodied: syntax.IsExpressionBodied(),
                                    isExtensionMethod: syntax.ParameterList.Parameters.FirstOrDefault() is ParameterSyntax firstParam &&
                                                       !firstParam.IsArgList &&
                                                       firstParam.Modifiers.Any(SyntaxKind.ThisKeyword),
                                    isNullableAnalysisEnabled: isNullableAnalysisEnabled,
                                    isVararg: syntax.IsVarArg(),
                                    isExplicitInterfaceImplementation: methodKind == MethodKind.ExplicitInterfaceImplementation,
                                    hasThisInitializer: false);
 
            return (declarationModifiers, flags);
        }
 
        private bool HasAnyBody => flags.HasAnyBody;
 
        private (TypeWithAnnotations ReturnType, ImmutableArray<ParameterSymbol> Parameters, ImmutableArray<TypeParameterConstraintClause> DeclaredConstraintsForOverrideOrImplementation) MakeParametersAndBindReturnType(BindingDiagnosticBag diagnostics)
        {
            var syntax = GetSyntax();
            var withTypeParamsBinder = this.DeclaringCompilation.GetBinderFactory(syntax.SyntaxTree).GetBinder(syntax.ReturnType, syntax, this);
 
            // Constraint checking for parameter and return types must be delayed until
            // the method has been added to the containing type member list since
            // evaluating the constraints may depend on accessing this method from
            // the container (comparing this method to others to find overrides for
            // instance). Constraints are checked in AfterAddingTypeMembersChecks.
            var signatureBinder = withTypeParamsBinder.WithAdditionalFlagsAndContainingMemberOrLambda(BinderFlags.SuppressConstraintChecks, this);
 
            ImmutableArray<ParameterSymbol> parameters = ParameterHelpers.MakeParameters(
                signatureBinder, this, syntax.ParameterList, out _,
                allowRefOrOut: true,
                allowThis: true,
                addRefReadOnlyModifier: IsVirtual || IsAbstract,
                diagnostics: diagnostics).Cast<SourceParameterSymbol, ParameterSymbol>();
 
            var returnTypeSyntax = syntax.ReturnType;
            Debug.Assert(returnTypeSyntax is not ScopedTypeSyntax);
 
            returnTypeSyntax = returnTypeSyntax.SkipScoped(out _).SkipRef();
            TypeWithAnnotations returnType = signatureBinder.BindType(returnTypeSyntax, diagnostics);
 
            // span-like types are returnable in general
            if (returnType.IsRestrictedType(ignoreSpanLikeTypes: true))
            {
                if (returnType.SpecialType == SpecialType.System_TypedReference &&
                    (this.ContainingType.SpecialType == SpecialType.System_TypedReference || this.ContainingType.SpecialType == SpecialType.System_ArgIterator))
                {
                    // Two special cases: methods in the special types TypedReference and ArgIterator are allowed to return TypedReference
                }
                else
                {
                    // The return type of a method, delegate, or function pointer cannot be '{0}'
                    diagnostics.Add(ErrorCode.ERR_MethodReturnCantBeRefAny, syntax.ReturnType.Location, returnType.Type);
                }
            }
 
            Debug.Assert(this.RefKind == RefKind.None || !returnType.IsVoidType() || returnTypeSyntax.HasErrors);
 
            ImmutableArray<TypeParameterConstraintClause> declaredConstraints = default;
 
            if (this.Arity != 0 && (syntax.ExplicitInterfaceSpecifier != null || IsOverride))
            {
                // When a generic method overrides a generic method declared in a base class, or is an 
                // explicit interface member implementation of a method in a base interface, the method
                // shall not specify any type-parameter-constraints-clauses, except for a struct, class, or default constraint.
                // In these cases, the type parameters of the method inherit constraints from the method being overridden or 
                // implemented.
                if (syntax.ConstraintClauses.Count > 0)
                {
                    Binder.CheckFeatureAvailability(
                        syntax.ConstraintClauses[0].WhereKeyword, MessageID.IDS_OverrideWithConstraints, diagnostics);
 
                    declaredConstraints = signatureBinder.WithAdditionalFlags(BinderFlags.GenericConstraintsClause | BinderFlags.SuppressConstraintChecks).
                                              BindTypeParameterConstraintClauses(this, TypeParameters, syntax.TypeParameterList, syntax.ConstraintClauses,
                                                                                 diagnostics, performOnlyCycleSafeValidation: false, isForOverride: true);
                    Debug.Assert(declaredConstraints.All(clause => clause.ConstraintTypes.IsEmpty));
                }
 
                // Force resolution of nullable type parameter used in the signature of an override or explicit interface implementation
                // based on constraints specified by the declaration.
                foreach (var param in parameters)
                {
                    forceMethodTypeParameters(param.TypeWithAnnotations, this, declaredConstraints);
                }
 
                forceMethodTypeParameters(returnType, this, declaredConstraints);
            }
 
            return (returnType, parameters, declaredConstraints);
 
            static void forceMethodTypeParameters(TypeWithAnnotations type, SourceOrdinaryMethodSymbol method, ImmutableArray<TypeParameterConstraintClause> declaredConstraints)
            {
                type.VisitType(null, (type, args, unused2) =>
                {
                    if (type.DefaultType is TypeParameterSymbol typeParameterSymbol && typeParameterSymbol.DeclaringMethod == (object)args.method)
                    {
                        var asValueType = args.declaredConstraints.IsDefault ||
                            (args.declaredConstraints[typeParameterSymbol.Ordinal].Constraints & (TypeParameterConstraintKind.ReferenceType | TypeParameterConstraintKind.Default)) == 0;
                        type.TryForceResolve(asValueType);
                    }
                    return false;
                }, typePredicate: null, arg: (method, declaredConstraints), canDigThroughNullable: false, useDefaultType: true);
            }
        }
 
        protected sealed override void ExtensionMethodChecks(BindingDiagnosticBag diagnostics)
        {
            // errors relevant for extension methods
            if (IsExtensionMethod)
            {
                var syntax = GetSyntax();
                var parameter0Type = this.Parameters[0].TypeWithAnnotations;
                var parameter0RefKind = this.Parameters[0].RefKind;
                if (!parameter0Type.Type.IsValidExtensionParameterType())
                {
                    // Duplicate Dev10 behavior by selecting the parameter type.
                    var parameterSyntax = syntax.ParameterList.Parameters[0];
                    Debug.Assert(parameterSyntax.Type != null);
                    var loc = parameterSyntax.Type.Location;
                    diagnostics.Add(ErrorCode.ERR_BadTypeforThis, loc, parameter0Type.Type);
                }
                else if (parameter0RefKind == RefKind.Ref && !parameter0Type.Type.IsValueType)
                {
                    diagnostics.Add(ErrorCode.ERR_RefExtensionMustBeValueTypeOrConstrainedToOne, _location, Name);
                }
                else if (parameter0RefKind is RefKind.In or RefKind.RefReadOnlyParameter && parameter0Type.TypeKind != TypeKind.Struct)
                {
                    diagnostics.Add(ErrorCode.ERR_InExtensionMustBeValueType, _location, Name);
                }
                else if ((object)ContainingType.ContainingType != null)
                {
                    diagnostics.Add(ErrorCode.ERR_ExtensionMethodsDecl, _location, ContainingType.Name);
                }
                else if (!ContainingType.IsScriptClass && !(ContainingType.IsStatic && ContainingType.Arity == 0))
                {
                    // Duplicate Dev10 behavior by selecting the containing type identifier. However if there
                    // is no containing type (in the interactive case for instance), select the method identifier.
                    var typeDecl = syntax.Parent as TypeDeclarationSyntax;
                    var identifier = (typeDecl != null) ? typeDecl.Identifier : syntax.Identifier;
                    var loc = identifier.GetLocation();
                    diagnostics.Add(ErrorCode.ERR_BadExtensionAgg, loc);
                }
                else if (!IsStatic)
                {
                    diagnostics.Add(ErrorCode.ERR_BadExtensionMeth, _location);
                }
                else
                {
                    // Verify ExtensionAttribute is available.
                    var attributeConstructor = Binder.GetWellKnownTypeMember(DeclaringCompilation, WellKnownMember.System_Runtime_CompilerServices_ExtensionAttribute__ctor, out var useSiteInfo);
 
                    var thisKeyword = syntax.ParameterList.Parameters[0].Modifiers.FirstOrDefault(SyntaxKind.ThisKeyword);
                    if ((object)attributeConstructor == null)
                    {
                        var memberDescriptor = WellKnownMembers.GetDescriptor(WellKnownMember.System_Runtime_CompilerServices_ExtensionAttribute__ctor);
                        // do not use Binder.ReportUseSiteErrorForAttributeCtor in this case, because we'll need to report a special error id, not a generic use site error.
                        diagnostics.Add(
                            ErrorCode.ERR_ExtensionAttrNotFound,
                            thisKeyword.GetLocation(),
                            memberDescriptor.DeclaringTypeMetadataName);
                    }
                    else
                    {
                        diagnostics.Add(useSiteInfo, thisKeyword);
                    }
                }
            }
        }
 
        protected sealed override Location ReturnTypeLocation => GetSyntax().ReturnType.Location;
 
        internal MethodDeclarationSyntax GetSyntax()
        {
            Debug.Assert(syntaxReferenceOpt != null);
            return (MethodDeclarationSyntax)syntaxReferenceOpt.GetSyntax();
        }
 
        internal sealed override ExecutableCodeBinder TryGetBodyBinder(BinderFactory binderFactoryOpt = null, bool ignoreAccessibility = false)
        {
            return TryGetBodyBinderFromSyntax(binderFactoryOpt, ignoreAccessibility);
        }
 
        protected sealed override void CompleteAsyncMethodChecksBetweenStartAndFinish()
        {
            if (IsPartialDefinition)
            {
                DeclaringCompilation.SymbolDeclaredEvent(this);
            }
        }
 
        protected sealed override int GetParameterCountFromSyntax() => GetSyntax().ParameterList.ParameterCount;
 
        internal static void InitializePartialMethodParts(SourceOrdinaryMethodSymbol definition, SourceOrdinaryMethodSymbol implementation)
        {
            Debug.Assert(definition.IsPartialDefinition);
            Debug.Assert(implementation.IsPartialImplementation);
 
            // Thse casts must succeed as being partial means we would have created the uncommon forms.
            SourceOrdinaryMethodSymbolComplex.InitializePartialMethodParts(
                (SourceOrdinaryMethodSymbolComplex)definition,
                (SourceOrdinaryMethodSymbolComplex)implementation);
        }
 
        /// <summary>
        /// If this is a partial implementation part returns the definition part and vice versa.
        /// </summary>
        internal abstract SourceOrdinaryMethodSymbol OtherPartOfPartial { get; }
 
        /// <summary>
        /// Returns true if this symbol represents a partial method definition (the part that specifies a signature but no body).
        /// </summary>
        internal bool IsPartialDefinition
        {
            get
            {
                return this.IsPartial && !HasAnyBody && !HasExternModifier;
            }
        }
 
        /// <summary>
        /// Returns true if this symbol represents a partial method implementation (the part that specifies both signature and body).
        /// </summary>
        internal bool IsPartialImplementation
        {
            get
            {
                return this.IsPartial && (HasAnyBody || HasExternModifier);
            }
        }
 
        /// <summary>
        /// True if this is a partial method that doesn't have an implementation part.
        /// </summary>
        internal bool IsPartialWithoutImplementation
        {
            get
            {
                return this.IsPartialDefinition && this.OtherPartOfPartial is null;
            }
        }
 
        internal SourceOrdinaryMethodSymbol SourcePartialDefinition
        {
            get
            {
                return this.IsPartialImplementation ? this.OtherPartOfPartial : null;
            }
        }
 
        internal SourceOrdinaryMethodSymbol SourcePartialImplementation
        {
            get
            {
                return this.IsPartialDefinition ? this.OtherPartOfPartial : null;
            }
        }
 
        public sealed override MethodSymbol PartialDefinitionPart
        {
            get
            {
                return SourcePartialDefinition;
            }
        }
 
        public sealed override MethodSymbol PartialImplementationPart
        {
            get
            {
                return SourcePartialImplementation;
            }
        }
 
        public sealed override bool IsExtern
        {
            get
            {
                return IsPartialDefinition
                    ? this.OtherPartOfPartial?.IsExtern ?? false
                    : HasExternModifier;
            }
        }
 
        public sealed override string GetDocumentationCommentXml(CultureInfo preferredCulture = null, bool expandIncludes = false, CancellationToken cancellationToken = default(CancellationToken))
        {
            ref var lazyDocComment = ref expandIncludes ? ref this.lazyExpandedDocComment : ref this.lazyDocComment;
            return SourceDocumentationCommentUtils.GetAndCacheDocumentationComment(this, expandIncludes, ref lazyDocComment);
        }
 
        protected sealed override SourceMemberMethodSymbol BoundAttributesSource
        {
            get
            {
                return this.SourcePartialDefinition;
            }
        }
 
        internal sealed override OneOrMany<SyntaxList<AttributeListSyntax>> GetAttributeDeclarations()
        {
            // Attributes on partial methods are owned by the definition part.
            // If this symbol has a non-null PartialDefinitionPart, we should have accessed this method through that definition symbol instead
            Debug.Assert(PartialDefinitionPart is null);
 
            if ((object)this.SourcePartialImplementation != null)
            {
                return OneOrMany.Create(ImmutableArray.Create(AttributeDeclarationSyntaxList, this.SourcePartialImplementation.AttributeDeclarationSyntaxList));
            }
            else
            {
                return OneOrMany.Create(AttributeDeclarationSyntaxList);
            }
        }
 
        private SyntaxList<AttributeListSyntax> AttributeDeclarationSyntaxList
        {
            get
            {
                var sourceContainer = this.ContainingType as SourceMemberContainerTypeSymbol;
                if ((object)sourceContainer != null && sourceContainer.AnyMemberHasAttributes)
                {
                    return this.GetSyntax().AttributeLists;
                }
 
                return default(SyntaxList<AttributeListSyntax>);
            }
        }
 
        private static DeclarationModifiers MakeDeclarationModifiers(MethodDeclarationSyntax syntax, NamedTypeSymbol containingType, Location location, DeclarationModifiers allowedModifiers, BindingDiagnosticBag diagnostics)
        {
            return ModifierUtils.MakeAndCheckNonTypeMemberModifiers(isOrdinaryMethod: true, isForInterfaceMember: containingType.IsInterface,
                                                                    syntax.Modifiers, defaultAccess: DeclarationModifiers.None, allowedModifiers, location, diagnostics, out _);
        }
 
        internal sealed override void ForceComplete(SourceLocation locationOpt, Predicate<Symbol> filter, CancellationToken cancellationToken)
        {
            var implementingPart = this.SourcePartialImplementation;
            if ((object)implementingPart != null)
            {
                implementingPart.ForceComplete(locationOpt, filter, cancellationToken);
            }
 
            base.ForceComplete(locationOpt, filter, cancellationToken);
        }
 
        public sealed override bool IsDefinedInSourceTree(
            SyntaxTree tree,
            TextSpan? definedWithinSpan,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            // Since only the declaring (and not the implementing) part of a partial method appears in the member
            // list, we need to ensure we complete the implementation part when needed.
            Debug.Assert(this.DeclaringSyntaxReferences.Length == 1);
            return
                IsDefinedInSourceTree(this.SyntaxRef, tree, definedWithinSpan) ||
                this.SourcePartialImplementation?.IsDefinedInSourceTree(tree, definedWithinSpan, cancellationToken) == true;
        }
 
        protected abstract override void CheckConstraintsForExplicitInterfaceType(ConversionsBase conversions, BindingDiagnosticBag diagnostics);
 
        protected sealed override void PartialMethodChecks(BindingDiagnosticBag diagnostics)
        {
            var implementingPart = this.SourcePartialImplementation;
            if ((object)implementingPart != null)
            {
                PartialMethodChecks(this, implementingPart, diagnostics);
            }
        }
 
        /// <summary>
        /// Report differences between the defining and implementing
        /// parts of a partial method. Diagnostics are reported on the
        /// implementing part, matching Dev10 behavior.
        /// </summary>
        /// <remarks>
        /// This method is analogous to <see cref="SourcePropertySymbol.PartialPropertyChecks" />.
        /// Whenever new checks are added to this method, the other method should also have those checks added, if applicable.
        /// </remarks>
        private static void PartialMethodChecks(SourceOrdinaryMethodSymbol definition, SourceOrdinaryMethodSymbol implementation, BindingDiagnosticBag diagnostics)
        {
            Debug.Assert(!ReferenceEquals(definition, implementation));
 
            MethodSymbol constructedDefinition = definition.ConstructIfGeneric(TypeMap.TypeParametersAsTypeSymbolsWithIgnoredAnnotations(implementation.TypeParameters));
            bool hasTypeDifferences = !constructedDefinition.ReturnTypeWithAnnotations.Equals(implementation.ReturnTypeWithAnnotations, TypeCompareKind.AllIgnoreOptions);
            if (hasTypeDifferences)
            {
                diagnostics.Add(ErrorCode.ERR_PartialMethodReturnTypeDifference, implementation.GetFirstLocation());
            }
            else if (MemberSignatureComparer.ConsideringTupleNamesCreatesDifference(definition, implementation))
            {
                hasTypeDifferences = true;
                diagnostics.Add(ErrorCode.ERR_PartialMemberInconsistentTupleNames, implementation.GetFirstLocation(), definition, implementation);
            }
 
            if (definition.RefKind != implementation.RefKind)
            {
                diagnostics.Add(ErrorCode.ERR_PartialMemberRefReturnDifference, implementation.GetFirstLocation());
            }
 
            if (definition.IsStatic != implementation.IsStatic)
            {
                diagnostics.Add(ErrorCode.ERR_PartialMemberStaticDifference, implementation.GetFirstLocation());
            }
 
            if (definition.IsDeclaredReadOnly != implementation.IsDeclaredReadOnly)
            {
                diagnostics.Add(ErrorCode.ERR_PartialMemberReadOnlyDifference, implementation.GetFirstLocation());
            }
 
            if (definition.IsExtensionMethod != implementation.IsExtensionMethod)
            {
                diagnostics.Add(ErrorCode.ERR_PartialMethodExtensionDifference, implementation.GetFirstLocation());
            }
 
            if (definition.IsUnsafe != implementation.IsUnsafe && definition.CompilationAllowsUnsafe()) // Don't cascade.
            {
                diagnostics.Add(ErrorCode.ERR_PartialMemberUnsafeDifference, implementation.GetFirstLocation());
            }
 
            if (definition.IsParams() != implementation.IsParams())
            {
                diagnostics.Add(ErrorCode.ERR_PartialMemberParamsDifference, implementation.GetFirstLocation());
            }
 
            if (definition.HasExplicitAccessModifier != implementation.HasExplicitAccessModifier
                || definition.DeclaredAccessibility != implementation.DeclaredAccessibility)
            {
                diagnostics.Add(ErrorCode.ERR_PartialMemberAccessibilityDifference, implementation.GetFirstLocation());
            }
 
            if (definition.IsVirtual != implementation.IsVirtual
                || definition.IsOverride != implementation.IsOverride
                || definition.IsSealed != implementation.IsSealed
                || definition.IsNew != implementation.IsNew)
            {
                diagnostics.Add(ErrorCode.ERR_PartialMemberExtendedModDifference, implementation.GetFirstLocation());
            }
 
            PartialMethodConstraintsChecks(definition, implementation, diagnostics);
 
            if (SourceMemberContainerTypeSymbol.CheckValidScopedOverride(
                constructedDefinition,
                implementation,
                diagnostics,
                static (diagnostics, implementedMethod, implementingMethod, implementingParameter, blameAttributes, arg) =>
                {
                    diagnostics.Add(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, implementingMethod.GetFirstLocation(), new FormattedSymbol(implementingParameter, SymbolDisplayFormat.ShortFormat));
                },
                extraArgument: (object)null,
                allowVariance: false,
                invokedAsExtensionMethod: false))
            {
                hasTypeDifferences = true;
            }
 
            if (SourceMemberContainerTypeSymbol.CheckValidNullableMethodOverride(
                implementation.DeclaringCompilation,
                constructedDefinition,
                implementation,
                diagnostics,
                static (diagnostics, implementedMethod, implementingMethod, topLevel, arg) =>
                {
                    // report only if this is an unsafe *nullability* difference
                    diagnostics.Add(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnPartial, implementingMethod.GetFirstLocation());
                },
                static (diagnostics, implementedMethod, implementingMethod, implementingParameter, blameAttributes, arg) =>
                {
                    diagnostics.Add(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnPartial, implementingMethod.GetFirstLocation(), new FormattedSymbol(implementingParameter, SymbolDisplayFormat.ShortFormat));
                },
                extraArgument: (object)null))
            {
                hasTypeDifferences = true;
            }
 
            if ((!hasTypeDifferences && !MemberSignatureComparer.PartialMethodsStrictComparer.Equals(definition, implementation)) ||
                hasDifferencesInParameterOrTypeParameterName(definition, implementation))
            {
                diagnostics.Add(ErrorCode.WRN_PartialMethodTypeDifference, implementation.GetFirstLocation(),
                    new FormattedSymbol(definition, SymbolDisplayFormat.MinimallyQualifiedFormat),
                    new FormattedSymbol(implementation, SymbolDisplayFormat.MinimallyQualifiedFormat));
            }
 
            static bool hasDifferencesInParameterOrTypeParameterName(SourceOrdinaryMethodSymbol definition, SourceOrdinaryMethodSymbol implementation)
            {
                return !definition.Parameters.SequenceEqual(implementation.Parameters, (a, b) => a.Name == b.Name) ||
                    !definition.TypeParameters.SequenceEqual(implementation.TypeParameters, (a, b) => a.Name == b.Name);
            }
        }
 
        private static void PartialMethodConstraintsChecks(SourceOrdinaryMethodSymbol definition, SourceOrdinaryMethodSymbol implementation, BindingDiagnosticBag diagnostics)
        {
            Debug.Assert(!ReferenceEquals(definition, implementation));
            Debug.Assert(definition.Arity == implementation.Arity);
 
            var typeParameters1 = definition.TypeParameters;
 
            int arity = typeParameters1.Length;
            if (arity == 0)
            {
                return;
            }
 
            var typeParameters2 = implementation.TypeParameters;
            var indexedTypeParameters = IndexedTypeParameterSymbol.Take(arity);
            var typeMap1 = new TypeMap(typeParameters1, indexedTypeParameters, allowAlpha: true);
            var typeMap2 = new TypeMap(typeParameters2, indexedTypeParameters, allowAlpha: true);
 
            // Report any mismatched method constraints.
            for (int i = 0; i < arity; i++)
            {
                var typeParameter1 = typeParameters1[i];
                var typeParameter2 = typeParameters2[i];
 
                if (!MemberSignatureComparer.HaveSameConstraints(typeParameter1, typeMap1, typeParameter2, typeMap2))
                {
                    diagnostics.Add(ErrorCode.ERR_PartialMethodInconsistentConstraints, implementation.GetFirstLocation(), implementation, typeParameter2.Name);
                }
                else if (!MemberSignatureComparer.HaveSameNullabilityInConstraints(typeParameter1, typeMap1, typeParameter2, typeMap2))
                {
                    diagnostics.Add(ErrorCode.WRN_NullabilityMismatchInConstraintsOnPartialImplementation, implementation.GetFirstLocation(), implementation, typeParameter2.Name);
                }
            }
        }
 
        internal sealed override bool CallsAreOmitted(SyntaxTree syntaxTree)
        {
            if (this.IsPartialWithoutImplementation)
            {
                return true;
            }
 
            return base.CallsAreOmitted(syntaxTree);
        }
 
        internal sealed override bool GenerateDebugInfo => !IsAsync && !IsIterator;
 
#nullable enable
        protected override void MethodChecks(BindingDiagnosticBag diagnostics)
        {
            Debug.Assert(this.MethodKind != MethodKind.UserDefinedOperator, "SourceUserDefinedOperatorSymbolBase overrides this");
 
            var (returnType, parameters, declaredConstraints) = MakeParametersAndBindReturnType(diagnostics);
 
            MethodSymbol? overriddenOrExplicitlyImplementedMethod = MethodChecks(returnType, parameters, diagnostics);
 
            if (!declaredConstraints.IsDefault && overriddenOrExplicitlyImplementedMethod is object)
            {
                for (int i = 0; i < declaredConstraints.Length; i++)
                {
                    var typeParameter = this.TypeParameters[i];
                    ErrorCode report;
 
                    switch (declaredConstraints[i].Constraints & (TypeParameterConstraintKind.ReferenceType | TypeParameterConstraintKind.ValueType | TypeParameterConstraintKind.Default))
                    {
                        case TypeParameterConstraintKind.ReferenceType:
                            if (!typeParameter.IsReferenceType)
                            {
                                report = ErrorCode.ERR_OverrideRefConstraintNotSatisfied;
                                break;
                            }
                            continue;
                        case TypeParameterConstraintKind.ValueType:
                            if (!typeParameter.IsNonNullableValueType())
                            {
                                report = ErrorCode.ERR_OverrideValConstraintNotSatisfied;
                                break;
                            }
                            continue;
                        case TypeParameterConstraintKind.Default:
                            if (typeParameter.IsReferenceType || typeParameter.IsValueType)
                            {
                                report = ErrorCode.ERR_OverrideDefaultConstraintNotSatisfied;
                                break;
                            }
                            continue;
                        default:
                            continue;
                    }
 
                    diagnostics.Add(report, typeParameter.GetFirstLocation(), this, typeParameter,
                                    overriddenOrExplicitlyImplementedMethod.TypeParameters[i], overriddenOrExplicitlyImplementedMethod);
                }
            }
 
            CheckModifiers(MethodKind == MethodKind.ExplicitInterfaceImplementation, _location, diagnostics);
        }
#nullable disable
 
        // Consider moving this to flags to save space
        internal bool HasExplicitAccessModifier { get; }
 
        private static (DeclarationModifiers mods, bool hasExplicitAccessMod) MakeModifiers(MethodDeclarationSyntax syntax, NamedTypeSymbol containingType, MethodKind methodKind, bool hasBody, Location location, BindingDiagnosticBag diagnostics)
        {
            bool isInterface = containingType.IsInterface;
            bool isExplicitInterfaceImplementation = methodKind == MethodKind.ExplicitInterfaceImplementation;
 
            // This is needed to make sure we can detect 'public' modifier specified explicitly and
            // check it against language version below.
            var defaultAccess = isInterface && !isExplicitInterfaceImplementation ? DeclarationModifiers.None : DeclarationModifiers.Private;
 
            // Check that the set of modifiers is allowed
            var allowedModifiers = DeclarationModifiers.Partial | DeclarationModifiers.Unsafe;
            var defaultInterfaceImplementationModifiers = DeclarationModifiers.None;
 
            if (!isExplicitInterfaceImplementation)
            {
                allowedModifiers |= DeclarationModifiers.New |
                                    DeclarationModifiers.Sealed |
                                    DeclarationModifiers.Abstract |
                                    DeclarationModifiers.Static |
                                    DeclarationModifiers.Virtual |
                                    DeclarationModifiers.AccessibilityMask;
 
                if (!isInterface)
                {
                    allowedModifiers |= DeclarationModifiers.Override;
                }
                else
                {
                    defaultInterfaceImplementationModifiers |= DeclarationModifiers.Sealed |
                                                               DeclarationModifiers.Abstract |
                                                               DeclarationModifiers.Static |
                                                               DeclarationModifiers.Virtual |
                                                               DeclarationModifiers.Extern |
                                                               DeclarationModifiers.Async |
                                                               DeclarationModifiers.Partial |
                                                               DeclarationModifiers.AccessibilityMask;
                }
            }
            else
            {
                Debug.Assert(isExplicitInterfaceImplementation);
 
                if (isInterface)
                {
                    allowedModifiers |= DeclarationModifiers.Abstract;
                }
 
                allowedModifiers |= DeclarationModifiers.Static;
            }
 
            allowedModifiers |= DeclarationModifiers.Extern | DeclarationModifiers.Async;
 
            if (containingType.IsStructType())
            {
                allowedModifiers |= DeclarationModifiers.ReadOnly;
            }
 
            // In order to detect whether explicit accessibility mods were provided, we pass the default value
            // for 'defaultAccess' and manually add in the 'defaultAccess' flags after the call.
            bool hasExplicitAccessMod;
            DeclarationModifiers mods = MakeDeclarationModifiers(syntax, containingType, location, allowedModifiers, diagnostics);
            if ((mods & DeclarationModifiers.AccessibilityMask) == 0)
            {
                hasExplicitAccessMod = false;
                mods |= defaultAccess;
            }
            else
            {
                hasExplicitAccessMod = true;
            }
 
            ModifierUtils.CheckFeatureAvailabilityForStaticAbstractMembersInInterfacesIfNeeded(mods, isExplicitInterfaceImplementation, location, diagnostics);
 
            ModifierUtils.ReportDefaultInterfaceImplementationModifiers(hasBody, mods,
                                                                        defaultInterfaceImplementationModifiers,
                                                                        location, diagnostics);
 
            mods = AddImpliedModifiers(mods, isInterface, methodKind, hasBody);
            return (mods, hasExplicitAccessMod);
        }
 
        private static DeclarationModifiers AddImpliedModifiers(DeclarationModifiers mods, bool containingTypeIsInterface, MethodKind methodKind, bool hasBody)
        {
            // Let's overwrite modifiers for interface and explicit interface implementation methods with what they are supposed to be. 
            // Proper errors must have been reported by now.
            if (containingTypeIsInterface)
            {
                mods = ModifierUtils.AdjustModifiersForAnInterfaceMember(mods, hasBody,
                                                                         methodKind == MethodKind.ExplicitInterfaceImplementation);
            }
            else if (methodKind == MethodKind.ExplicitInterfaceImplementation)
            {
                mods = (mods & ~DeclarationModifiers.AccessibilityMask) | DeclarationModifiers.Private;
            }
            return mods;
        }
 
        private const DeclarationModifiers PartialMethodExtendedModifierMask =
            DeclarationModifiers.Virtual |
            DeclarationModifiers.Override |
            DeclarationModifiers.New |
            DeclarationModifiers.Sealed |
            DeclarationModifiers.Extern;
 
        private bool HasExtendedPartialModifier => (DeclarationModifiers & PartialMethodExtendedModifierMask) != 0;
 
        private void CheckModifiers(bool isExplicitInterfaceImplementation, Location location, BindingDiagnosticBag diagnostics)
        {
            bool isVararg = this.IsVararg;
 
            Debug.Assert(!IsStatic || !IsOverride); // Otherwise should have been reported and cleared earlier.
            Debug.Assert(!IsStatic || ContainingType.IsInterface || (!IsAbstract && !IsVirtual)); // Otherwise should have been reported and cleared earlier.
 
            bool isExplicitInterfaceImplementationInInterface = isExplicitInterfaceImplementation && ContainingType.IsInterface;
 
            if (IsPartial && HasExplicitAccessModifier)
            {
                Binder.CheckFeatureAvailability(SyntaxNode, MessageID.IDS_FeatureExtendedPartialMethods, diagnostics, location);
            }
 
            if (IsPartial && IsAbstract)
            {
                diagnostics.Add(ErrorCode.ERR_PartialMemberCannotBeAbstract, location);
            }
            else if (IsPartial && !HasExplicitAccessModifier && !ReturnsVoid)
            {
                diagnostics.Add(ErrorCode.ERR_PartialMethodWithNonVoidReturnMustHaveAccessMods, location, this);
            }
            else if (IsPartial && !HasExplicitAccessModifier && HasExtendedPartialModifier)
            {
                diagnostics.Add(ErrorCode.ERR_PartialMethodWithExtendedModMustHaveAccessMods, location, this);
            }
            else if (IsPartial && !HasExplicitAccessModifier && Parameters.Any(static p => p.RefKind == RefKind.Out))
            {
                diagnostics.Add(ErrorCode.ERR_PartialMethodWithOutParamMustHaveAccessMods, location, this);
            }
            else if (this.DeclaredAccessibility == Accessibility.Private && (IsVirtual || (IsAbstract && !isExplicitInterfaceImplementationInInterface) || IsOverride))
            {
                diagnostics.Add(ErrorCode.ERR_VirtualPrivate, location, this);
            }
            else if (IsOverride && (IsNew || IsVirtual))
            {
                // A member '{0}' marked as override cannot be marked as new or virtual
                diagnostics.Add(ErrorCode.ERR_OverrideNotNew, location, this);
            }
            else if (IsSealed && !IsOverride && !(isExplicitInterfaceImplementationInInterface && IsAbstract))
            {
                // '{0}' cannot be sealed because it is not an override
                diagnostics.Add(ErrorCode.ERR_SealedNonOverride, location, this);
            }
            else if (IsSealed && ContainingType.TypeKind == TypeKind.Struct)
            {
                // The modifier '{0}' is not valid for this item
                diagnostics.Add(ErrorCode.ERR_BadMemberFlag, location, SyntaxFacts.GetText(SyntaxKind.SealedKeyword));
            }
            else if (ReturnType.IsStatic)
            {
                // '{0}': static types cannot be used as return types
                diagnostics.Add(ErrorFacts.GetStaticClassReturnCode(ContainingType.IsInterfaceType()), location, ReturnType);
            }
            else if (IsAbstract && IsExtern)
            {
                diagnostics.Add(ErrorCode.ERR_AbstractAndExtern, location, this);
            }
            else if (IsAbstract && IsSealed && !isExplicitInterfaceImplementationInInterface)
            {
                diagnostics.Add(ErrorCode.ERR_AbstractAndSealed, location, this);
            }
            else if (IsAbstract && IsVirtual)
            {
                diagnostics.Add(ErrorCode.ERR_AbstractNotVirtual, location, this.Kind.Localize(), this);
            }
            else if (IsAbstract && ContainingType.TypeKind == TypeKind.Struct)
            {
                // The modifier '{0}' is not valid for this item
                diagnostics.Add(ErrorCode.ERR_BadMemberFlag, location, SyntaxFacts.GetText(SyntaxKind.AbstractKeyword));
            }
            else if (IsVirtual && ContainingType.TypeKind == TypeKind.Struct)
            {
                // The modifier '{0}' is not valid for this item
                diagnostics.Add(ErrorCode.ERR_BadMemberFlag, location, SyntaxFacts.GetText(SyntaxKind.VirtualKeyword));
            }
            else if (IsStatic && IsDeclaredReadOnly)
            {
                // Static member '{0}' cannot be marked 'readonly'.
                diagnostics.Add(ErrorCode.ERR_StaticMemberCantBeReadOnly, location, this);
            }
            else if (IsAbstract && !ContainingType.IsAbstract && (ContainingType.TypeKind == TypeKind.Class || ContainingType.TypeKind == TypeKind.Submission))
            {
                // '{0}' is abstract but it is contained in non-abstract type '{1}'
                diagnostics.Add(ErrorCode.ERR_AbstractInConcreteClass, location, this, ContainingType);
            }
            else if (IsVirtual && ContainingType.IsSealed)
            {
                // '{0}' is a new virtual member in sealed type '{1}'
                diagnostics.Add(ErrorCode.ERR_NewVirtualInSealed, location, this, ContainingType);
            }
            else if (!HasAnyBody && IsAsync)
            {
                diagnostics.Add(ErrorCode.ERR_BadAsyncLacksBody, location);
            }
            else if (!HasAnyBody && !IsExtern && !IsAbstract && !IsPartial && !IsExpressionBodied)
            {
                diagnostics.Add(ErrorCode.ERR_ConcreteMissingBody, location, this);
            }
            else if (ContainingType.IsSealed && this.DeclaredAccessibility.HasProtected() && !this.IsOverride)
            {
                diagnostics.Add(AccessCheck.GetProtectedMemberInSealedTypeError(ContainingType), location, this);
            }
            else if (ContainingType.IsStatic && !IsStatic)
            {
                diagnostics.Add(ErrorCode.ERR_InstanceMemberInStaticClass, location, Name);
            }
            else if (isVararg && (IsGenericMethod || ContainingType.IsGenericType || Parameters.Length > 0 && Parameters[Parameters.Length - 1].IsParams))
            {
                diagnostics.Add(ErrorCode.ERR_BadVarargs, location);
            }
            else if (isVararg && IsAsync)
            {
                diagnostics.Add(ErrorCode.ERR_VarargsAsync, location);
            }
        }
 
        private sealed class SourceOrdinaryMethodSymbolSimple : SourceOrdinaryMethodSymbol
        {
            // Avoid adding fields here if possible.  This 'simple' type handles the majority of source methods in any
            // compilation.  So any fields here can add significantly to heap usage.  In measurements, there are roughly
            // 25:1 more 'simple' methods than 'complex' methods. So consider placing new data in
            // SourceOrdinaryMethodSymbolComplex instead if it is state for rare methods.
 
            public SourceOrdinaryMethodSymbolSimple(
                NamedTypeSymbol containingType,
                string name,
                Location location,
                MethodDeclarationSyntax syntax,
                MethodKind methodKind,
                bool isNullableAnalysisEnabled,
                BindingDiagnosticBag diagnostics)
                : base(containingType, name, location, syntax, methodKind, isNullableAnalysisEnabled, diagnostics)
            {
            }
 
            internal sealed override SourceOrdinaryMethodSymbol OtherPartOfPartial
                => null;
 
            protected sealed override TypeSymbol ExplicitInterfaceType
                => null;
 
            protected sealed override MethodSymbol FindExplicitlyImplementedMethod(BindingDiagnosticBag diagnostics)
                => null;
 
            public sealed override ImmutableArray<TypeParameterSymbol> TypeParameters
                => ImmutableArray<TypeParameterSymbol>.Empty;
 
            public sealed override ImmutableArray<ImmutableArray<TypeWithAnnotations>> GetTypeParameterConstraintTypes()
                => ImmutableArray<ImmutableArray<TypeWithAnnotations>>.Empty;
 
            public sealed override ImmutableArray<TypeParameterConstraintKind> GetTypeParameterConstraintKinds()
                => ImmutableArray<TypeParameterConstraintKind>.Empty;
 
            protected sealed override void CheckConstraintsForExplicitInterfaceType(ConversionsBase conversions, BindingDiagnosticBag diagnostics)
            {
            }
        }
 
        /// <summary>
        /// Specialized subclass of SourceOrdinaryMethodSymbol for less common cases.  Specifically, we only use this
        /// for methods that are:
        /// <list type="number">
        /// <item>Generic.</item>
        /// <item>An explicit interface implementation.</item>
        /// <item>Partial.</item>
        /// </list>
        /// </summary>
        private sealed class SourceOrdinaryMethodSymbolComplex : SourceOrdinaryMethodSymbol
        {
            private readonly TypeSymbol _explicitInterfaceType;
 
            private readonly TypeParameterInfo _typeParameterInfo;
 
            /// <summary>
            /// If this symbol represents a partial method definition or implementation part, its other part (if any).
            /// This should be set, if at all, before this symbol appears among the members of its owner.  
            /// The implementation part is not listed among the "members" of the enclosing type.
            /// </summary>
            private SourceOrdinaryMethodSymbol _otherPartOfPartial;
 
            public SourceOrdinaryMethodSymbolComplex(
                NamedTypeSymbol containingType,
                TypeSymbol explicitInterfaceType,
                string name,
                Location location,
                MethodDeclarationSyntax syntax,
                MethodKind methodKind,
                bool isNullableAnalysisEnabled,
                BindingDiagnosticBag diagnostics)
                : base(containingType, name, location, syntax, methodKind, isNullableAnalysisEnabled, diagnostics)
            {
                _explicitInterfaceType = explicitInterfaceType;
 
                // Compute the type parameters.  If empty (the common case), directly point at the singleton to reduce
                // the amount of pointers-to-arrays this type needs to store.
                var typeParameters = MakeTypeParameters(syntax, diagnostics);
                _typeParameterInfo = typeParameters.IsEmpty
                    ? TypeParameterInfo.Empty
                    : new TypeParameterInfo { LazyTypeParameters = typeParameters };
            }
 
            protected sealed override TypeSymbol ExplicitInterfaceType => _explicitInterfaceType;
            internal sealed override SourceOrdinaryMethodSymbol OtherPartOfPartial => _otherPartOfPartial;
 
            internal static void InitializePartialMethodParts(SourceOrdinaryMethodSymbolComplex definition, SourceOrdinaryMethodSymbolComplex implementation)
            {
                Debug.Assert(definition.IsPartialDefinition);
                Debug.Assert(implementation.IsPartialImplementation);
 
                Debug.Assert(definition._otherPartOfPartial is not { } alreadySetImplPart || alreadySetImplPart == implementation);
                Debug.Assert(implementation._otherPartOfPartial is not { } alreadySetDefPart || alreadySetDefPart == definition);
 
                definition._otherPartOfPartial = implementation;
                implementation._otherPartOfPartial = definition;
 
                Debug.Assert(definition._otherPartOfPartial == implementation);
                Debug.Assert(implementation._otherPartOfPartial == definition);
            }
 
            protected sealed override MethodSymbol FindExplicitlyImplementedMethod(BindingDiagnosticBag diagnostics)
            {
                var syntax = GetSyntax();
                return this.FindExplicitlyImplementedMethod(isOperator: false, _explicitInterfaceType, syntax.Identifier.ValueText, syntax.ExplicitInterfaceSpecifier, diagnostics);
            }
 
            public sealed override ImmutableArray<TypeParameterSymbol> TypeParameters
            {
                get
                {
                    Debug.Assert(!_typeParameterInfo.LazyTypeParameters.IsDefault);
                    return _typeParameterInfo.LazyTypeParameters;
                }
            }
 
            public sealed override ImmutableArray<ImmutableArray<TypeWithAnnotations>> GetTypeParameterConstraintTypes()
            {
                if (_typeParameterInfo.LazyTypeParameterConstraintTypes.IsDefault)
                {
                    GetTypeParameterConstraintKinds();
 
                    var diagnostics = BindingDiagnosticBag.GetInstance();
                    var syntax = GetSyntax();
                    var withTypeParametersBinder =
                        this.DeclaringCompilation
                        .GetBinderFactory(syntax.SyntaxTree)
                        .GetBinder(syntax.ReturnType, syntax, this);
                    var constraints = this.MakeTypeParameterConstraintTypes(
                        withTypeParametersBinder,
                        TypeParameters,
                        syntax.TypeParameterList,
                        syntax.ConstraintClauses,
                        diagnostics);
                    if (ImmutableInterlocked.InterlockedInitialize(
                            ref _typeParameterInfo.LazyTypeParameterConstraintTypes,
                            constraints))
                    {
                        this.AddDeclarationDiagnostics(diagnostics);
                    }
                    diagnostics.Free();
                }
 
                return _typeParameterInfo.LazyTypeParameterConstraintTypes;
            }
 
            public sealed override ImmutableArray<TypeParameterConstraintKind> GetTypeParameterConstraintKinds()
            {
                if (_typeParameterInfo.LazyTypeParameterConstraintKinds.IsDefault)
                {
                    var syntax = GetSyntax();
                    var withTypeParametersBinder =
                        this.DeclaringCompilation
                        .GetBinderFactory(syntax.SyntaxTree)
                        .GetBinder(syntax.ReturnType, syntax, this);
                    var constraints = this.MakeTypeParameterConstraintKinds(
                        withTypeParametersBinder,
                        TypeParameters,
                        syntax.TypeParameterList,
                        syntax.ConstraintClauses);
 
                    ImmutableInterlocked.InterlockedInitialize(
                        ref _typeParameterInfo.LazyTypeParameterConstraintKinds,
                        constraints);
                }
 
                return _typeParameterInfo.LazyTypeParameterConstraintKinds;
            }
 
            protected sealed override void CheckConstraintsForExplicitInterfaceType(ConversionsBase conversions, BindingDiagnosticBag diagnostics)
            {
                if ((object)_explicitInterfaceType != null)
                {
                    var syntax = this.GetSyntax();
                    Debug.Assert(syntax.ExplicitInterfaceSpecifier != null);
                    _explicitInterfaceType.CheckAllConstraints(DeclaringCompilation, conversions, new SourceLocation(syntax.ExplicitInterfaceSpecifier.Name), diagnostics);
                }
            }
 
            private ImmutableArray<TypeParameterSymbol> MakeTypeParameters(MethodDeclarationSyntax syntax, BindingDiagnosticBag diagnostics)
            {
                if (syntax.Arity == 0)
                {
                    return ImmutableArray<TypeParameterSymbol>.Empty;
                }
 
                Debug.Assert(syntax.TypeParameterList != null);
 
                MessageID.IDS_FeatureGenerics.CheckFeatureAvailability(diagnostics, syntax.TypeParameterList.LessThanToken);
 
                OverriddenMethodTypeParameterMapBase typeMap = null;
                if (this.IsOverride)
                {
                    typeMap = new OverriddenMethodTypeParameterMap(this);
                }
                else if (this.IsExplicitInterfaceImplementation)
                {
                    typeMap = new ExplicitInterfaceMethodTypeParameterMap(this);
                }
 
                var typeParameters = syntax.TypeParameterList.Parameters;
                var result = ArrayBuilder<TypeParameterSymbol>.GetInstance();
 
                for (int ordinal = 0; ordinal < typeParameters.Count; ordinal++)
                {
                    var parameter = typeParameters[ordinal];
                    if (parameter.VarianceKeyword.Kind() != SyntaxKind.None)
                    {
                        diagnostics.Add(ErrorCode.ERR_IllegalVarianceSyntax, parameter.VarianceKeyword.GetLocation());
                    }
 
                    var identifier = parameter.Identifier;
                    var location = identifier.GetLocation();
                    var name = identifier.ValueText;
 
                    // Note: It is not an error to have a type parameter named the same as its enclosing method: void M<M>() {}
 
                    for (int i = 0; i < result.Count; i++)
                    {
                        if (name == result[i].Name)
                        {
                            diagnostics.Add(ErrorCode.ERR_DuplicateTypeParameter, location, name);
                            break;
                        }
                    }
 
                    SourceMemberContainerTypeSymbol.ReportReservedTypeName(identifier.Text, this.DeclaringCompilation, diagnostics.DiagnosticBag, location);
 
                    var tpEnclosing = ContainingType.FindEnclosingTypeParameter(name);
                    if ((object)tpEnclosing != null)
                    {
                        // Type parameter '{0}' has the same name as the type parameter from outer type '{1}'
                        diagnostics.Add(ErrorCode.WRN_TypeParameterSameAsOuterTypeParameter, location, name, tpEnclosing.ContainingType);
                    }
 
                    var syntaxRefs = ImmutableArray.Create(parameter.GetReference());
                    var locations = ImmutableArray.Create(location);
                    var typeParameter = (typeMap != null) ?
                        (TypeParameterSymbol)new SourceOverridingMethodTypeParameterSymbol(
                            typeMap,
                            name,
                            ordinal,
                            locations,
                            syntaxRefs) :
                        new SourceMethodTypeParameterSymbol(
                            this,
                            name,
                            ordinal,
                            locations,
                            syntaxRefs);
 
                    result.Add(typeParameter);
                }
 
                return result.ToImmutableAndFree();
            }
        }
    }
}