| File: RequiresDynamicCodeAnalyzer.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; 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); private static readonly DiagnosticDescriptor s_referenceNotMarkedIsAotCompatibleRule = DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.ReferenceNotMarkedIsAotCompatible); public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(s_requiresDynamicCodeRule, s_requiresDynamicCodeAttributeMismatch, s_requiresDynamicCodeOnStaticCtor, s_requiresDynamicCodeOnEntryPoint, s_referenceNotMarkedIsAotCompatibleRule); 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; } case IntrinsicId.Enum_GetValues: { foreach (var value in arguments[0].AsEnumerable()) { if (value is not SystemTypeValue) { 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); } private protected override ImmutableArray<Action<CompilationAnalysisContext>> ExtraCompilationActions => ImmutableArray.Create<Action<CompilationAnalysisContext>>((context) => { CheckReferencedAssemblies( context, MSBuildPropertyOptionNames.VerifyReferenceAotCompatibility, "IsAotCompatible", s_referenceNotMarkedIsAotCompatibleRule); }); 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); } } }