| File: ISymbolExtensions.cs | Web Access |
| Project: src\runtime\src\tools\illink\src\ILLink.RoslynAnalyzer\ILLink.RoslynAnalyzer.csproj (ILLink.RoslynAnalyzer) |
// Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; using ILLink.RoslynAnalyzer.DataFlow; using ILLink.Shared.DataFlow; using Microsoft.CodeAnalysis; namespace ILLink.RoslynAnalyzer { public static class ISymbolExtensions { /// <summary> /// Returns true if symbol <see paramref="symbol"/> has an attribute with name <see paramref="attributeName"/>. /// </summary> internal static bool HasAttribute(this ISymbol symbol, string attributeName) { foreach (var attr in symbol.GetAttributes()) if (attr.AttributeClass?.Name == attributeName) return true; return false; } internal static bool TryGetAttribute(this ISymbol member, string attributeName, [NotNullWhen(returnValue: true)] out AttributeData? attribute) { attribute = null; foreach (var attr in member.GetAttributes()) { if (attr.AttributeClass is { } attrClass && attrClass.HasName(attributeName)) { attribute = attr; return true; } } return false; } internal static IEnumerable<AttributeData> GetAttributes(this ISymbol member, string attributeName) { foreach (var attr in member.GetAttributes()) { if (attr.AttributeClass is { } attrClass && attrClass.HasName(attributeName)) yield return attr; } } /// <summary> /// Gets DynamicallyAccessedMemberTypes for any DynamicallyAccessedMembers annotation on the symbol. /// This doesn't validate whether the annotation is valid based on the type of the annotated symbol. /// Use FlowAnnotations.Get*Annotation when getting annotations that are valid and should participate /// in dataflow analysis. /// </summary> internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypes(this ISymbol symbol) { if (!TryGetAttribute(symbol, DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out var dynamicallyAccessedMembers)) return DynamicallyAccessedMemberTypes.None; return (DynamicallyAccessedMemberTypes)dynamicallyAccessedMembers.ConstructorArguments[0].Value!; } internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesOnReturnType(this IMethodSymbol methodSymbol) { AttributeData? dynamicallyAccessedMembers = null; foreach (var returnTypeAttribute in methodSymbol.GetReturnTypeAttributes()) if (returnTypeAttribute.AttributeClass is var attrClass && attrClass != null && attrClass.HasName(DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute)) { dynamicallyAccessedMembers = returnTypeAttribute; break; } if (dynamicallyAccessedMembers == null) return DynamicallyAccessedMemberTypes.None; return (DynamicallyAccessedMemberTypes)dynamicallyAccessedMembers.ConstructorArguments[0].Value!; } internal static ValueSet<string> GetFeatureGuardAnnotations(this IPropertySymbol propertySymbol) { HashSet<string> featureSet = new(); foreach (var featureGuardAttribute in propertySymbol.GetAttributes(DynamicallyAccessedMembersAnalyzer.FullyQualifiedFeatureGuardAttribute)) { if (featureGuardAttribute.ConstructorArguments is [TypedConstant { Value: INamedTypeSymbol featureType }]) featureSet.Add(featureType.GetDisplayName()); } return featureSet.Count == 0 ? ValueSet<string>.Empty : new ValueSet<string>(featureSet); } internal static bool TryGetReturnAttribute(this IMethodSymbol member, string attributeName, [NotNullWhen(returnValue: true)] out AttributeData? attribute) { attribute = null; foreach (var attr in member.GetReturnTypeAttributes()) { if (attr.AttributeClass is { } attrClass && attrClass.HasName(attributeName)) { attribute = attr; return true; } } return false; } internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesOnAssociatedSymbol(this IMethodSymbol methodSymbol) => methodSymbol.AssociatedSymbol is ISymbol associatedSymbol ? GetDynamicallyAccessedMemberTypes(associatedSymbol) : DynamicallyAccessedMemberTypes.None; internal static bool TryGetOverriddenMember(this ISymbol? symbol, [NotNullWhen(returnValue: true)] out ISymbol? overriddenMember) { overriddenMember = symbol switch { IMethodSymbol method => method.OverriddenMethod, IPropertySymbol property => property.OverriddenProperty, IEventSymbol @event => @event.OverriddenEvent, _ => null, }; return overriddenMember != null; } public static SymbolDisplayFormat ILLinkTypeDisplayFormat { get; } = new SymbolDisplayFormat( typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters ); public static SymbolDisplayFormat ILLinkMemberDisplayFormat { get; } = new SymbolDisplayFormat( typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameOnly, genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters, memberOptions: SymbolDisplayMemberOptions.IncludeParameters | SymbolDisplayMemberOptions.IncludeExplicitInterface, parameterOptions: SymbolDisplayParameterOptions.IncludeType | SymbolDisplayParameterOptions.IncludeParamsRefOut ); public static string GetDisplayName(this ISymbol symbol) { var sb = new StringBuilder(); switch (symbol) { case IFieldSymbol fieldSymbol: sb.Append(fieldSymbol.ContainingSymbol.ToDisplayString(ILLinkTypeDisplayFormat)); sb.Append('.'); sb.Append(fieldSymbol.MetadataName); break; case IParameterSymbol parameterSymbol: sb.Append(parameterSymbol.Name); break; case IMethodSymbol methodSymbol when methodSymbol.IsStaticConstructor(): sb.Append(symbol.ContainingType.ToDisplayString(ILLinkTypeDisplayFormat)); sb.Append("..cctor()"); break; case IMethodSymbol methodSymbol: // Use definition type parameter names, not instance type parameters methodSymbol = methodSymbol.OriginalDefinition; // Format the declaring type with namespace and containing types. if (methodSymbol.ContainingSymbol?.Kind == SymbolKind.NamedType) { // If the containing symbol is a method (for example for local functions), // don't include the containing type's name. This matches the behavior of // CSharpErrorMessageFormat. sb.Append(methodSymbol.ContainingType.ToDisplayString(ILLinkTypeDisplayFormat)); sb.Append('.'); } // Format parameter types with only type names. sb.Append(methodSymbol.ToDisplayString(ILLinkMemberDisplayFormat)); break; default: sb.Append(symbol.ToDisplayString()); break; } return sb.ToString(); } public static bool IsInterface(this ISymbol symbol) { if (symbol is not INamedTypeSymbol namedTypeSymbol) return false; var typeSymbol = namedTypeSymbol as ITypeSymbol; return typeSymbol.TypeKind == TypeKind.Interface; } public static bool IsSubclassOf(this ISymbol symbol, string ns, string type) { if (symbol is not ITypeSymbol typeSymbol) return false; while (typeSymbol != null) { if (typeSymbol.IsTypeOf(ns, type)) return true; typeSymbol = typeSymbol.ContainingType; } return false; } public static bool IsConstructor([NotNullWhen(returnValue: true)] this ISymbol? symbol) => (symbol as IMethodSymbol)?.MethodKind is MethodKind.Constructor or MethodKind.StaticConstructor; public static bool IsStaticConstructor([NotNullWhen(returnValue: true)] this ISymbol? symbol) => (symbol as IMethodSymbol)?.MethodKind == MethodKind.StaticConstructor; public static bool IsEntryPoint(this IMethodSymbol methodSymbol, Compilation compilation) => methodSymbol.Name is WellKnownMemberNames.EntryPointMethodName or WellKnownMemberNames.TopLevelStatementsEntryPointMethodName && methodSymbol.IsStatic && (methodSymbol.ReturnsVoid || methodSymbol.ReturnType.SpecialType == SpecialType.System_Int32 || SymbolEqualityComparer.Default.Equals(methodSymbol.ReturnType.OriginalDefinition, compilation.TaskType()) || SymbolEqualityComparer.Default.Equals(methodSymbol.ReturnType.OriginalDefinition, compilation.TaskOfTType())); public static bool IsUnmanagedCallersOnlyEntryPoint(this IMethodSymbol methodSymbol) { foreach (var attr in methodSymbol.GetAttributes()) { if (attr.AttributeClass is { } attrClass && attrClass.HasName("System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute")) { foreach (var namedArgument in attr.NamedArguments) { if (namedArgument.Key == "EntryPoint") return true; } } } return false; } } }