|
// 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, or the number of type parameters it takes.
/// 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; }
/// <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);
}
/// <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;
}
/// <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
{
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);
}
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);
}
/// <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 TypeSymbol 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.Parameters))
{
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<SynthesizedAttributeData> 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) : 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.IsMetadataNewSlot => IsMetadataNewSlot();
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<SynthesizedAttributeData> 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)))
));
}
}
}
}
|