File: Symbols\Source\SourceMemberMethodSymbol.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.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Globalization;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Emit;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
    internal abstract class SourceMemberMethodSymbol : LocalFunctionOrSourceMemberMethodSymbol, IAttributeTargetSymbol
    {
        // The flags type is used to compact many different bits of information.
        protected struct Flags
        {
            // We currently pack everything into a 32 bit int with the following layout:
            //
            // |            |t|a|b|e|n|vvv|yy|s|r|q|z|kkk|wwwww|
            // 
            // w = method kind.  5 bits.
            // k = ref kind.  3 bits.
            // z = isExtensionMethod. 1 bit.
            // q = isMetadataVirtualIgnoringInterfaceChanges. 1 bit.
            // r = isMetadataVirtual. 1 bit. (At least as true as isMetadataVirtualIgnoringInterfaceChanges.)
            // s = isMetadataVirtualLocked. 1 bit.
            // y = ReturnsVoid. 2 bits.
            // v = NullableContext. 3 bits.
            // n = IsNullableAnalysisEnabled. 1 bit.
            // e = IsExpressionBody. 1 bit.
            // b = HasAnyBody. 1 bit.
            // a = IsVararg. 1 bit.
            // t = HasThisInitializer. 1 bit.
            private int _flags;
 
            private const int MethodKindOffset = 0;
            private const int MethodKindSize = 5;
            private const int MethodKindMask = (1 << MethodKindSize) - 1;
 
            private const int RefKindOffset = MethodKindOffset + MethodKindSize;
            private const int RefKindSize = 3;
            private const int RefKindMask = (1 << RefKindSize) - 1;
 
            private const int IsExtensionMethodOffset = RefKindOffset + RefKindSize;
            private const int IsExtensionMethodSize = 1;
 
            private const int IsMetadataVirtualIgnoringInterfaceChangesOffset = IsExtensionMethodOffset + IsExtensionMethodSize;
            private const int IsMetadataVirtualIgnoringInterfaceChangesSize = 1;
 
            private const int IsMetadataVirtualOffset = IsMetadataVirtualIgnoringInterfaceChangesOffset + IsMetadataVirtualIgnoringInterfaceChangesSize;
            private const int IsMetadataVirtualSize = 1;
 
            private const int IsMetadataVirtualLockedOffset = IsMetadataVirtualOffset + IsMetadataVirtualSize;
            private const int IsMetadataVirtualLockedSize = 1;
 
            private const int ReturnsVoidOffset = IsMetadataVirtualLockedOffset + IsMetadataVirtualLockedSize;
            private const int ReturnsVoidSize = 2;
 
            private const int NullableContextOffset = ReturnsVoidOffset + ReturnsVoidSize;
            private const int NullableContextSize = 3;
            private const int NullableContextMask = (1 << NullableContextSize) - 1;
 
            private const int IsNullableAnalysisEnabledOffset = NullableContextOffset + NullableContextSize;
            private const int IsNullableAnalysisEnabledSize = 1;
 
            private const int IsExpressionBodiedOffset = IsNullableAnalysisEnabledOffset + IsNullableAnalysisEnabledSize;
            private const int IsExpressionBodiedSize = 1;
 
            private const int HasAnyBodyOffset = IsExpressionBodiedOffset + IsExpressionBodiedSize;
            private const int HasAnyBodySize = 1;
 
            private const int IsVarargOffset = HasAnyBodyOffset + HasAnyBodySize;
            private const int IsVarargSize = 1;
 
            private const int HasThisInitializerOffset = IsVarargOffset + IsVarargSize;
#pragma warning disable IDE0051 // Remove unused private members
            private const int HasThisInitializerSize = 1;
#pragma warning restore IDE0051 // Remove unused private members
 
            private const int HasAnyBodyBit = 1 << HasAnyBodyOffset;
            private const int IsExpressionBodiedBit = 1 << IsExpressionBodiedOffset;
            private const int IsExtensionMethodBit = 1 << IsExtensionMethodOffset;
            private const int IsMetadataVirtualIgnoringInterfaceChangesBit = 1 << IsMetadataVirtualIgnoringInterfaceChangesOffset;
            private const int IsMetadataVirtualBit = 1 << IsMetadataVirtualIgnoringInterfaceChangesOffset;
            private const int IsMetadataVirtualLockedBit = 1 << IsMetadataVirtualLockedOffset;
            private const int IsVarargBit = 1 << IsVarargOffset;
            private const int HasThisInitializerBit = 1 << HasThisInitializerOffset;
 
            private const int ReturnsVoidBit = 1 << ReturnsVoidOffset;
            private const int ReturnsVoidIsSetBit = 1 << ReturnsVoidOffset + 1;
 
            private const int IsNullableAnalysisEnabledBit = 1 << IsNullableAnalysisEnabledOffset;
 
            public bool ReturnsVoid
            {
                get
                {
                    int bits = _flags;
                    var value = (bits & ReturnsVoidBit) != 0;
                    Debug.Assert((bits & ReturnsVoidIsSetBit) != 0);
                    return value;
                }
            }
 
            public void SetReturnsVoid(bool value)
            {
                int bits = _flags;
                Debug.Assert((bits & ReturnsVoidIsSetBit) == 0);
                Debug.Assert(value || (bits & ReturnsVoidBit) == 0);
                ThreadSafeFlagOperations.Set(ref _flags, ReturnsVoidIsSetBit | (value ? ReturnsVoidBit : 0));
            }
 
            public MethodKind MethodKind
            {
                get { return (MethodKind)((_flags >> MethodKindOffset) & MethodKindMask); }
            }
 
            public RefKind RefKind
            {
                get { return (RefKind)((_flags >> RefKindOffset) & RefKindMask); }
            }
 
            public bool HasAnyBody
            {
                get { return (_flags & HasAnyBodyBit) != 0; }
            }
 
            public bool IsExpressionBodied
            {
                get { return (_flags & IsExpressionBodiedBit) != 0; }
            }
 
            public bool IsExtensionMethod
            {
                get { return (_flags & IsExtensionMethodBit) != 0; }
            }
 
            public bool IsNullableAnalysisEnabled
            {
                get { return (_flags & IsNullableAnalysisEnabledBit) != 0; }
            }
 
            public bool IsMetadataVirtualLocked
            {
                get { return (_flags & IsMetadataVirtualLockedBit) != 0; }
            }
 
            public bool IsVararg
            {
                get { return (_flags & IsVarargBit) != 0; }
            }
 
            public readonly bool HasThisInitializer
                => (_flags & HasThisInitializerBit) != 0;
 
#if DEBUG
            static Flags()
            {
                // Verify masks are sufficient for values.
                Debug.Assert(EnumUtilities.ContainsAllValues<MethodKind>(MethodKindMask));
                Debug.Assert(EnumUtilities.ContainsAllValues<RefKind>(RefKindMask));
                Debug.Assert(EnumUtilities.ContainsAllValues<NullableContextKind>(NullableContextMask));
            }
#endif
 
            private static bool ModifiersRequireMetadataVirtual(DeclarationModifiers modifiers)
            {
                return (modifiers & (DeclarationModifiers.Abstract | DeclarationModifiers.Virtual | DeclarationModifiers.Override)) != 0;
            }
 
            public Flags(
                MethodKind methodKind,
                RefKind refKind,
                DeclarationModifiers declarationModifiers,
                bool returnsVoid,
                bool returnsVoidIsSet,
                bool hasAnyBody,
                bool isExpressionBodied,
                bool isExtensionMethod,
                bool isNullableAnalysisEnabled,
                bool isVararg,
                bool isExplicitInterfaceImplementation,
                bool hasThisInitializer)
            {
                Debug.Assert(!returnsVoid || returnsVoidIsSet);
 
                bool isMetadataVirtual = (isExplicitInterfaceImplementation && (declarationModifiers & DeclarationModifiers.Static) == 0) || ModifiersRequireMetadataVirtual(declarationModifiers);
 
                int methodKindInt = ((int)methodKind & MethodKindMask) << MethodKindOffset;
                int refKindInt = ((int)refKind & RefKindMask) << RefKindOffset;
                int hasAnyBodyInt = hasAnyBody ? HasAnyBodyBit : 0;
                int isExpressionBodyInt = isExpressionBodied ? IsExpressionBodiedBit : 0;
                int isExtensionMethodInt = isExtensionMethod ? IsExtensionMethodBit : 0;
                int isNullableAnalysisEnabledInt = isNullableAnalysisEnabled ? IsNullableAnalysisEnabledBit : 0;
                int isVarargInt = isVararg ? IsVarargBit : 0;
                int isMetadataVirtualIgnoringInterfaceImplementationChangesInt = isMetadataVirtual ? IsMetadataVirtualIgnoringInterfaceChangesBit : 0;
                int isMetadataVirtualInt = isMetadataVirtual ? IsMetadataVirtualBit : 0;
                int hasThisInitializerInt = hasThisInitializer ? HasThisInitializerBit : 0;
 
                _flags = methodKindInt
                    | refKindInt
                    | hasAnyBodyInt
                    | isExpressionBodyInt
                    | isExtensionMethodInt
                    | isNullableAnalysisEnabledInt
                    | isVarargInt
                    | isMetadataVirtualIgnoringInterfaceImplementationChangesInt
                    | isMetadataVirtualInt
                    | hasThisInitializerInt
                    | (returnsVoid ? ReturnsVoidBit : 0)
                    | (returnsVoidIsSet ? ReturnsVoidIsSetBit : 0);
            }
 
            public Flags(
                MethodKind methodKind,
                RefKind refKind,
                DeclarationModifiers declarationModifiers,
                bool returnsVoid,
                bool returnsVoidIsSet,
                bool isExpressionBodied,
                bool isExtensionMethod,
                bool isNullableAnalysisEnabled,
                bool isVararg,
                bool isExplicitInterfaceImplementation,
                bool hasThisInitializer)
                : this(methodKind,
                       refKind,
                       declarationModifiers,
                       returnsVoid: returnsVoid,
                       returnsVoidIsSet: returnsVoidIsSet,
                       hasAnyBody: false,
                       isExpressionBodied: isExpressionBodied,
                       isExtensionMethod: isExtensionMethod,
                       isNullableAnalysisEnabled: isNullableAnalysisEnabled,
                       isVararg: isVararg,
                       isExplicitInterfaceImplementation: isExplicitInterfaceImplementation,
                       hasThisInitializer: hasThisInitializer)
            {
            }
 
            public bool IsMetadataVirtual(bool ignoreInterfaceImplementationChanges = false)
            {
                // This flag is immutable, so there's no reason to set a lock bit, as we do below.
                if (ignoreInterfaceImplementationChanges)
                {
                    return (_flags & IsMetadataVirtualIgnoringInterfaceChangesBit) != 0;
                }
 
                if (!IsMetadataVirtualLocked)
                {
                    ThreadSafeFlagOperations.Set(ref _flags, IsMetadataVirtualLockedBit);
                }
 
                return (_flags & IsMetadataVirtualBit) != 0;
            }
 
            public void EnsureMetadataVirtual()
            {
                // ACASEY: This assert is here to check that we're not mutating the value of IsMetadataVirtual after
                // someone has consumed it.  The best practice is to not access IsMetadataVirtual before ForceComplete
                // has been called on all SourceNamedTypeSymbols.  If it is necessary to do so, then you can pass
                // ignoreInterfaceImplementationChanges: true, but you must be conscious that seeing "false" may not
                // reflect the final, emitted modifier.
                Debug.Assert(!IsMetadataVirtualLocked);
                if ((_flags & IsMetadataVirtualBit) == 0)
                {
                    ThreadSafeFlagOperations.Set(ref _flags, IsMetadataVirtualBit);
                }
            }
 
            public bool TryGetNullableContext(out byte? value)
            {
                return ((NullableContextKind)((_flags >> NullableContextOffset) & NullableContextMask)).TryGetByte(out value);
            }
 
            public bool SetNullableContext(byte? value)
            {
                return ThreadSafeFlagOperations.Set(ref _flags, (((int)value.ToNullableContextFlags() & NullableContextMask) << NullableContextOffset));
            }
        }
 
        protected SymbolCompletionState state;
 
        protected readonly DeclarationModifiers DeclarationModifiers;
        protected Flags flags;
 
        private readonly NamedTypeSymbol _containingType;
        private ParameterSymbol _lazyThisParameter;
 
        private OverriddenOrHiddenMembersResult _lazyOverriddenOrHiddenMembers;
 
        protected readonly Location _location;
        protected string lazyDocComment;
        protected string lazyExpandedDocComment;
 
        //null if has never been computed. Initial binding diagnostics
        //are stashed here in service of API usage patterns
        //where method body diagnostics are requested multiple times.
        private ImmutableArray<Diagnostic> _cachedDiagnostics;
        internal ImmutableArray<Diagnostic> Diagnostics
        {
            get { return _cachedDiagnostics; }
        }
 
        internal ImmutableArray<Diagnostic> SetDiagnostics(ImmutableArray<Diagnostic> newSet, out bool diagsWritten)
        {
            //return the diagnostics that were actually saved in the event that there were two threads racing. 
            diagsWritten = ImmutableInterlocked.InterlockedInitialize(ref _cachedDiagnostics, newSet);
            return _cachedDiagnostics;
        }
 
        protected SourceMemberMethodSymbol(
            NamedTypeSymbol containingType,
            SyntaxReference syntaxReferenceOpt,
            Location location,
            bool isIterator,
            (DeclarationModifiers declarationModifiers, Flags flags) modifiersAndFlags)
            : base(syntaxReferenceOpt, isIterator)
        {
            Debug.Assert(containingType is not null);
            Debug.Assert(location is not null);
            Debug.Assert(containingType.DeclaringCompilation is not null);
 
            _containingType = containingType;
            _location = location;
            DeclarationModifiers = modifiersAndFlags.declarationModifiers;
            flags = modifiersAndFlags.flags;
        }
 
        protected void CheckEffectiveAccessibility(TypeWithAnnotations returnType, ImmutableArray<ParameterSymbol> parameters, BindingDiagnosticBag diagnostics)
        {
            if (this.DeclaredAccessibility <= Accessibility.Private || MethodKind == MethodKind.ExplicitInterfaceImplementation)
            {
                return;
            }
 
            ErrorCode code = (this.MethodKind == MethodKind.Conversion || this.MethodKind == MethodKind.UserDefinedOperator) ?
                ErrorCode.ERR_BadVisOpReturn :
                ErrorCode.ERR_BadVisReturnType;
 
            var useSiteInfo = new CompoundUseSiteInfo<AssemblySymbol>(diagnostics, ContainingAssembly);
            if (!this.IsNoMoreVisibleThan(returnType, ref useSiteInfo))
            {
                // Inconsistent accessibility: return type '{1}' is less accessible than method '{0}'
                diagnostics.Add(code, GetFirstLocation(), this, returnType.Type);
            }
 
            code = (this.MethodKind == MethodKind.Conversion || this.MethodKind == MethodKind.UserDefinedOperator) ?
                ErrorCode.ERR_BadVisOpParam :
                ErrorCode.ERR_BadVisParamType;
 
            foreach (var parameter in parameters)
            {
                if (!parameter.TypeWithAnnotations.IsAtLeastAsVisibleAs(this, ref useSiteInfo))
                {
                    // Inconsistent accessibility: parameter type '{1}' is less accessible than method '{0}'
                    diagnostics.Add(code, GetFirstLocation(), this, parameter.Type);
                }
            }
 
            diagnostics.Add(GetFirstLocation(), useSiteInfo);
        }
 
        protected void CheckFileTypeUsage(TypeWithAnnotations returnType, ImmutableArray<ParameterSymbol> parameters, BindingDiagnosticBag diagnostics)
        {
            if (ContainingType.HasFileLocalTypes())
            {
                return;
            }
 
            if (returnType.Type.HasFileLocalTypes())
            {
                diagnostics.Add(ErrorCode.ERR_FileTypeDisallowedInSignature, GetFirstLocation(), returnType.Type, ContainingType);
            }
 
            foreach (var param in parameters)
            {
                if (param.Type.HasFileLocalTypes())
                {
                    diagnostics.Add(ErrorCode.ERR_FileTypeDisallowedInSignature, GetFirstLocation(), param.Type, ContainingType);
                }
            }
        }
 
        protected static Flags MakeFlags(
            MethodKind methodKind,
            RefKind refKind,
            DeclarationModifiers declarationModifiers,
            bool returnsVoid,
            bool returnsVoidIsSet,
            bool isExpressionBodied,
            bool isExtensionMethod,
            bool isNullableAnalysisEnabled,
            bool isVarArg,
            bool isExplicitInterfaceImplementation,
            bool hasThisInitializer)
        {
            return new Flags(methodKind, refKind, declarationModifiers, returnsVoid, returnsVoidIsSet, isExpressionBodied, isExtensionMethod, isNullableAnalysisEnabled, isVarArg, isExplicitInterfaceImplementation, hasThisInitializer);
        }
 
        protected void SetReturnsVoid(bool returnsVoid)
        {
            this.flags.SetReturnsVoid(returnsVoid);
        }
 
        /// <remarks>
        /// Implementers should assume that a lock has been taken on MethodChecksLockObject.
        /// In particular, it should not (generally) be necessary to use CompareExchange to
        /// protect assignments to fields.
        /// </remarks>
        protected abstract void MethodChecks(BindingDiagnosticBag diagnostics);
 
        /// <summary>
        /// We can usually lock on the syntax reference of this method, but it turns
        /// out that some synthesized methods (e.g. field-like event accessors) also
        /// need to do method checks.  This property allows such methods to supply
        /// their own lock objects, so that we don't have to add a new field to every
        /// SourceMethodSymbol.
        /// </summary>
        protected virtual object MethodChecksLockObject
        {
            get { return this.syntaxReferenceOpt; }
        }
 
        protected void LazyMethodChecks()
        {
            if (!state.HasComplete(CompletionPart.FinishMethodChecks))
            {
                // TODO: if this lock ever encloses a potential call to Debugger.NotifyOfCrossThreadDependency,
                // then we should call DebuggerUtilities.CallBeforeAcquiringLock() (see method comment for more
                // details).
 
                object lockObject = MethodChecksLockObject;
                Debug.Assert(lockObject != null);
                lock (lockObject)
                {
                    if (state.NotePartComplete(CompletionPart.StartMethodChecks))
                    {
                        // By setting StartMethodChecks, we've committed to doing the checks and setting
                        // FinishMethodChecks.  So there is no cancellation supported between one and the other.
                        var diagnostics = BindingDiagnosticBag.GetInstance();
                        try
                        {
                            MethodChecks(diagnostics);
                            AddDeclarationDiagnostics(diagnostics);
                        }
                        finally
                        {
                            state.NotePartComplete(CompletionPart.FinishMethodChecks);
                            diagnostics.Free();
                        }
                    }
                    else
                    {
                        // Either (1) this thread is in the process of completing the method,
                        // or (2) some other thread has beat us to the punch and completed the method.
                        // We can distinguish the two cases here by checking for the FinishMethodChecks
                        // part to be complete, which would only occur if another thread completed this
                        // method.
                        //
                        // The other case, in which this thread is in the process of completing the method,
                        // requires that we return here even though the checks are not complete.  That's because
                        // methods are processed by first populating the return type and parameters by binding
                        // the syntax from source.  Those values are visible to the same thread for the purpose
                        // of computing which methods are implemented and overridden.  But then those values
                        // may be rewritten (by the same thread) to copy down custom modifiers.  In order to
                        // allow the same thread to see the return type and parameters from the syntax (though
                        // they do not yet take on their final values), we return here.
 
                        // Due to the fact that LazyMethodChecks is potentially reentrant, we must use a 
                        // reentrant lock to avoid deadlock and cannot assert that at this point method checks
                        // have completed (state.HasComplete(CompletionPart.FinishMethodChecks)).
                    }
                }
            }
        }
 
        protected virtual void LazyAsyncMethodChecks(CancellationToken cancellationToken)
        {
            state.NotePartComplete(CompletionPart.StartAsyncMethodChecks);
            state.NotePartComplete(CompletionPart.FinishAsyncMethodChecks);
        }
 
        public sealed override Symbol ContainingSymbol
        {
            get
            {
                return _containingType;
            }
        }
 
        public override NamedTypeSymbol ContainingType
        {
            get
            {
                return _containingType;
            }
        }
 
        public override Symbol AssociatedSymbol
        {
            get
            {
                return null;
            }
        }
 
        #region Flags
 
        public override bool ReturnsVoid
        {
            get
            {
                return flags.ReturnsVoid;
            }
        }
 
        public sealed override MethodKind MethodKind
        {
            get
            {
                return this.flags.MethodKind;
            }
        }
 
        public override bool IsExtensionMethod
        {
            get
            {
                return this.flags.IsExtensionMethod;
            }
        }
 
        // TODO (tomat): sealed
        internal override bool IsMetadataNewSlot(bool ignoreInterfaceImplementationChanges = false)
        {
            if (IsExplicitInterfaceImplementation && _containingType.IsInterface)
            {
                // All implementations of methods from base interfaces should omit the newslot bit to ensure no new vtable slot is allocated.
                return false;
            }
 
            // If C# and the runtime don't agree on the overridden method,
            // then we will mark the method as newslot and specify the
            // override explicitly (see GetExplicitImplementationOverrides
            // in NamedTypeSymbolAdapter.cs).
            return this.IsOverride ?
                this.RequiresExplicitOverride(out _) :
                !this.IsStatic && this.IsMetadataVirtual(ignoreInterfaceImplementationChanges ? IsMetadataVirtualOption.IgnoreInterfaceImplementationChanges : IsMetadataVirtualOption.None);
        }
 
        // TODO (tomat): sealed?
        internal override bool IsMetadataVirtual(IsMetadataVirtualOption option = IsMetadataVirtualOption.None)
        {
#if DEBUG
            if (option == IsMetadataVirtualOption.ForceCompleteIfNeeded && !this.flags.IsMetadataVirtualLocked)
            {
                this.ContainingSymbol.ForceComplete(locationOpt: null, filter: null, cancellationToken: CancellationToken.None);
            }
#endif
 
            return this.flags.IsMetadataVirtual(ignoreInterfaceImplementationChanges: option == IsMetadataVirtualOption.IgnoreInterfaceImplementationChanges);
        }
 
        internal void EnsureMetadataVirtual()
        {
            Debug.Assert(!this.IsStatic);
            this.flags.EnsureMetadataVirtual();
        }
 
        public override Accessibility DeclaredAccessibility
        {
            get
            {
                return ModifierUtils.EffectiveAccessibility(this.DeclarationModifiers);
            }
        }
 
        internal bool HasExternModifier
        {
            get
            {
                return (this.DeclarationModifiers & DeclarationModifiers.Extern) != 0;
            }
        }
 
        public override bool IsExtern
        {
            get
            {
                return HasExternModifier;
            }
        }
 
        public sealed override bool IsSealed
        {
            get
            {
                return (this.DeclarationModifiers & DeclarationModifiers.Sealed) != 0;
            }
        }
 
        public sealed override bool IsAbstract
        {
            get
            {
                return (this.DeclarationModifiers & DeclarationModifiers.Abstract) != 0;
            }
        }
 
        public sealed override bool IsOverride
        {
            get
            {
                return (this.DeclarationModifiers & DeclarationModifiers.Override) != 0;
            }
        }
 
        internal bool IsPartial
        {
            get
            {
                return (this.DeclarationModifiers & DeclarationModifiers.Partial) != 0;
            }
        }
 
        public sealed override bool IsVirtual
        {
            get
            {
                return (this.DeclarationModifiers & DeclarationModifiers.Virtual) != 0;
            }
        }
 
        internal bool IsNew
        {
            get
            {
                return (this.DeclarationModifiers & DeclarationModifiers.New) != 0;
            }
        }
 
        public sealed override bool IsStatic
        {
            get
            {
                return (this.DeclarationModifiers & DeclarationModifiers.Static) != 0;
            }
        }
 
        internal bool IsUnsafe
        {
            get
            {
                return (this.DeclarationModifiers & DeclarationModifiers.Unsafe) != 0;
            }
        }
 
        public sealed override bool IsAsync
        {
            get
            {
                return (this.DeclarationModifiers & DeclarationModifiers.Async) != 0;
            }
        }
 
        internal override bool IsDeclaredReadOnly
        {
            get
            {
                return (this.DeclarationModifiers & DeclarationModifiers.ReadOnly) != 0;
            }
        }
 
        internal override bool IsInitOnly => false;
 
        internal sealed override Cci.CallingConvention CallingConvention
        {
            get
            {
                var cc = IsVararg ? Cci.CallingConvention.ExtraArguments : Cci.CallingConvention.Default;
 
                if (IsGenericMethod)
                {
                    cc |= Cci.CallingConvention.Generic;
                }
 
                if (!IsStatic)
                {
                    cc |= Cci.CallingConvention.HasThis;
                }
 
                return cc;
            }
        }
 
        #endregion
 
        #region Syntax
 
        internal (BlockSyntax blockBody, ArrowExpressionClauseSyntax arrowBody) Bodies
        {
            get
            {
                switch (SyntaxNode)
                {
                    case BaseMethodDeclarationSyntax method:
                        return (method.Body, method.ExpressionBody);
 
                    case AccessorDeclarationSyntax accessor:
                        return (accessor.Body, accessor.ExpressionBody);
 
                    case ArrowExpressionClauseSyntax arrowExpression:
                        Debug.Assert(arrowExpression.Parent.Kind() == SyntaxKind.PropertyDeclaration ||
                                     arrowExpression.Parent.Kind() == SyntaxKind.IndexerDeclaration ||
                                     this is SynthesizedClosureMethod);
                        return (null, arrowExpression);
 
                    case BlockSyntax block:
                        Debug.Assert(this is SynthesizedClosureMethod);
                        return (block, null);
 
                    default:
                        return (null, null);
                }
            }
        }
 
        private Binder TryGetInMethodBinder(BinderFactory binderFactoryOpt = null)
        {
            CSharpSyntaxNode contextNode = GetInMethodSyntaxNode();
            if (contextNode == null)
            {
                return null;
            }
 
            Binder result = (binderFactoryOpt ?? this.DeclaringCompilation.GetBinderFactory(contextNode.SyntaxTree)).GetBinder(contextNode);
#if DEBUG
            Binder current = result;
            do
            {
                if (current is InMethodBinder)
                {
                    break;
                }
 
                current = current.Next;
            }
            while (current != null);
 
            Debug.Assert(current is InMethodBinder);
#endif
            return result;
        }
 
        internal abstract ExecutableCodeBinder TryGetBodyBinder(BinderFactory binderFactoryOpt = null, bool ignoreAccessibility = false);
 
        protected ExecutableCodeBinder TryGetBodyBinderFromSyntax(BinderFactory binderFactoryOpt = null, bool ignoreAccessibility = false)
        {
            Binder inMethod = TryGetInMethodBinder(binderFactoryOpt);
            return inMethod == null ? null : new ExecutableCodeBinder(SyntaxNode, this, inMethod.WithAdditionalFlags(ignoreAccessibility ? BinderFlags.IgnoreAccessibility : BinderFlags.None));
        }
 
        /// <summary>
        /// Overridden by <see cref="SourceOrdinaryMethodSymbol"/>, 
        /// which might return locations of partial methods.
        /// </summary>
        public override ImmutableArray<Location> Locations
            => ImmutableArray.Create(_location);
 
        public override Location TryGetFirstLocation()
            => _location;
 
        public 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);
        }
 
        #endregion
 
        public override ImmutableArray<CustomModifier> RefCustomModifiers
        {
            get
            {
                return ImmutableArray<CustomModifier>.Empty;
            }
        }
 
        public sealed override ImmutableArray<TypeWithAnnotations> TypeArgumentsWithAnnotations
        {
            get
            {
                return GetTypeParametersAsTypeArguments();
            }
        }
 
        public sealed override int Arity
        {
            get
            {
                return TypeParameters.Length;
            }
        }
 
        internal sealed override bool TryGetThisParameter(out ParameterSymbol thisParameter)
        {
            thisParameter = _lazyThisParameter;
            if ((object)thisParameter != null || IsStatic)
            {
                return true;
            }
 
            Interlocked.CompareExchange(ref _lazyThisParameter, new ThisParameterSymbol(this), null);
            thisParameter = _lazyThisParameter;
            return true;
        }
 
        //overridden appropriately in SourceMemberMethodSymbol
        public override ImmutableArray<MethodSymbol> ExplicitInterfaceImplementations
        {
            get
            {
                return ImmutableArray<MethodSymbol>.Empty;
            }
        }
 
        internal sealed override OverriddenOrHiddenMembersResult OverriddenOrHiddenMembers
        {
            get
            {
                this.LazyMethodChecks();
                if (_lazyOverriddenOrHiddenMembers == null)
                {
                    Interlocked.CompareExchange(ref _lazyOverriddenOrHiddenMembers, this.MakeOverriddenOrHiddenMembers(), null);
                }
 
                return _lazyOverriddenOrHiddenMembers;
            }
        }
 
        internal sealed override bool RequiresCompletion
        {
            get { return true; }
        }
 
        internal sealed override bool HasComplete(CompletionPart part)
        {
            return state.HasComplete(part);
        }
 
#nullable enable
        internal override void ForceComplete(SourceLocation? locationOpt, Predicate<Symbol>? filter, CancellationToken cancellationToken)
        {
            if (filter?.Invoke(this) == false)
            {
                return;
            }
 
            while (true)
            {
                cancellationToken.ThrowIfCancellationRequested();
                var incompletePart = state.NextIncompletePart;
                switch (incompletePart)
                {
                    case CompletionPart.Attributes:
                        GetAttributes();
 
                        if (this is SynthesizedPrimaryConstructor primaryConstructor)
                        {
                            // The constructor is responsible for completion of the backing fields
                            foreach (var field in primaryConstructor.GetBackingFields())
                            {
                                field.GetAttributes();
                            }
                        }
                        break;
 
                    case CompletionPart.ReturnTypeAttributes:
                        this.GetReturnTypeAttributes();
                        break;
 
                    case CompletionPart.Type:
                        var unusedType = this.ReturnTypeWithAnnotations;
                        state.NotePartComplete(CompletionPart.Type);
                        break;
 
                    case CompletionPart.Parameters:
                        foreach (var parameter in this.Parameters)
                        {
                            parameter.ForceComplete(locationOpt, filter: null, cancellationToken);
                        }
 
                        state.NotePartComplete(CompletionPart.Parameters);
                        break;
 
                    case CompletionPart.TypeParameters:
                        foreach (var typeParameter in this.TypeParameters)
                        {
                            typeParameter.ForceComplete(locationOpt, filter: null, cancellationToken);
                        }
 
                        state.NotePartComplete(CompletionPart.TypeParameters);
                        break;
 
                    case CompletionPart.StartAsyncMethodChecks:
                    case CompletionPart.FinishAsyncMethodChecks:
                        LazyAsyncMethodChecks(cancellationToken);
                        break;
 
                    case CompletionPart.StartMethodChecks:
                    case CompletionPart.FinishMethodChecks:
                        LazyMethodChecks();
                        goto done;
 
                    case CompletionPart.None:
                        return;
 
                    default:
                        // any other values are completion parts intended for other kinds of symbols
                        state.NotePartComplete(CompletionPart.All & ~CompletionPart.MethodSymbolAll);
                        break;
                }
 
                state.SpinWaitComplete(incompletePart, cancellationToken);
            }
 
done:
// Don't return until we've seen all of the CompletionParts. This ensures all
// diagnostics have been reported (not necessarily on this thread).
            CompletionPart allParts = CompletionPart.MethodSymbolAll;
            state.SpinWaitComplete(allParts, cancellationToken);
        }
#nullable disable
 
        protected sealed override void NoteAttributesComplete(bool forReturnType)
        {
            var part = forReturnType ? CompletionPart.ReturnTypeAttributes : CompletionPart.Attributes;
            state.NotePartComplete(part);
        }
 
        internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, BindingDiagnosticBag diagnostics)
        {
            base.AfterAddingTypeMembersChecks(conversions, diagnostics);
 
            var compilation = this.DeclaringCompilation;
 
            if (IsDeclaredReadOnly && !ContainingType.IsReadOnly)
            {
                compilation.EnsureIsReadOnlyAttributeExists(diagnostics, _location, modifyCompilation: true);
            }
 
            if (compilation.ShouldEmitNullableAttributes(this) &&
                ShouldEmitNullableContextValue(out _))
            {
                compilation.EnsureNullableContextAttributeExists(diagnostics, _location, modifyCompilation: true);
            }
        }
 
        // Consider moving this state to SourceMethodSymbol to emit NullableContextAttributes
        // on lambdas and local functions (see https://github.com/dotnet/roslyn/issues/36736).
        internal override byte? GetLocalNullableContextValue()
        {
            byte? value;
            if (!flags.TryGetNullableContext(out value))
            {
                value = ComputeNullableContextValue();
                flags.SetNullableContext(value);
            }
            return value;
        }
 
        private byte? ComputeNullableContextValue()
        {
            var compilation = DeclaringCompilation;
            if (!compilation.ShouldEmitNullableAttributes(this))
            {
                return null;
            }
 
            var builder = new MostCommonNullableValueBuilder();
            foreach (var typeParameter in TypeParameters)
            {
                typeParameter.GetCommonNullableValues(compilation, ref builder);
            }
            builder.AddValue(ReturnTypeWithAnnotations);
            foreach (var parameter in Parameters)
            {
                parameter.GetCommonNullableValues(compilation, ref builder);
            }
            return builder.MostCommonValue;
        }
 
        internal override bool IsNullableAnalysisEnabled()
        {
            Debug.Assert(!this.IsConstructor()); // Constructors should use IsNullableEnabledForConstructorsAndInitializers() instead.
            return flags.IsNullableAnalysisEnabled;
        }
 
        internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder<SynthesizedAttributeData> attributes)
        {
            base.AddSynthesizedAttributes(moduleBuilder, ref attributes);
 
            if (IsDeclaredReadOnly && !ContainingType.IsReadOnly)
            {
                AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeIsReadOnlyAttribute(this));
            }
 
            var compilation = this.DeclaringCompilation;
 
            if (compilation.ShouldEmitNullableAttributes(this) &&
                ShouldEmitNullableContextValue(out byte nullableContextValue))
            {
                AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableContextAttribute(this, nullableContextValue));
            }
 
            if (this.RequiresExplicitOverride(out _))
            {
                // On platforms where it is present, add PreserveBaseOverridesAttribute when a methodimpl is used to override a class method.
                AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizePreserveBaseOverridesAttribute());
            }
 
            bool isAsync = this.IsAsync;
            bool isIterator = this.IsIterator;
 
            if (!isAsync && !isIterator)
            {
                return;
            }
 
            // The async state machine type is not synthesized until the async method body is rewritten. If we are
            // only emitting metadata the method body will not have been rewritten, and the async state machine
            // type will not have been created. In this case, omit the attribute.
            if (moduleBuilder.CompilationState.TryGetStateMachineType(this, out NamedTypeSymbol stateMachineType))
            {
                var arg = new TypedConstant(compilation.GetWellKnownType(WellKnownType.System_Type),
                    TypedConstantKind.Type, stateMachineType.GetUnboundGenericTypeOrSelf());
 
                if (isAsync && isIterator)
                {
                    AddSynthesizedAttribute(ref attributes,
                        compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_AsyncIteratorStateMachineAttribute__ctor,
                            ImmutableArray.Create(arg)));
                }
                else if (isAsync)
                {
                    AddSynthesizedAttribute(ref attributes,
                        compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_AsyncStateMachineAttribute__ctor,
                            ImmutableArray.Create(arg)));
                }
                else if (isIterator)
                {
                    AddSynthesizedAttribute(ref attributes,
                        compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_IteratorStateMachineAttribute__ctor,
                            ImmutableArray.Create(arg)));
                }
            }
 
            if (isAsync && !isIterator)
            {
                // Regular async (not async-iterator) kick-off method calls MoveNext, which contains user code.
                // This means we need to emit DebuggerStepThroughAttribute in order
                // to have correct stepping behavior during debugging.
                AddSynthesizedAttribute(ref attributes, compilation.SynthesizeDebuggerStepThroughAttribute());
            }
        }
 
        /// <summary>
        /// Checks to see if a body is legal given the current modifiers.
        /// If it is not, a diagnostic is added with the current type.
        /// </summary>
        protected void CheckModifiersForBody(Location location, BindingDiagnosticBag diagnostics)
        {
            if (IsExtern && !IsAbstract)
            {
                diagnostics.Add(ErrorCode.ERR_ExternHasBody, location, this);
            }
            else if (IsAbstract && !IsExtern)
            {
                diagnostics.Add(ErrorCode.ERR_AbstractHasBody, location, this);
            }
            // Do not report error for IsAbstract && IsExtern. Dev10 reports CS0180 only
            // in that case ("member cannot be both extern and abstract").
        }
 
        protected void CheckFeatureAvailabilityAndRuntimeSupport(SyntaxNode declarationSyntax, Location location, bool hasBody, BindingDiagnosticBag diagnostics)
        {
            if (_containingType.IsInterface)
            {
                if ((!IsStatic || MethodKind is MethodKind.StaticConstructor) &&
                    (hasBody || IsExplicitInterfaceImplementation))
                {
                    Binder.CheckFeatureAvailability(declarationSyntax, MessageID.IDS_DefaultInterfaceImplementation, diagnostics, location);
                }
 
                if ((((hasBody || IsExtern) && !(IsStatic && IsVirtual)) || IsExplicitInterfaceImplementation) && !ContainingAssembly.RuntimeSupportsDefaultInterfaceImplementation)
                {
                    diagnostics.Add(ErrorCode.ERR_RuntimeDoesNotSupportDefaultInterfaceImplementation, location);
                }
 
                if (((!hasBody && IsAbstract) || IsVirtual) && !IsExplicitInterfaceImplementation && IsStatic && !ContainingAssembly.RuntimeSupportsStaticAbstractMembersInInterfaces)
                {
                    diagnostics.Add(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, location);
                }
            }
        }
 
        /// <summary>
        /// Returns true if the method body is an expression, as expressed
        /// by the <see cref="ArrowExpressionClauseSyntax"/> syntax. False
        /// otherwise.
        /// </summary>
        /// <remarks>
        /// If the method has both block body and an expression body
        /// present, this is not treated as expression-bodied.
        /// </remarks>
        internal bool IsExpressionBodied => flags.IsExpressionBodied;
 
        public sealed override RefKind RefKind => this.flags.RefKind;
        public sealed override bool IsVararg => this.flags.IsVararg;
 
        internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree)
        {
            Debug.Assert(this.SyntaxNode.SyntaxTree == localTree);
 
            (BlockSyntax blockBody, ArrowExpressionClauseSyntax expressionBody) = Bodies;
            CSharpSyntaxNode bodySyntax = null;
 
            // All locals are declared within the body of the method.
            if (blockBody?.Span.Contains(localPosition) == true)
            {
                bodySyntax = blockBody;
            }
            else if (expressionBody?.Span.Contains(localPosition) == true)
            {
                bodySyntax = expressionBody;
            }
            else
            {
                // Method without body doesn't declare locals.
                Debug.Assert(bodySyntax != null);
                return -1;
            }
 
            return localPosition - bodySyntax.SpanStart;
        }
    }
}