| File: Symbols\MethodSymbolExtensions.cs | Web Access |
| Project: src\roslyn\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable disable using Roslyn.Utilities; using System.Collections.Immutable; using System.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.Symbols { internal static class MethodSymbolExtensions { public static bool IsParams(this MethodSymbol method) { return method.ParameterCount != 0 && method.Parameters[method.ParameterCount - 1].IsParams; } internal static bool IsSynthesizedLambda(this MethodSymbol method) { Debug.Assert((object)method != null); return method.IsImplicitlyDeclared && method.MethodKind == MethodKind.AnonymousFunction; } /// <summary> /// The runtime considers a method to be a finalizer (i.e. a method that should be invoked /// by the garbage collector) if it (directly or indirectly) overrides System.Object.Finalize. /// </summary> /// <remarks> /// As an optimization, return true immediately for metadata methods with MethodKind /// Destructor - they are guaranteed to be finalizers. /// </remarks> /// <param name="method">Method to inspect.</param> /// <param name="skipFirstMethodKindCheck">This method is used to determine the method kind of /// a PEMethodSymbol, so we may need to avoid using MethodKind until we move on to a different /// MethodSymbol.</param> public static bool IsRuntimeFinalizer(this MethodSymbol method, bool skipFirstMethodKindCheck = false) { // Note: Flipping the metadata-virtual bit on a method can't change it from not-a-runtime-finalize // to runtime-finalizer (since it will also be marked newslot), so it is safe to use // IsMetadataVirtual(ignoreInterfaceImplementationChanges: true). This also has the advantage of making // this method safe to call before declaration diagnostics have been computed. if ((object)method == null || method.Name != WellKnownMemberNames.DestructorName || method.ParameterCount != 0 || method.Arity != 0 || !method.IsMetadataVirtual(context: null, ignoreInterfaceImplementationChanges: true)) { return false; } while ((object)method != null) { if (!skipFirstMethodKindCheck && method.MethodKind == MethodKind.Destructor) { // For metadata symbols (and symbols that wrap them), having MethodKind // Destructor guarantees that the method is a runtime finalizer. // This is also true for source symbols, since we make them explicitly // override System.Object.Finalize. return true; } else if (method.ContainingType.SpecialType == SpecialType.System_Object) { return true; } else if (method.IsMetadataNewSlot(context: null, ignoreInterfaceImplementationChanges: true)) { // If the method isn't a runtime override, then it can't be a finalizer. return false; } // At this point, we know method originated with a DestructorDeclarationSyntax in source, // so it can't have the "new" modifier. // First is fine, since there should only be one, since there are no parameters. method = method.GetFirstRuntimeOverriddenMethodIgnoringNewSlot(out _); skipFirstMethodKindCheck = false; } return false; } /// <summary> /// Returns a constructed method symbol if 'method' is generic, otherwise just returns 'method' /// </summary> public static MethodSymbol ConstructIfGeneric(this MethodSymbol method, ImmutableArray<TypeWithAnnotations> typeArguments) { Debug.Assert(method.IsGenericMethod == (typeArguments.Length > 0)); return method.IsGenericMethod ? method.Construct(typeArguments) : method; } /// <summary> /// Some kinds of methods are not considered to be hideable by certain kinds of members. /// Specifically, methods, properties, and types cannot hide constructors, destructors, /// operators, conversions, or accessors. /// </summary> public static bool CanBeHiddenByMember(this MethodSymbol hiddenMethod, Symbol hidingMember) { Debug.Assert((object)hiddenMethod != null); // Nothing can hide a destructor (see SymbolPreparer::ReportHiding) if (hiddenMethod.MethodKind == MethodKind.Destructor) { return false; } switch (hidingMember.Kind) { case SymbolKind.ErrorType: case SymbolKind.NamedType: case SymbolKind.Method: case SymbolKind.Property: return CanBeHiddenByMethodPropertyOrType(hiddenMethod, hidingMember); case SymbolKind.Field: case SymbolKind.Event: // Events are not covered by CSemanticChecker::FindSymHiddenByMethPropAgg. return true; default: throw ExceptionUtilities.UnexpectedValue(hidingMember.Kind); } } /// <summary> /// Some kinds of methods are never considered hidden by methods, properties, or types /// (constructors, destructors, operators, conversions, and accessors). /// </summary> private static bool CanBeHiddenByMethodPropertyOrType(MethodSymbol method, Symbol hidingMember) { switch (method.MethodKind) { // See CSemanticChecker::FindSymHiddenByMethPropAgg. case MethodKind.Destructor: case MethodKind.Constructor: case MethodKind.StaticConstructor: case MethodKind.Conversion: return false; case MethodKind.UserDefinedOperator: return !method.IsStatic && hidingMember is MethodSymbol { IsStatic: false, MethodKind: MethodKind.UserDefinedOperator }; case MethodKind.EventAdd: case MethodKind.EventRemove: case MethodKind.PropertyGet: case MethodKind.PropertySet: return method.IsIndexedPropertyAccessor(); default: return true; } } #nullable enable /// <summary> /// Returns whether this method is async and returns void. /// </summary> public static bool IsAsyncReturningVoid(this MethodSymbol method) { return method.IsAsync && method.ReturnsVoid; } /// <summary> /// Returns whether this method is async and returns a task, task-like, or other type with a method-level builder. /// </summary> public static bool IsAsyncEffectivelyReturningTask(this MethodSymbol method, CSharpCompilation compilation) { return method.IsAsync && method.ReturnType is NamedTypeSymbol { Arity: 0 } && (method.HasAsyncMethodBuilderAttribute(builderArgument: out _) || method.ReturnType.IsNonGenericTaskType(compilation)); } /// <summary> /// Returns whether this method is async and returns a generic task, task-like, or other type with a method-level builder. /// </summary> public static bool IsAsyncEffectivelyReturningGenericTask(this MethodSymbol method, CSharpCompilation compilation) { return method.IsAsync && method.ReturnType is NamedTypeSymbol { Arity: 1 } && (method.HasAsyncMethodBuilderAttribute(builderArgument: out _) || method.ReturnType.IsGenericTaskType(compilation)); } /// <summary> /// Returns whether this method is async and returns an IAsyncEnumerable`1. /// </summary> public static bool IsAsyncReturningIAsyncEnumerable(this MethodSymbol method, CSharpCompilation compilation) { return method.IsAsync && method.ReturnType.IsIAsyncEnumerableType(compilation); } /// <summary> /// Returns whether this method is async and returns an IAsyncEnumerator`1. /// </summary> public static bool IsAsyncReturningIAsyncEnumerator(this MethodSymbol method, CSharpCompilation compilation) { return method.IsAsync && method.ReturnType.IsIAsyncEnumeratorType(compilation); } internal static CSharpSyntaxNode ExtractReturnTypeSyntax(this MethodSymbol method) { if (method is SynthesizedSimpleProgramEntryPointSymbol synthesized) { return (CSharpSyntaxNode)synthesized.ReturnTypeSyntax; } method = method.PartialDefinitionPart ?? method; foreach (var reference in method.DeclaringSyntaxReferences) { SyntaxNode node = reference.GetSyntax(); if (node is MethodDeclarationSyntax methodDeclaration) { return methodDeclaration.ReturnType; } else if (node is LocalFunctionStatementSyntax statement) { return statement.ReturnType; } } return (CSharpSyntaxNode)CSharpSyntaxTree.Dummy.GetRoot(); } internal static bool IsValidUnscopedRefAttributeTarget(this MethodSymbol method) { return !method.IsStatic && method.ContainingType is NamedTypeSymbol containingType && (containingType.IsStructType() == true || (containingType.IsInterface && method.IsImplementable())) && method.MethodKind is (MethodKind.Ordinary or MethodKind.ExplicitInterfaceImplementation or MethodKind.PropertyGet or MethodKind.PropertySet or MethodKind.UserDefinedOperator) && !method.IsInitOnly; } internal static bool HasUnscopedRefAttributeOnMethodOrProperty(this MethodSymbol? method) { if (method is null) { return false; } return method.HasUnscopedRefAttribute || method.AssociatedSymbol is PropertySymbol { HasUnscopedRefAttribute: true }; } } }