/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs
1 329 строк
55 KB
Julien Couvreur
Merge remote-tracking branch 'dotnet/main' into merge-main
17 июн 2026, 12:39
17 июн 2026, 12:39
be1f70f
Код
Авторство
О чём код?
// 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.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Reflection.Metadata; using Microsoft.CodeAnalysis.CSharp.Emit; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Symbols; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Symbols { /// <summary> /// Represents a method or method-like symbol (including constructor, /// destructor, operator, or property/event accessor). /// </summary> internal abstract partial class MethodSymbol : Symbol, IMethodSymbolInternal { internal const MethodSymbol None = null; // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // Changes to the public interface of this class should remain synchronized with the VB version. // Do not make any changes to the public interface without making the corresponding change // to the VB version. // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! protected MethodSymbol() { } /// <summary> /// The original definition of this symbol. If this symbol is constructed from another /// symbol by type substitution then OriginalDefinition gets the original symbol as it was defined in /// source or metadata. /// </summary> public new virtual MethodSymbol OriginalDefinition { get { return this; } } protected sealed override Symbol OriginalSymbolDefinition { get { return this.OriginalDefinition; } } /// <summary> /// Gets what kind of method this is. There are several different kinds of things in the /// C# language that are represented as methods. This property allow distinguishing those things /// without having to decode the name of the method. /// </summary> public abstract MethodKind MethodKind { get; } /// <summary> /// Returns the arity of this method. Arity is the number of type parameters a method declares. /// A non-generic method has zero arity. /// </summary> public abstract int Arity { get; } /// <summary> /// Returns whether this method is generic; i.e., does it have any type parameters? /// </summary> public virtual bool IsGenericMethod { get { return this.Arity != 0; } } /// <summary> /// Returns true if this symbol requires an instance reference as the implicit receiver. This is false if the symbol is static, or a <see cref="LocalFunctionSymbol"/> /// </summary> public virtual bool RequiresInstanceReceiver => !IsStatic; /// <summary> /// True if the method itself is excluded from code coverage instrumentation. /// True for source methods marked with <see cref="AttributeDescription.ExcludeFromCodeCoverageAttribute"/>. /// </summary> internal virtual bool IsDirectlyExcludedFromCodeCoverage { get => false; } internal abstract bool HasSpecialNameAttribute { get; } /// <summary> /// Returns the method-level runtime async setting from /// <c>RuntimeAsyncMethodGenerationAttribute</c>, or <see cref="ThreeState.Unknown"/> /// if no setting was specified. /// </summary> internal abstract ThreeState RuntimeAsyncMethodGenerationAttributeSetting { get; } /// <summary> /// If a method is annotated with `[MemberNotNull(...)]` attributes, returns the list of members /// listed in those attributes. /// Otherwise, an empty array. /// </summary> internal virtual ImmutableArray<string> NotNullMembers => ImmutableArray<string>.Empty; internal virtual ImmutableArray<string> NotNullWhenTrueMembers => ImmutableArray<string>.Empty; internal virtual ImmutableArray<string> NotNullWhenFalseMembers => ImmutableArray<string>.Empty; #nullable enable /// <summary> /// Returns the <see cref="UnmanagedCallersOnlyAttributeData"/> data for this method, if there is any. If forceComplete /// is false and the data has not yet been loaded or only early attribute binding has occurred, then either /// <see cref="UnmanagedCallersOnlyAttributeData.Uninitialized"/> or /// <see cref="UnmanagedCallersOnlyAttributeData.AttributePresentDataNotBound"/> will be returned, respectively. /// If passing true for forceComplete, ensure that cycles will not occur by not calling in the process of binding /// an attribute argument. /// </summary> internal abstract UnmanagedCallersOnlyAttributeData? GetUnmanagedCallersOnlyAttributeData(bool forceComplete); #nullable disable /// <summary> /// Returns true if this method is an extension method. /// </summary> public abstract bool IsExtensionMethod { get; } /// <summary> /// True if this symbol has a special name (metadata flag SpecialName is set). /// </summary> internal abstract bool HasSpecialName { get; } /// <summary> /// Misc implementation metadata flags (ImplFlags in metadata). /// </summary> internal abstract MethodImplAttributes ImplementationAttributes { get; } /// <summary> /// True if the type has declarative security information (HasSecurity flags). /// </summary> internal abstract bool HasDeclarativeSecurity { get; } internal abstract bool HasAsyncMethodBuilderAttribute(out TypeSymbol builderArgument); #nullable enable /// <summary> /// Platform invoke information, or null if the method isn't a P/Invoke. /// </summary> public abstract DllImportData? GetDllImportData(); #nullable disable /// <summary> /// Declaration security information associated with this type, or null if there is none. /// </summary> internal abstract IEnumerable<Microsoft.Cci.SecurityAttribute> GetSecurityInformation(); /// <summary> /// Marshalling information for return value (FieldMarshal in metadata). /// </summary> internal abstract MarshalPseudoCustomAttributeData ReturnValueMarshallingInformation { get; } /// <summary> /// True if the method calls another method containing security code (metadata flag RequiresSecurityObject is set). /// </summary> /// <remarks> /// A method can me marked as RequiresSecurityObject by applying the DynamicSecurityMethodAttribute in source. /// DynamicSecurityMethodAttribute is a pseudo custom attribute defined as an internal class in System.Security namespace. /// This attribute is set on certain security methods defined within mscorlib. /// </remarks> internal abstract bool RequiresSecurityObject { get; } // Note: This is no public "IsNew". This is intentional, because new has no syntactic meaning. /// <summary> /// Returns true if this method hides base methods by name. This cannot be specified directly /// in the C# language, but can be true for methods defined in other languages imported from /// metadata. The equivalent of the "hidebyname" flag in metadata. /// </summary> public abstract bool HidesBaseMethodsByName { get; } /// <summary> /// Returns whether this method is using CLI VARARG calling convention. This is used for C-style variable /// argument lists. This is used extremely rarely in C# code and is represented using the undocumented "__arglist" keyword. /// /// Note that methods with "params" on the last parameter are indicated with the "IsParams" property on ParameterSymbol, and /// are not represented with this property. /// </summary> public abstract bool IsVararg { get; } /// <summary> /// Returns whether this built-in operator checks for integer overflow. /// </summary> public virtual bool IsCheckedBuiltin { get { return false; } } /// <summary> /// Returns true if this method has no return type; i.e., returns "void". /// </summary> public abstract bool ReturnsVoid { get; } /// <summary> /// Source: Returns whether this method is async; i.e., does it have the async modifier? /// Metadata: Returns false; methods from metadata cannot be async. /// </summary> public abstract bool IsAsync { get; } /// <summary> /// Indicates whether or not the method returns by reference /// </summary> public bool ReturnsByRef { get { return this.RefKind == RefKind.Ref; } } /// <summary> /// Indicates whether or not the method returns by ref readonly /// </summary> public bool ReturnsByRefReadonly { get { Debug.Assert(this.RefKind != RefKind.Out); return this.RefKind == RefKind.RefReadOnly; } } /// <summary> /// Gets the ref kind of the method's return value /// </summary> public abstract RefKind RefKind { get; } /// <summary> /// Gets the return type of the method along with its annotations /// </summary> public abstract TypeWithAnnotations ReturnTypeWithAnnotations { get; } /// <summary> /// Gets the return type of the method /// </summary> public TypeSymbol ReturnType => ReturnTypeWithAnnotations.Type; public abstract FlowAnalysisAnnotations ReturnTypeFlowAnalysisAnnotations { get; } public abstract ImmutableHashSet<string> ReturnNotNullIfParameterNotNull { get; } /// <summary> /// Flow analysis annotations on the method itself (ie. DoesNotReturn) /// </summary> public abstract FlowAnalysisAnnotations FlowAnalysisAnnotations { get; } /// <summary> /// Returns the type arguments that have been substituted for the type parameters. /// If nothing has been substituted for a given type parameter, /// then the type parameter itself is consider the type argument. /// </summary> public abstract ImmutableArray<TypeWithAnnotations> TypeArgumentsWithAnnotations { get; } /// <summary> /// Get the type parameters on this method. If the method has not generic, /// returns an empty list. /// </summary> public abstract ImmutableArray<TypeParameterSymbol> TypeParameters { get; } internal ImmutableArray<TypeWithAnnotations> GetTypeParametersAsTypeArguments() { return TypeMap.TypeParametersAsTypeSymbolsWithAnnotations(TypeParameters); } #nullable enable /// <summary> /// Call <see cref="TryGetThisParameter"/> and throw if it returns false. /// </summary> internal ParameterSymbol? ThisParameter { get { ParameterSymbol? thisParameter; if (!TryGetThisParameter(out thisParameter)) { throw ExceptionUtilities.Unreachable(); } return thisParameter; } } /// <returns> /// True if this <see cref="MethodSymbol"/> type supports retrieving the this parameter /// and false otherwise. Note that a return value of true does not guarantee a non-null /// <paramref name="thisParameter"/> (e.g. fails for static methods). /// </returns> internal virtual bool TryGetThisParameter(out ParameterSymbol? thisParameter) { thisParameter = null; return false; } #nullable disable /// <summary> /// Optimization: in many cases, the parameter count (fast) is sufficient and we /// don't need the actual parameter symbols (slow). /// </summary> /// <remarks> /// The default implementation is always correct, but may be unnecessarily slow. /// </remarks> internal virtual int ParameterCount { get { return this.Parameters.Length; } } /// <summary> /// Gets the parameters of this method. If this method has no parameters, returns /// an empty list. /// </summary> public abstract ImmutableArray<ParameterSymbol> Parameters { get; } /// <summary> /// Returns the method symbol that this method was constructed from. The resulting /// method symbol /// has the same containing type (if any), but has type arguments that are the same /// as the type parameters (although its containing type might not). /// </summary> public virtual MethodSymbol ConstructedFrom { get { return this; } } /// <summary> /// Source: Was the member name qualified with a type name? /// Metadata: Is the member an explicit implementation? /// </summary> /// <remarks> /// Will not always agree with ExplicitInterfaceImplementations.Any() /// (e.g. if binding of the type part of the name fails). /// </remarks> internal virtual bool IsExplicitInterfaceImplementation { get { return ExplicitInterfaceImplementations.Any(); } } /// <summary> /// Indicates whether the method is declared readonly, i.e. /// whether the 'this' receiver parameter is 'ref readonly'. /// See also <see cref="IsEffectivelyReadOnly"/> /// </summary> internal abstract bool IsDeclaredReadOnly { get; } /// <summary> /// Indicates whether the accessor is marked with the 'init' modifier. /// </summary> internal abstract bool IsInitOnly { get; } /// <summary> /// Indicates whether the method is effectively readonly, /// by either the method or the containing type being marked readonly. /// </summary> internal virtual bool IsEffectivelyReadOnly => (IsDeclaredReadOnly || ContainingType?.IsReadOnly == true) && IsValidReadOnlyTarget; protected bool IsValidReadOnlyTarget => !IsStatic && ContainingType.IsStructType() && MethodKind != MethodKind.Constructor && !IsInitOnly; /// <summary> /// Returns interface methods explicitly implemented by this method. /// </summary> /// <remarks> /// Methods imported from metadata can explicitly implement more than one method, /// that is why return type is ImmutableArray. /// </remarks> public abstract ImmutableArray<MethodSymbol> ExplicitInterfaceImplementations { get; } /// <summary> /// Custom modifiers associated with the ref modifier, or an empty array if there are none. /// </summary> public abstract ImmutableArray<CustomModifier> RefCustomModifiers { get; } /// <summary> /// Gets the attributes on method's return type. /// Returns an empty array if there are no attributes. /// </summary> public virtual ImmutableArray<CSharpAttributeData> GetReturnTypeAttributes() { Debug.Assert(!(this is IAttributeTargetSymbol)); //such types must override // Return an empty array by default. // Sub-classes that can have return type attributes must // override this method return ImmutableArray<CSharpAttributeData>.Empty; } /// <summary> /// If this method has MethodKind of MethodKind.PropertyGet or MethodKind.PropertySet, /// returns the property that this method is the getter or setter for. /// If this method has MethodKind of MethodKind.EventAdd or MethodKind.EventRemove, /// returns the event that this method is the adder or remover for. /// Note, the set of possible associated symbols might be expanded in the future to /// reflect changes in the languages. /// </summary> public abstract Symbol AssociatedSymbol { get; } /// <summary> /// Returns the original virtual or abstract method which a given method symbol overrides, /// ignoring any other overriding methods in base classes. /// </summary> /// <param name="accessingTypeOpt">The search must respect accessibility from this type.</param> internal MethodSymbol GetLeastOverriddenMethod(NamedTypeSymbol accessingTypeOpt) { return GetLeastOverriddenMethodCore(accessingTypeOpt, requireSameReturnType: false); } /// <summary> /// Returns the original virtual or abstract method which a given method symbol overrides, /// ignoring any other overriding methods in base classes. /// </summary> /// <param name="accessingTypeOpt">The search must respect accessibility from this type.</param> /// <param name="requireSameReturnType">The returned method must have the same return type.</param> private MethodSymbol GetLeastOverriddenMethodCore(NamedTypeSymbol accessingTypeOpt, bool requireSameReturnType) { accessingTypeOpt = accessingTypeOpt?.OriginalDefinition; MethodSymbol m = this; while (m.IsOverride && !m.HidesBaseMethodsByName) { // We might not be able to access the overridden method. For example, // // .assembly A // { // InternalsVisibleTo("B") // public class A { internal virtual void M() { } } // } // // .assembly B // { // InternalsVisibleTo("C") // public class B : A { internal override void M() { } } // } // // .assembly C // { // public class C : B { ... new B().M ... } // A.M is not accessible from here // } // // See InternalsVisibleToAndStrongNameTests: IvtVirtualCall1, IvtVirtualCall2, IvtVirtual_ParamsAndDynamic. MethodSymbol overridden = m.OverriddenMethod; var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded; if ((object)overridden == null || (accessingTypeOpt is { } && !AccessCheck.IsSymbolAccessible(overridden, accessingTypeOpt, ref discardedUseSiteInfo)) || (requireSameReturnType && !this.ReturnType.Equals(overridden.ReturnType, TypeCompareKind.AllIgnoreOptions))) { break; } m = overridden; } return m; } /// <summary> /// Returns the original virtual or abstract method which a given method symbol overrides, /// ignoring any other overriding methods in base classes. /// Also, if the given method symbol is generic then the resulting virtual or abstract method is constructed with the /// same type arguments as the given method. /// </summary> /// <param name="requireSameReturnType">The returned method must have the same return type.</param> internal MethodSymbol GetConstructedLeastOverriddenMethod(NamedTypeSymbol accessingTypeOpt, bool requireSameReturnType) { var m = this.ConstructedFrom.GetLeastOverriddenMethodCore(accessingTypeOpt, requireSameReturnType); return m.IsGenericMethod ? m.Construct(this.TypeArgumentsWithAnnotations) : m; } /// <summary> /// If this method overrides another method (because it both had the override modifier /// and there correctly was a method to override), returns the overridden method. /// Note that if an overriding method D.M overrides C.M, which in turn overrides /// virtual method A.M, the "overridden method" of D.M is C.M, not the original virtual /// method A.M. Note also that constructed generic methods are not considered to /// override anything. /// </summary> public MethodSymbol OverriddenMethod { get { if (this.IsOverride && ReferenceEquals(this.ConstructedFrom, this)) { if (IsDefinition) { return (MethodSymbol)OverriddenOrHiddenMembers.GetOverriddenMember(); } return (MethodSymbol)OverriddenOrHiddenMembersResult.GetOverriddenMember(this, OriginalDefinition.OverriddenMethod); } return null; } } /// <summary> /// Returns true if calls to this method are omitted in this syntax tree. Calls are omitted /// when the called method is a partial method with no implementation part, or when the /// called method is a conditional method whose condition is not true in the source file /// corresponding to the given syntax tree. /// </summary> internal virtual bool CallsAreOmitted(SyntaxTree syntaxTree) { return syntaxTree != null && this.CallsAreConditionallyOmitted(syntaxTree); } /// <summary> /// Calls are conditionally omitted if both the following requirements are true: /// (a) IsConditional == true, i.e. it has at least one applied/inherited conditional attribute AND /// (b) None of conditional symbols corresponding to these conditional attributes are defined in the given syntaxTree. /// </summary> /// <remarks> /// Forces binding and decoding of attributes. /// </remarks> private bool CallsAreConditionallyOmitted(SyntaxTree syntaxTree) { if (this.IsConditional) { ImmutableArray<string> conditionalSymbols = this.GetAppliedConditionalSymbols(); Debug.Assert(conditionalSymbols != null); if (syntaxTree.IsAnyPreprocessorSymbolDefined(conditionalSymbols)) { return false; } if (this.IsOverride) { var overriddenMethod = this.OverriddenMethod; if ((object)overriddenMethod != null && overriddenMethod.IsConditional) { return overriddenMethod.CallsAreConditionallyOmitted(syntaxTree); } } return true; } else { return false; } } /// <summary> /// Returns a sequence of preprocessor symbols specified in <see cref="ConditionalAttribute"/> applied on this symbol, or null if there are none. /// </summary> internal abstract ImmutableArray<string> GetAppliedConditionalSymbols(); /// <summary> /// Returns a flag indicating whether this symbol has at least one applied/inherited conditional attribute. /// </summary> /// <remarks> /// Forces binding and decoding of attributes. /// </remarks> public bool IsConditional { get { if (this.GetAppliedConditionalSymbols().Any()) { return true; } // Conditional attributes are inherited by overriding methods. if (this.IsOverride) { var overriddenMethod = this.OverriddenMethod; if ((object)overriddenMethod != null) { return overriddenMethod.IsConditional; } } return false; } } /// <summary> /// Returns true if this is a constructor attributed with HasSetsRequiredMembers /// </summary> internal bool HasSetsRequiredMembers => MethodKind == MethodKind.Constructor && HasSetsRequiredMembersImpl; protected abstract bool HasSetsRequiredMembersImpl { get; } internal abstract bool HasUnscopedRefAttribute { get; } internal abstract bool UseUpdatedEscapeRules { get; } /// <summary> /// Some method kinds do not participate in overriding/hiding (e.g. constructors). /// </summary> internal static bool CanOverrideOrHide(MethodKind kind) { switch (kind) { case MethodKind.AnonymousFunction: case MethodKind.Constructor: case MethodKind.Destructor: case MethodKind.ExplicitInterfaceImplementation: case MethodKind.StaticConstructor: case MethodKind.ReducedExtension: return false; case MethodKind.Conversion: case MethodKind.DelegateInvoke: case MethodKind.EventAdd: case MethodKind.EventRemove: case MethodKind.LocalFunction: case MethodKind.UserDefinedOperator: case MethodKind.Ordinary: case MethodKind.PropertyGet: case MethodKind.PropertySet: return true; default: throw ExceptionUtilities.UnexpectedValue(kind); } } internal virtual OverriddenOrHiddenMembersResult OverriddenOrHiddenMembers { get { // To save space, the default implementation does not cache its result. We expect there to // be a very large number of MethodSymbols and we expect that a large percentage of them will // obviously not override anything (e.g. static methods, constructors, destructors, etc). return this.MakeOverriddenOrHiddenMembers(); } } /// <summary> /// Returns value 'Method' of the <see cref="SymbolKind"/> /// </summary> public sealed override SymbolKind Kind { get { return SymbolKind.Method; } } /// <summary> /// Returns true if this symbol represents a constructor of a script class. /// </summary> internal bool IsScriptConstructor { get { return MethodKind == MethodKind.Constructor && ContainingType.IsScriptClass; } } internal virtual bool IsScriptInitializer { get { return false; } } /// <summary> /// Returns if the method is implicit constructor (normal and static) /// </summary> internal bool IsImplicitConstructor { get { return ((MethodKind == MethodKind.Constructor || MethodKind == MethodKind.StaticConstructor) && IsImplicitlyDeclared); } } /// <summary> /// Returns if the method is implicit instance constructor /// </summary> internal bool IsImplicitInstanceConstructor { get { return MethodKind == MethodKind.Constructor && IsImplicitlyDeclared; } } /// <summary> /// Returns true if this symbol represents a constructor of an interactive submission class. /// </summary> internal bool IsSubmissionConstructor { get { return IsScriptConstructor && ContainingAssembly.IsInteractive; } } internal bool IsSubmissionInitializer { get { return IsScriptInitializer && ContainingAssembly.IsInteractive; } } /// <summary> /// Determines whether this method is a candidate for a default assembly entry point /// (i.e. it is a static method called "Main"). /// </summary> internal bool IsEntryPointCandidate { get { Debug.Assert(!this.IsExtensionBlockMember()); if (this.IsPartialDefinition() && this.PartialImplementationPart is null) { return false; } return IsStatic && !IsAbstract && !IsVirtual && Name == WellKnownMemberNames.EntryPointMethodName; } } internal override TResult Accept<TArgument, TResult>(CSharpSymbolVisitor<TArgument, TResult> visitor, TArgument argument) { return visitor.VisitMethod(this, argument); } public override void Accept(CSharpSymbolVisitor visitor) { visitor.VisitMethod(this); } public override TResult Accept<TResult>(CSharpSymbolVisitor<TResult> visitor) { return visitor.VisitMethod(this); } #nullable enable public MethodSymbol? ReduceExtensionMethod(TypeSymbol receiverType, CSharpCompilation? compilation) { return ReduceExtensionMethod(receiverType, compilation, wasFullyInferred: out _); } /// <summary> /// If this is an extension method that can be applied to a receiver of the given type, /// returns a reduced extension method symbol thus formed. Otherwise, returns null. /// </summary> /// <param name="compilation">The compilation in which constraints should be checked. /// Should not be null, but if it is null we treat constraints as we would in the latest /// language version.</param> public MethodSymbol? ReduceExtensionMethod(TypeSymbol receiverType, CSharpCompilation? compilation, out bool wasFullyInferred) { if ((object)receiverType == null) { throw new ArgumentNullException(nameof(receiverType)); } if (!this.IsExtensionMethod || this.MethodKind == MethodKind.ReducedExtension || receiverType.IsVoidType()) { wasFullyInferred = false; return null; } return ReducedExtensionMethodSymbol.Create(this, receiverType, compilation, out wasFullyInferred); } #nullable disable /// <summary> /// If this is an extension method, returns a reduced extension method /// symbol representing the method. Otherwise, returns null. /// </summary> public MethodSymbol ReduceExtensionMethod() { return (this.IsExtensionMethod && this.MethodKind != MethodKind.ReducedExtension) ? ReducedExtensionMethodSymbol.Create(this) : null; } /// <summary> /// If this method is a reduced extension method, returns the extension method that /// should be used at call site during ILGen. Otherwise, returns null. /// </summary> internal virtual MethodSymbol CallsiteReducedFromMethod { get { return null; } } /// <summary> /// If this is a partial method declaration without a body, and the method also /// has a part that implements it with a body, returns that implementing /// definition. Otherwise null. /// </summary> public virtual MethodSymbol PartialImplementationPart { get { return null; } } /// <summary> /// If this is a partial method with a body, returns the corresponding /// definition part (without a body). Otherwise null. /// </summary> public virtual MethodSymbol PartialDefinitionPart { get { return null; } } /// <summary> /// If this method is a reduced extension method, gets the extension method definition that /// this method was reduced from. Otherwise, returns null. /// </summary> public virtual MethodSymbol ReducedFrom { get { return null; } } /// <summary> /// If this method can be applied to an object, returns the type of object it is applied to. /// </summary> public virtual TypeSymbol ReceiverType { get { return this.ContainingType; } } /// <summary> /// If this method is a reduced extension method, returns a type inferred during reduction process for the type parameter. /// </summary> /// <param name="reducedFromTypeParameter">Type parameter of the corresponding <see cref="ReducedFrom"/> method.</param> /// <returns>Inferred type or Nothing if nothing was inferred.</returns> /// <exception cref="System.InvalidOperationException">If this is not a reduced extension method.</exception> /// <exception cref="System.ArgumentNullException">If <paramref name="reducedFromTypeParameter"/> is null.</exception> /// <exception cref="System.ArgumentException">If <paramref name="reducedFromTypeParameter"/> doesn't belong to the corresponding <see cref="ReducedFrom"/> method.</exception> public virtual TypeWithAnnotations GetTypeInferredDuringReduction(TypeParameterSymbol reducedFromTypeParameter) { throw new InvalidOperationException(); } /// <summary> /// Apply type substitution to a generic method to create a method symbol with the given type parameters supplied. /// </summary> /// <param name="typeArguments"></param> /// <returns></returns> public MethodSymbol Construct(params TypeSymbol[] typeArguments) { return this.Construct(ImmutableArray.Create(typeArguments)); } // https://github.com/dotnet/roslyn/issues/30071: Replace with Construct(ImmutableArray<TypeWithAnnotations>). /// <summary> /// Apply type substitution to a generic method to create a method symbol with the given type parameters supplied. /// </summary> /// <param name="typeArguments"></param> /// <returns></returns> public MethodSymbol Construct(ImmutableArray<TypeSymbol> typeArguments) { return Construct(typeArguments.SelectAsArray(a => TypeWithAnnotations.Create(a))); } internal MethodSymbol Construct(ImmutableArray<TypeWithAnnotations> typeArguments) { if (!ReferenceEquals(this, ConstructedFrom) || this.Arity == 0) { throw new InvalidOperationException(); } if (typeArguments.IsDefault) { throw new ArgumentNullException(nameof(typeArguments)); } if (typeArguments.Any(NamedTypeSymbol.TypeWithAnnotationsIsNullFunction)) { throw new ArgumentException(CSharpResources.TypeArgumentCannotBeNull, nameof(typeArguments)); } if (typeArguments.Length != this.Arity) { throw new ArgumentException(CSharpResources.WrongNumberOfTypeArguments, nameof(typeArguments)); } if (ConstructedNamedTypeSymbol.TypeParametersMatchTypeArguments(this.TypeParameters, typeArguments)) { return this; } return new ConstructedMethodSymbol(this, typeArguments); } internal MethodSymbol AsMember(NamedTypeSymbol newOwner) { Debug.Assert(this.IsDefinition); Debug.Assert(ReferenceEquals(newOwner.OriginalDefinition, this.ContainingSymbol.OriginalDefinition)); return newOwner.IsDefinition ? this : new SubstitutedMethodSymbol(newOwner, this); } /// <summary> /// As a performance optimization, cache parameter types and refkinds - overload resolution uses them a lot. /// </summary> private ParameterSignature _lazyParameterSignature; internal ImmutableArray<TypeWithAnnotations> ParameterTypesWithAnnotations { get { ParameterSignature.PopulateParameterSignature(this.Parameters, ref _lazyParameterSignature); return _lazyParameterSignature.parameterTypesWithAnnotations; } } internal TypeSymbol GetParameterType(int index) => ParameterTypesWithAnnotations[index].Type; /// <summary> /// Null if no parameter is ref/out. Otherwise the RefKind for each parameter. /// </summary> internal ImmutableArray<RefKind> ParameterRefKinds { get { ParameterSignature.PopulateParameterSignature(this.Parameters, ref _lazyParameterSignature); return _lazyParameterSignature.parameterRefKinds; } } internal abstract Microsoft.Cci.CallingConvention CallingConvention { get; } internal virtual ImmutableArray<NamedTypeSymbol> UnmanagedCallingConventionTypes => ImmutableArray<NamedTypeSymbol>.Empty; /// <summary> /// Returns the map from type parameters to type arguments. /// If this is not a generic method instantiation, returns null. /// The map targets the original definition of the method. /// </summary> internal virtual TypeMap TypeSubstitution { get { return null; } } #region Use-Site Diagnostics internal override UseSiteInfo<AssemblySymbol> GetUseSiteInfo() { if (this.IsDefinition) { return new UseSiteInfo<AssemblySymbol>(PrimaryDependency); } // There is no reason to specially check type arguments because // constructed members are never imported. return this.OriginalDefinition.GetUseSiteInfo(); } internal bool CalculateUseSiteDiagnostic(ref UseSiteInfo<AssemblySymbol> result) { Debug.Assert(this.IsDefinition); // Check return type, custom modifiers, parameters if (DeriveUseSiteInfoFromType(ref result, this.ReturnTypeWithAnnotations, IsInitOnly ? AllowedRequiredModifierType.System_Runtime_CompilerServices_IsExternalInit : AllowedRequiredModifierType.None) || DeriveUseSiteInfoFromCustomModifiers(ref result, this.RefCustomModifiers, AllowedRequiredModifierType.System_Runtime_InteropServices_InAttribute) || DeriveUseSiteInfoFromParameters(ref result, this.GetParametersIncludingExtensionParameter(skipExtensionIfStatic: false))) { return true; } // If the member is in an assembly with unified references, // we check if its definition depends on a type from a unified reference. if (this.ContainingModule?.HasUnifiedReferences == true) { HashSet<TypeSymbol> unificationCheckedTypes = null; DiagnosticInfo diagnosticInfo = result.DiagnosticInfo; if (this.ReturnTypeWithAnnotations.GetUnificationUseSiteDiagnosticRecursive(ref diagnosticInfo, this, ref unificationCheckedTypes) || GetUnificationUseSiteDiagnosticRecursive(ref diagnosticInfo, this.RefCustomModifiers, this, ref unificationCheckedTypes) || GetUnificationUseSiteDiagnosticRecursive(ref diagnosticInfo, this.Parameters, this, ref unificationCheckedTypes) || GetUnificationUseSiteDiagnosticRecursive(ref diagnosticInfo, this.TypeParameters, this, ref unificationCheckedTypes)) { result = result.AdjustDiagnosticInfo(diagnosticInfo); return true; } result = result.AdjustDiagnosticInfo(diagnosticInfo); } return false; } #nullable enable internal static (bool IsCallConvs, ImmutableHashSet<INamedTypeSymbolInternal>? CallConvs) TryDecodeUnmanagedCallersOnlyCallConvsField( string key, TypedConstant value, bool isField, Location? location, BindingDiagnosticBag? diagnostics) { ImmutableHashSet<INamedTypeSymbolInternal>? callingConventionTypes = null; if (!UnmanagedCallersOnlyAttributeData.IsCallConvsTypedConstant(key, isField, in value)) { return (false, callingConventionTypes); } if (value.Values.IsDefaultOrEmpty) { callingConventionTypes = ImmutableHashSet<INamedTypeSymbolInternal>.Empty; return (true, callingConventionTypes); } var builder = PooledHashSet<INamedTypeSymbolInternal>.GetInstance(); foreach (var callConvTypedConstant in value.Values) { Debug.Assert(callConvTypedConstant.Kind == TypedConstantKind.Type); if (!(callConvTypedConstant.ValueInternal is NamedTypeSymbol callConvType) || !FunctionPointerTypeSymbol.IsCallingConventionModifier(callConvType)) { // `{0}` is not a valid calling convention type for 'UnmanagedCallersOnly'. diagnostics?.Add(ErrorCode.ERR_InvalidUnmanagedCallersOnlyCallConv, location!, callConvTypedConstant.ValueInternal ?? "null"); } else { _ = builder.Add(callConvType); } } callingConventionTypes = builder.ToImmutableHashSet(); builder.Free(); return (true, callingConventionTypes); } /// <summary> /// Determines if this method is a valid target for UnmanagedCallersOnly, reporting an error in the given diagnostic /// bag if it is not null. <paramref name="node"/> and <paramref name="diagnostics"/> should both be null, or /// neither should be null. If an error would be reported (whether or not diagnostics is null), true is returned. /// </summary> internal bool CheckAndReportValidUnmanagedCallersOnlyTarget(SyntaxNode? node, BindingDiagnosticBag? diagnostics) { Debug.Assert((node == null) == (diagnostics == null)); if (!IsStatic || IsAbstract || IsVirtual || MethodKind is not (MethodKind.Ordinary or MethodKind.LocalFunction)) { // `UnmanagedCallersOnly` can only be applied to ordinary static methods or local functions. diagnostics?.Add(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, node!.Location); return true; } if (isGenericMethod(this) || ContainingType.IsGenericType) { diagnostics?.Add(ErrorCode.ERR_UnmanagedCallersOnlyMethodOrTypeCannotBeGeneric, node!.Location); return true; } return false; static bool isGenericMethod([DisallowNull] MethodSymbol? method) { do { if (method.IsGenericMethod) { return true; } method = method.ContainingSymbol as MethodSymbol; } while (method is not null); return false; } } #nullable disable /// <summary> /// Returns true if the error code is highest priority while calculating use site error for this symbol. /// </summary> protected sealed override bool IsHighestPriorityUseSiteErrorCode(int code) => code is (int)ErrorCode.ERR_UnsupportedCompilerFeature or (int)ErrorCode.ERR_BindToBogus; public sealed override bool HasUnsupportedMetadata { get { DiagnosticInfo info = GetUseSiteInfo().DiagnosticInfo; return (object)info != null && info.Code is (int)ErrorCode.ERR_BindToBogus or (int)ErrorCode.ERR_UnsupportedCompilerFeature; } } #endregion internal virtual bool IsIterator { get { return false; } } /// <summary> /// If the method was written as an iterator method (i.e. with yield statements in its body) returns the /// element type of the iterator. Otherwise returns default(TypeWithAnnotations). /// </summary> internal virtual TypeWithAnnotations IteratorElementTypeWithAnnotations { get { return default; } } /// <summary> /// Generates bound block representing method's body for methods in lowered form and adds it to /// a collection of method bodies of the current module. This method is supposed to only be /// called for method symbols which return SynthesizesLoweredBoundBody == true. /// </summary> internal virtual void GenerateMethodBody(TypeCompilationState compilationState, BindingDiagnosticBag diagnostics) { throw ExceptionUtilities.Unreachable(); } /// <summary> /// Returns true for synthesized symbols which generate synthesized body in lowered form /// </summary> internal virtual bool SynthesizesLoweredBoundBody { get { return false; } } /// <summary> /// Return true iff the method contains user code. /// </summary> internal abstract bool GenerateDebugInfo { get; } /// <summary> /// Calculates a syntax offset for a local (user-defined or long-lived synthesized) declared at <paramref name="localPosition"/>. /// Must be implemented by all methods that may contain user code. /// </summary> /// <remarks> /// Syntax offset is a unique identifier for the local within the emitted method body. /// It's based on position of the local declarator. In single-part method bodies it's simply the distance /// from the start of the method body syntax span. If a method body has multiple parts (such as a constructor /// comprising of code for member initializers and constructor initializer calls) the offset is calculated /// as if all source these parts were concatenated together and prepended to the constructor body. /// The resulting syntax offset is then negative for locals defined outside of the constructor body. /// </remarks> internal abstract int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree); internal virtual CodeAnalysis.NullableAnnotation ReceiverNullableAnnotation => RequiresInstanceReceiver ? CodeAnalysis.NullableAnnotation.NotAnnotated : CodeAnalysis.NullableAnnotation.None; /// <summary> /// Build and add synthesized return type attributes for this method symbol. /// </summary> internal virtual void AddSynthesizedReturnTypeAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder<CSharpAttributeData> attributes) { if (this.ReturnsByRefReadonly) { AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeIsReadOnlyAttribute(this)); } var compilation = this.DeclaringCompilation; var type = this.ReturnTypeWithAnnotations; if (type.Type.ContainsDynamic() && compilation.HasDynamicEmitAttributes(BindingDiagnosticBag.Discarded, Location.None)) { AddSynthesizedAttribute(ref attributes, compilation.SynthesizeDynamicAttribute(type.Type, type.CustomModifiers.Length + this.RefCustomModifiers.Length, this.RefKind)); } if (compilation.ShouldEmitNativeIntegerAttributes(type.Type)) { AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNativeIntegerAttribute(this, type.Type)); } if (type.Type.ContainsTupleNames() && compilation.HasTupleNamesAttributes(BindingDiagnosticBag.Discarded, Location.None)) { AddSynthesizedAttribute(ref attributes, compilation.SynthesizeTupleNamesAttribute(type.Type)); } if (compilation.ShouldEmitNullableAttributes(this)) { AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttributeIfNecessary(this, GetNullableContextValue(), type)); } } /// <summary> /// Returns true if locals are to be initialized /// </summary> public abstract bool AreLocalsZeroed { get; } internal abstract bool IsNullableAnalysisEnabled(); /// <summary> /// Gets the resolution priority of this method, 0 if not set. /// </summary> /// <remarks> /// Do not call this method from early attribute binding, cycles will occur. /// </remarks> internal int OverloadResolutionPriority => CanHaveOverloadResolutionPriority ? TryGetOverloadResolutionPriority() : 0; internal abstract int TryGetOverloadResolutionPriority(); internal bool CanHaveOverloadResolutionPriority => MethodKind is MethodKind.Ordinary or MethodKind.Constructor or MethodKind.UserDefinedOperator or MethodKind.ReducedExtension && !IsOverride; #region IMethodSymbolInternal bool IMethodSymbolInternal.HasDeclarativeSecurity => HasDeclarativeSecurity; bool IMethodSymbolInternal.IsAccessCheckedOnOverride => IsAccessCheckedOnOverride; bool IMethodSymbolInternal.IsExternal => IsExternal; bool IMethodSymbolInternal.IsHiddenBySignature => !HidesBaseMethodsByName; bool IMethodSymbolInternal.IsMetadataNewSlotIgnoringInterfaceImplementationChanges => IsMetadataNewSlot(context: null, ignoreInterfaceImplementationChanges: true); bool IMethodSymbolInternal.IsPlatformInvoke => GetDllImportData() != null; bool IMethodSymbolInternal.HasRuntimeSpecialName => HasRuntimeSpecialName; bool IMethodSymbolInternal.IsMetadataFinal => IsSealed; bool IMethodSymbolInternal.HasSpecialName => HasSpecialName; bool IMethodSymbolInternal.RequiresSecurityObject => RequiresSecurityObject; MethodImplAttributes IMethodSymbolInternal.ImplementationAttributes => ImplementationAttributes; bool IMethodSymbolInternal.IsIterator => IsIterator; ISymbolInternal IMethodSymbolInternal.AssociatedSymbol => AssociatedSymbol; IMethodSymbolInternal IMethodSymbolInternal.PartialImplementationPart => PartialImplementationPart; IMethodSymbolInternal IMethodSymbolInternal.PartialDefinitionPart => PartialDefinitionPart; /// <summary> /// Gets the handle for the signature of this method as it appears in metadata. /// Nil handle for symbols not loaded from metadata, or if the metadata is invalid. /// </summary> public virtual BlobHandle MetadataSignatureHandle => default; int IMethodSymbolInternal.ParameterCount => ParameterCount; ImmutableArray<IParameterSymbolInternal> IMethodSymbolInternal.Parameters => Parameters.Cast<ParameterSymbol, IParameterSymbolInternal>(); int IMethodSymbolInternal.CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree) => CalculateLocalSyntaxOffset(localPosition, localTree); IMethodSymbolInternal IMethodSymbolInternal.Construct(params ITypeSymbolInternal[] typeArguments) { return Construct((TypeSymbol[])typeArguments); } #endregion protected sealed override ISymbol CreateISymbol() { return new PublicModel.MethodSymbol(this); } public override bool Equals(Symbol other, TypeCompareKind compareKind) { if (other is SubstitutedMethodSymbol sms) { return sms.Equals(this, compareKind); } if (other is NativeIntegerMethodSymbol nms) { return nms.Equals(this, compareKind); } return base.Equals(other, compareKind); } public override int GetHashCode() { return base.GetHashCode(); } #nullable enable protected static void AddRequiredMembersMarkerAttributes(ref ArrayBuilder<CSharpAttributeData> attributes, MethodSymbol methodToAttribute) { if (methodToAttribute.ShouldCheckRequiredMembers() && methodToAttribute.ContainingType.HasAnyRequiredMembers) { var obsoleteData = methodToAttribute.ObsoleteAttributeData; Debug.Assert(obsoleteData != ObsoleteAttributeData.Uninitialized, "getting synthesized attributes before attributes are decoded"); CSharpCompilation declaringCompilation = methodToAttribute.DeclaringCompilation; if (obsoleteData == null) { AddSynthesizedAttribute(ref attributes, declaringCompilation.TrySynthesizeAttribute(WellKnownMember.System_ObsoleteAttribute__ctor, ImmutableArray.Create( new TypedConstant(declaringCompilation.GetSpecialType(SpecialType.System_String), TypedConstantKind.Primitive, PEModule.RequiredMembersMarker), // message new TypedConstant(declaringCompilation.GetSpecialType(SpecialType.System_Boolean), TypedConstantKind.Primitive, true)) // error )); } AddSynthesizedAttribute(ref attributes, declaringCompilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_CompilerFeatureRequiredAttribute__ctor, ImmutableArray.Create(new TypedConstant(declaringCompilation.GetSpecialType(SpecialType.System_String), TypedConstantKind.Primitive, nameof(CompilerFeatureRequiredFeatures.RequiredMembers))) )); } } protected static void AddClosedClassesFeatureRequiredAttribute(ref ArrayBuilder<CSharpAttributeData> attributes, MethodSymbol methodToAttribute) { if (methodToAttribute.ContainingType.IsClosed) { CSharpCompilation declaringCompilation = methodToAttribute.DeclaringCompilation; AddSynthesizedAttribute( ref attributes, declaringCompilation.TrySynthesizeAttribute( WellKnownMember.System_Runtime_CompilerServices_CompilerFeatureRequiredAttribute__ctor, [new TypedConstant(declaringCompilation.GetSpecialType(SpecialType.System_String), TypedConstantKind.Primitive, nameof(CompilerFeatureRequiredFeatures.ClosedClasses))])); } } public MethodSymbol? TryGetCorrespondingExtensionImplementationMethod() { Debug.Assert(this.IsDefinition); Debug.Assert(this.IsExtensionBlockMember()); return this.ContainingType.TryGetCorrespondingExtensionImplementationMethod(this); } } }