|
// 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;
using System.Collections.Immutable;
using System.Linq;
using ILLink.Shared;
using ILLink.Shared.TrimAnalysis;
using ILLink.Shared.TypeSystemProxy;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using MultiValue = ILLink.Shared.DataFlow.ValueSet<ILLink.Shared.DataFlow.SingleValue>;
namespace ILLink.RoslynAnalyzer
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class RequiresDynamicCodeAnalyzer : RequiresAnalyzerBase
{
private const string RequiresDynamicCodeAttribute = nameof(RequiresDynamicCodeAttribute);
public const string FullyQualifiedRequiresDynamicCodeAttribute = "System.Diagnostics.CodeAnalysis." + RequiresDynamicCodeAttribute;
private static readonly DiagnosticDescriptor s_requiresDynamicCodeOnStaticCtor = DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.RequiresDynamicCodeOnStaticConstructor);
private static readonly DiagnosticDescriptor s_requiresDynamicCodeOnEntryPoint = DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.RequiresDynamicCodeOnEntryPoint);
private static readonly DiagnosticDescriptor s_requiresDynamicCodeRule = DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.RequiresDynamicCode);
private static readonly DiagnosticDescriptor s_requiresDynamicCodeAttributeMismatch = DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.RequiresDynamicCodeAttributeMismatch);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
ImmutableArray.Create(s_requiresDynamicCodeRule, s_requiresDynamicCodeAttributeMismatch, s_requiresDynamicCodeOnStaticCtor, s_requiresDynamicCodeOnEntryPoint);
private protected override string RequiresAttributeName => RequiresDynamicCodeAttribute;
internal override string RequiresAttributeFullyQualifiedName => FullyQualifiedRequiresDynamicCodeAttribute;
private protected override DiagnosticTargets AnalyzerDiagnosticTargets => DiagnosticTargets.MethodOrConstructor | DiagnosticTargets.Class;
private protected override DiagnosticDescriptor RequiresDiagnosticRule => s_requiresDynamicCodeRule;
private protected override DiagnosticId RequiresDiagnosticId => DiagnosticId.RequiresDynamicCode;
private protected override DiagnosticDescriptor RequiresAttributeMismatch => s_requiresDynamicCodeAttributeMismatch;
private protected override DiagnosticDescriptor RequiresOnStaticCtor => s_requiresDynamicCodeOnStaticCtor;
private protected override DiagnosticDescriptor RequiresOnEntryPoint => s_requiresDynamicCodeOnEntryPoint;
internal override bool IsAnalyzerEnabled(AnalyzerOptions options) =>
options.IsMSBuildPropertyValueTrue(MSBuildPropertyOptionNames.EnableAotAnalyzer);
internal override bool IsIntrinsicallyHandled(IMethodSymbol calledMethod, MultiValue instance, ImmutableArray<MultiValue> arguments)
{
MethodProxy method = new(calledMethod);
var intrinsicId = Intrinsics.GetIntrinsicIdForMethod(method);
switch (intrinsicId)
{
case IntrinsicId.Type_MakeGenericType:
{
if (!instance.IsEmpty())
{
foreach (var value in instance.AsEnumerable())
{
if (value is SystemTypeValue typeValue)
{
if (!IsKnownInstantiation(arguments[0])
&& !IsConstrainedToBeReferenceTypes(typeValue.RepresentedType.GetGenericParameters()))
{
return false;
}
}
else
{
return false;
}
}
}
return true;
}
case IntrinsicId.MethodInfo_MakeGenericMethod:
{
if (!instance.IsEmpty())
{
foreach (var methodValue in instance.AsEnumerable())
{
if (methodValue is SystemReflectionMethodBaseValue methodBaseValue)
{
if (!IsKnownInstantiation(arguments[0])
&& !IsConstrainedToBeReferenceTypes(methodBaseValue.RepresentedMethod.GetGenericParameters()))
{
return false;
}
}
else
{
return false;
}
}
}
return true;
}
}
return false;
static bool IsKnownInstantiation(MultiValue genericParametersArray)
{
var typesValue = genericParametersArray.AsSingleValue();
if (typesValue is NullValue)
{
// This will fail at runtime but no warning needed
return true;
}
// Is this an array we model?
if (typesValue is not ArrayValue array)
{
return false;
}
int? size = array.Size.AsConstInt();
if (size == null)
{
return false;
}
for (int i = 0; i < size.Value; i++)
{
// Go over each element of the array. If the value is unknown, bail.
if (!array.TryGetValueByIndex(i, out MultiValue value))
{
return false;
}
var singleValue = value.AsSingleValue();
if (singleValue is not SystemTypeValue and not GenericParameterValue and not NullableSystemTypeValue)
{
return false;
}
}
return true;
}
static bool IsConstrainedToBeReferenceTypes(ImmutableArray<GenericParameterProxy> parameters)
{
foreach (GenericParameterProxy param in parameters)
if (!param.TypeParameterSymbol.HasReferenceTypeConstraint)
return false;
return true;
}
}
private protected override bool IsRequiresCheck(IPropertySymbol propertySymbol, Compilation compilation)
{
var runtimeFeaturesType = compilation.GetTypeByMetadataName("System.Runtime.CompilerServices.RuntimeFeature");
if (runtimeFeaturesType == null)
return false;
var isDynamicCodeSupportedProperty = runtimeFeaturesType.GetMembers("IsDynamicCodeSupported").OfType<IPropertySymbol>().FirstOrDefault();
if (isDynamicCodeSupportedProperty == null)
return false;
return SymbolEqualityComparer.Default.Equals(propertySymbol, isDynamicCodeSupportedProperty);
}
protected override bool VerifyAttributeArguments(AttributeData attribute) =>
attribute.ConstructorArguments.Length >= 1 && attribute.ConstructorArguments is [{ Type.SpecialType: SpecialType.System_String }, ..];
protected override string GetMessageFromAttribute(AttributeData? requiresAttribute)
{
var message = (string)requiresAttribute!.ConstructorArguments[0].Value!;
return MessageFormat.FormatRequiresAttributeMessageArg(message);
}
}
}
|