File: Symbols\Source\SourceMethodSymbol.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.
 
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Emit;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
    /// <summary>
    /// Base class to represent all source method-like symbols. This includes
    /// things like ordinary methods and constructors, and functions
    /// like lambdas and local functions.
    /// </summary>
    internal abstract partial class SourceMethodSymbol : MethodSymbol
    {
        /// <summary>
        /// If there are no constraints, returns an empty immutable array. Otherwise, returns an immutable
        /// array of types, indexed by the constrained type parameter in <see cref="MethodSymbol.TypeParameters"/>.
        /// </summary>
        public abstract ImmutableArray<ImmutableArray<TypeWithAnnotations>> GetTypeParameterConstraintTypes();
 
        /// <summary>
        /// If there are no constraints, returns an empty immutable array. Otherwise, returns an immutable
        /// array of kinds, indexed by the constrained type parameter in <see cref="MethodSymbol.TypeParameters"/>.
        /// </summary>
        public abstract ImmutableArray<TypeParameterConstraintKind> GetTypeParameterConstraintKinds();
 
        protected static void ReportBadRefToken(TypeSyntax returnTypeSyntax, BindingDiagnosticBag diagnostics)
        {
            if (!returnTypeSyntax.HasErrors)
            {
                var refKeyword = returnTypeSyntax.GetFirstToken();
                diagnostics.Add(ErrorCode.ERR_UnexpectedToken, refKeyword.GetLocation(), refKeyword.ToString());
            }
        }
 
        protected bool AreContainingSymbolLocalsZeroed
        {
            get
            {
                if (ContainingSymbol is SourceMethodSymbol method)
                {
                    return method.AreLocalsZeroed;
                }
                else if (ContainingType is SourceMemberContainerTypeSymbol type)
                {
                    return type.AreLocalsZeroed;
                }
                else
                {
                    // Sometimes a source method symbol can be contained in a non-source symbol.
                    // For example in EE. We aren't concerned with respecting SkipLocalsInit in such cases.
                    return true;
                }
            }
        }
 
        internal void ReportAsyncParameterErrors(BindingDiagnosticBag diagnostics, Location location)
        {
            var parameters = !this.IsStatic ? this.GetParametersIncludingExtensionParameter() : Parameters;
 
            foreach (var parameter in parameters)
            {
                bool isExtensionParameter = parameter.IsExtensionParameter();
                if (parameter.RefKind != RefKind.None)
                {
                    diagnostics.Add(ErrorCode.ERR_BadAsyncArgType, getLocation(parameter, location, isExtensionParameter));
                }
                else if (parameter.Type.IsPointerOrFunctionPointer() && !isExtensionParameter)
                {
                    // We already reported an error elsewhere if the receiver parameter of an extension is a pointer type.
                    diagnostics.Add(ErrorCode.ERR_UnsafeAsyncArgType, getLocation(parameter, location, isExtensionParameter));
                }
                else if (parameter.Type.IsRestrictedType())
                {
                    diagnostics.Add(ErrorCode.ERR_BadSpecialByRefParameter, getLocation(parameter, location, isExtensionParameter), parameter.Type);
                }
            }
 
            static Location getLocation(ParameterSymbol parameter, Location location, bool isReceiverParameter)
            {
                return isReceiverParameter
                    ? location
                    : parameter.TryGetFirstLocation() ?? location;
            }
        }
 
        protected override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable();
 
        internal sealed override bool UseUpdatedEscapeRules => ContainingModule.UseUpdatedEscapeRules;
 
        internal override bool HasAsyncMethodBuilderAttribute(out TypeSymbol? builderArgument)
        {
            return SourceMemberContainerTypeSymbol.HasAsyncMethodBuilderAttribute(this, out builderArgument);
        }
 
        internal sealed override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder<CSharpAttributeData> attributes)
        {
            base.AddSynthesizedAttributes(moduleBuilder, ref attributes);
            AddSynthesizedAttributes(this, moduleBuilder, ref attributes);
        }
 
        internal static void AddSynthesizedAttributes(MethodSymbol target, PEModuleBuilder moduleBuilder, ref ArrayBuilder<CSharpAttributeData> attributes)
        {
            Debug.Assert(target is not (LambdaSymbol or LocalFunctionSymbol));
 
            if (target.IsDeclaredReadOnly && !target.ContainingType.IsReadOnly)
            {
                AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeIsReadOnlyAttribute(target));
            }
 
            var compilation = target.DeclaringCompilation;
 
            if (compilation.ShouldEmitNullableAttributes(target) &&
                target.ShouldEmitNullableContextValue(out byte nullableContextValue))
            {
                AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableContextAttribute(target, nullableContextValue));
            }
 
            if (target.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 = target.IsAsync;
            bool isIterator = target.IsIterator;
 
            if ((isAsync || isIterator) && !target.GetIsNewExtensionMember())
            {
                // 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(target, 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());
                }
            }
 
            // Do not generate CompilerGeneratedAttribute for members of compiler-generated types:
            if (((target.IsImplicitlyDeclared && target is not SourceFieldLikeEventSymbol.SourceEventDefinitionAccessorSymbol { PartialImplementationPart.IsImplicitlyDeclared: false }) ||
                 target is SourcePropertyAccessorSymbol { IsAutoPropertyAccessor: true }) &&
                !target.ContainingType.IsImplicitlyDeclared &&
                target is SynthesizedMethodBaseSymbol or
                          SourcePropertyAccessorSymbol or
                          SynthesizedSourceOrdinaryMethodSymbol or
                          SynthesizedRecordEqualityOperatorBase or
                          SynthesizedEventAccessorSymbol or
                          SourceFieldLikeEventSymbol.SourceEventDefinitionAccessorSymbol)
            {
                Debug.Assert(WellKnownMembers.IsSynthesizedAttributeOptional(WellKnownMember.System_Runtime_CompilerServices_CompilerGeneratedAttribute__ctor));
                AddSynthesizedAttribute(ref attributes,
                    compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_CompilerGeneratedAttribute__ctor));
            }
 
            if (target is SourceConstructorSymbolBase)
            {
                AddRequiredMembersMarkerAttributes(ref attributes, target);
            }
 
            if (target.IsExtensionMethod)
            {
                // No need to check if [Extension] attribute was explicitly set since
                // we'll issue CS1112 error in those cases and won't generate IL.
                AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute(
                    WellKnownMember.System_Runtime_CompilerServices_ExtensionAttribute__ctor));
            }
 
            if (target is SourcePropertyAccessorSymbol { AssociatedSymbol: SourcePropertySymbolBase property })
            {
                if (!target.NotNullMembers.IsEmpty)
                {
                    foreach (var attributeData in property.MemberNotNullAttributeIfExists)
                    {
                        AddSynthesizedAttribute(ref attributes, SynthesizedAttributeData.Create(attributeData));
                    }
                }
 
                if (!target.NotNullWhenTrueMembers.IsEmpty || !target.NotNullWhenFalseMembers.IsEmpty)
                {
                    foreach (var attributeData in property.MemberNotNullWhenAttributeIfExists)
                    {
                        AddSynthesizedAttribute(ref attributes, SynthesizedAttributeData.Create(attributeData));
                    }
                }
            }
 
            if (target is MethodToClassRewriter.BaseMethodWrapperSymbol)
            {
                AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute(WellKnownMember.System_Diagnostics_DebuggerHiddenAttribute__ctor));
            }
        }
    }
}