|
// 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;
}
}
}
|