diff --git a/src/coreclr/tools/Common/Compiler/Logging/MessageOrigin.cs b/src/coreclr/tools/Common/Compiler/Logging/MessageOrigin.cs index 418f11f3f51a9..04ba20872a784 100644 --- a/src/coreclr/tools/Common/Compiler/Logging/MessageOrigin.cs +++ b/src/coreclr/tools/Common/Compiler/Logging/MessageOrigin.cs @@ -2,8 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; using System.Text; - +using Internal.IL; using Internal.TypeSystem; namespace ILCompiler.Logging @@ -35,6 +36,29 @@ public MessageOrigin(TypeSystemEntity memberDefinition, string fileName = null, SourceColumn = sourceColumn; } + public MessageOrigin(MethodIL origin, int ilOffset) + { + string document = null; + int? lineNumber = null; + + IEnumerable sequencePoints = origin.GetDebugInfo()?.GetSequencePoints(); + if (sequencePoints != null) + { + foreach (var sequencePoint in sequencePoints) + { + if (sequencePoint.Offset <= ilOffset) + { + document = sequencePoint.Document; + lineNumber = sequencePoint.LineNumber; + } + } + } + FileName = document; + MemberDefinition = origin.OwningMethod; + SourceLine = lineNumber; + SourceColumn = null; + } + public override string ToString() { if (FileName == null) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DiagnosticUtilities.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DiagnosticUtilities.cs index 219c7ed015706..fe2e79caec050 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DiagnosticUtilities.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DiagnosticUtilities.cs @@ -1,6 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Reflection.Metadata; +using ILCompiler.Logging; +using ILLink.Shared; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; @@ -44,40 +50,147 @@ internal static string GetGenericParameterDeclaringMemberDisplayName(GenericPara return ((TypeDesc)parent).GetDisplayName(); } - internal static string GetRequiresAttributeMessage(MethodDesc method, string requiresAttributeName) + internal static bool TryGetRequiresAttribute(TypeSystemEntity member, string requiresAttributeName, [NotNullWhen(returnValue: true)] out CustomAttributeValue? attribute) { - var ecmaMethod = method.GetTypicalMethodDefinition() as EcmaMethod; - if (ecmaMethod == null) - return null; + attribute = default; + CustomAttributeValue? decoded = default; + switch (member) + { + case MethodDesc method: + var ecmaMethod = method.GetTypicalMethodDefinition() as EcmaMethod; + if (ecmaMethod == null) + return false; + decoded = ecmaMethod.GetDecodedCustomAttribute("System.Diagnostics.CodeAnalysis", requiresAttributeName); + break; + case MetadataType type: + var ecmaType = type as EcmaType; + if (ecmaType == null) + return false; + decoded = ecmaType.GetDecodedCustomAttribute("System.Diagnostics.CodeAnalysis", requiresAttributeName); + break; + case PropertyPseudoDesc property: + decoded = property.GetDecodedCustomAttribute("System.Diagnostics.CodeAnalysis", requiresAttributeName); + break; + default: + Debug.Fail("Trying to operate with unsupported TypeSystemEntity " + member.GetType().ToString()); + break; + } + if (!decoded.HasValue) + return false; + + attribute = decoded.Value; + return true; + } - var decoded = ecmaMethod.GetDecodedCustomAttribute("System.Diagnostics.CodeAnalysis", requiresAttributeName); - if (decoded == null) + public static CustomAttributeValue? GetDecodedCustomAttribute(this PropertyPseudoDesc prop, string attributeNamespace, string attributeName) + { + var ecmaType = prop.OwningType as EcmaType; + var metadataReader = ecmaType.MetadataReader; + + var attributeHandle = metadataReader.GetCustomAttributeHandle(prop.GetCustomAttributes, + attributeNamespace, attributeName); + + if (attributeHandle.IsNil) return null; - var decodedValue = decoded.Value; + return metadataReader.GetCustomAttribute(attributeHandle).DecodeValue(new CustomAttributeTypeProvider(ecmaType.EcmaModule)); + } - if (decodedValue.FixedArguments.Length != 0) - return (string)decodedValue.FixedArguments[0].Value; + internal static string GetRequiresAttributeMessage(CustomAttributeValue attribute) + { + if (attribute.FixedArguments.Length != 0) + return (string)attribute.FixedArguments[0].Value; return null; } - internal static string GetRequiresAttributeUrl(MethodDesc method, string requiresAttributeName) + internal static string GetRequiresAttributeUrl(CustomAttributeValue attribute) { - var ecmaMethod = method.GetTypicalMethodDefinition() as EcmaMethod; - if (ecmaMethod == null) - return null; + if (attribute.NamedArguments.Length != 0 && attribute.NamedArguments[0].Name == "Url") + return (string)attribute.NamedArguments[0].Value; - var decoded = ecmaMethod.GetDecodedCustomAttribute("System.Diagnostics.CodeAnalysis", requiresAttributeName); - if (decoded == null) - return null; + return null; + } + + /// + /// Determines if method is within a declared Requires scope - this typically means that trim analysis + /// warnings should be suppressed in such a method. + /// + /// Unlike + /// if a declaring type has Requires, all methods in that type are considered "in scope" of that Requires. So this includes also + /// instance methods (not just statics and .ctors). + internal static bool IsInRequiresScope(this MethodDesc method, string requiresAttribute) => + method.IsInRequiresScope(requiresAttribute, true); + + /// + /// True if member of a call is considered to be annotated with the Requires... attribute. + /// Doesn't check the associated symbol for overrides and virtual methods because we should warn on mismatched between the property AND the accessors + /// + /// + /// MethodDesc that is either an overriding member or an overriden/virtual member + /// + internal static bool IsOverrideInRequiresScope(this MethodDesc method, string requiresAttribute) => + method.IsInRequiresScope(requiresAttribute, false); + + private static bool IsInRequiresScope(this MethodDesc method, string requiresAttribute, bool checkAssociatedSymbol) + { + if (method.HasCustomAttribute("System.Diagnostics.CodeAnalysis", requiresAttribute) && !method.IsStaticConstructor) + return true; - var decodedValue = decoded.Value; + if (method.OwningType is TypeDesc type && TryGetRequiresAttribute(type, requiresAttribute, out _)) + return true; - if (decodedValue.NamedArguments.Length != 0 && decodedValue.NamedArguments[0].Name == "Url") - return (string)decodedValue.NamedArguments[0].Value; + if (checkAssociatedSymbol && method.GetPropertyForAccessor() is PropertyPseudoDesc property && TryGetRequiresAttribute(property, requiresAttribute, out _)) + return true; - return null; + return false; + } + + internal static bool DoesMethodRequire(this MethodDesc method, string requiresAttribute, [NotNullWhen(returnValue: true)] out CustomAttributeValue? attribute) + { + attribute = null; + if (method.IsStaticConstructor) + return false; + + if (TryGetRequiresAttribute(method, requiresAttribute, out attribute)) + return true; + + if ((method.Signature.IsStatic || method.IsConstructor) && method.OwningType is TypeDesc owningType && + !owningType.IsArray && TryGetRequiresAttribute(owningType, requiresAttribute, out attribute)) + return true; + + return false; + } + + internal static bool DoesFieldRequire(this FieldDesc field, string requiresAttribute, [NotNullWhen(returnValue: true)] out CustomAttributeValue? attribute) + { + if (!field.IsStatic || field.OwningType is not TypeDesc owningType || owningType.IsArray) + { + attribute = null; + return false; + } + + return TryGetRequiresAttribute(field.OwningType, requiresAttribute, out attribute); + } + + internal static bool DoesPropertyRequire(this PropertyPseudoDesc property, string requiresAttribute, [NotNullWhen(returnValue: true)] out CustomAttributeValue? attribute) => + TryGetRequiresAttribute(property, requiresAttribute, out attribute); + + /// + /// Determines if member requires (and thus any usage of such method should be warned about). + /// + /// Unlike only static methods + /// and .ctors are reported as requires when the declaring type has Requires on it. + internal static bool DoesMemberRequire(this TypeSystemEntity member, string requiresAttribute, [NotNullWhen(returnValue: true)] out CustomAttributeValue? attribute) + { + attribute = null; + return member switch + { + MethodDesc method => DoesMethodRequire(method, requiresAttribute, out attribute), + FieldDesc field => DoesFieldRequire(field, requiresAttribute, out attribute), + PropertyPseudoDesc property => DoesPropertyRequire(property, requiresAttribute, out attribute), + _ => false + }; } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/EcmaExtensions.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/EcmaExtensions.cs index 48502b786adbd..a617257dde715 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/EcmaExtensions.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/EcmaExtensions.cs @@ -66,7 +66,8 @@ public static PropertyPseudoDesc GetProperty(this MetadataType mdType, string na public static PropertyPseudoDesc GetPropertyForAccessor(this MethodDesc accessor) { - var ecmaAccessor = (EcmaMethod)accessor.GetTypicalMethodDefinition(); + if (accessor.GetTypicalMethodDefinition() is not EcmaMethod ecmaAccessor) + return null; var type = (EcmaType)ecmaAccessor.OwningType; var reader = type.MetadataReader; var module = type.EcmaModule; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs index 420dc07dcfa75..c754c50ed99cf 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs @@ -7,8 +7,11 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Reflection.Metadata; using ILLink.Shared; +using ILCompiler.Logging; + using Internal.IL; using Internal.TypeSystem; @@ -28,14 +31,17 @@ class ReflectionMethodBodyScanner : MethodBodyScanner private readonly Logger _logger; private readonly NodeFactory _factory; private DependencyList _dependencies = new DependencyList(); + private const string RequiresUnreferencedCodeAttribute = nameof(RequiresUnreferencedCodeAttribute); + private const string RequiresDynamicCodeAttribute = nameof(RequiresDynamicCodeAttribute); + private const string RequiresAssemblyFilesAttribute = nameof(RequiresAssemblyFilesAttribute); public static bool RequiresReflectionMethodBodyScannerForCallSite(FlowAnnotations flowAnnotations, MethodDesc methodDefinition) { return GetIntrinsicIdForMethod(methodDefinition) > IntrinsicId.RequiresReflectionBodyScanner_Sentinel || flowAnnotations.RequiresDataflowAnalysis(methodDefinition) || - methodDefinition.HasCustomAttribute("System.Diagnostics.CodeAnalysis", "RequiresUnreferencedCodeAttribute") || - methodDefinition.HasCustomAttribute("System.Diagnostics.CodeAnalysis", "RequiresDynamicCodeAttribute") || + methodDefinition.DoesMethodRequire(RequiresUnreferencedCodeAttribute, out _) || + methodDefinition.DoesMethodRequire(RequiresDynamicCodeAttribute, out _) || methodDefinition.IsPInvoke; } @@ -48,30 +54,61 @@ public static bool RequiresReflectionMethodBodyScannerForMethodBody(FlowAnnotati public static bool RequiresReflectionMethodBodyScannerForAccess(FlowAnnotations flowAnnotations, FieldDesc fieldDefinition) { - return flowAnnotations.RequiresDataflowAnalysis(fieldDefinition); + return flowAnnotations.RequiresDataflowAnalysis(fieldDefinition) || + fieldDefinition.DoesFieldRequire(RequiresUnreferencedCodeAttribute, out _) || + fieldDefinition.DoesFieldRequire(RequiresDynamicCodeAttribute, out _); + } + + void CheckAndReportRequires(TypeSystemEntity calledMember, in MessageOrigin origin, string requiresAttributeName) + { + // If the caller of a method is already marked with `Requires` a new warning should not + // be produced for the callee. + if (ShouldSuppressAnalysisWarningsForRequires(origin.MemberDefinition, requiresAttributeName)) + return; + + if (!calledMember.DoesMemberRequire(requiresAttributeName, out var requiresAttribute)) + return; + + DiagnosticId diagnosticId = requiresAttributeName switch + { + RequiresUnreferencedCodeAttribute => DiagnosticId.RequiresUnreferencedCode, + RequiresDynamicCodeAttribute => DiagnosticId.RequiresDynamicCode, + RequiresAssemblyFilesAttribute => DiagnosticId.RequiresAssemblyFiles, + _ => throw new NotImplementedException($"{requiresAttributeName} is not a valid supported Requires attribute"), + }; + + ReportRequires(calledMember.GetDisplayName(), origin, diagnosticId, requiresAttribute); } - private bool ShouldEnablePatternReporting(MethodDesc method, string attributeName) + static bool ShouldSuppressAnalysisWarningsForRequires(TypeSystemEntity originMember, string requiresAttribute) { - if (method.HasCustomAttribute("System.Diagnostics.CodeAnalysis", attributeName)) + // Check if the current scope method has Requires on it + // since that attribute automatically suppresses all trim analysis warnings. + // Check both the immediate origin method as well as suppression context method + // since that will be different for compiler generated code. + if (originMember == null) + return false; + + if (originMember is not MethodDesc method) return false; + if (method.IsInRequiresScope(requiresAttribute)) + return true; + MethodDesc userMethod = ILCompiler.Logging.CompilerGeneratedState.GetUserDefinedMethodForCompilerGeneratedMember(method); if (userMethod != null && - userMethod.HasCustomAttribute("System.Diagnostics.CodeAnalysis", attributeName)) - return false; + userMethod.IsInRequiresScope(requiresAttribute)) + return true; - return true; + return false; } - bool ShouldEnableReflectionPatternReporting(MethodDesc method) + void ReportRequires(string displayName, in MessageOrigin currentOrigin, DiagnosticId diagnosticId, CustomAttributeValue? requiresAttribute) { - return ShouldEnablePatternReporting(method, "RequiresUnreferencedCodeAttribute"); - } + string arg1 = MessageFormat.FormatRequiresAttributeMessageArg(DiagnosticUtilities.GetRequiresAttributeMessage((CustomAttributeValue)requiresAttribute)); + string arg2 = MessageFormat.FormatRequiresAttributeUrlArg(DiagnosticUtilities.GetRequiresAttributeUrl((CustomAttributeValue)requiresAttribute)); - bool ShouldEnableAotPatternReporting(MethodDesc method) - { - return ShouldEnablePatternReporting(method, "RequiresDynamicCodeAttribute"); + _logger.LogWarning(currentOrigin, diagnosticId, displayName, arg1, arg2); } private enum ScanningPurpose @@ -123,7 +160,11 @@ public static DependencyList ScanAndProcessReturnValue(NodeFactory factory, Flow if (requiredMemberTypes != 0) { var targetContext = new MethodReturnOrigin(method); - var reflectionContext = new ReflectionPatternContext(scanner._logger, scanner.ShouldEnableReflectionPatternReporting(method), method, targetContext); + bool shouldEnableReflectionWarnings = !ShouldSuppressAnalysisWarningsForRequires(method, RequiresUnreferencedCodeAttribute); + var reflectionContext = new ReflectionPatternContext(scanner._logger, + shouldEnableReflectionWarnings, + method, + targetContext); reflectionContext.AnalyzingPattern(); scanner.RequireDynamicallyAccessedMembers(ref reflectionContext, requiredMemberTypes, scanner.MethodReturnValue, targetContext); reflectionContext.Dispose(); @@ -322,11 +363,14 @@ protected override void HandleStoreField(MethodIL methodBody, int offset, FieldD if (requiredMemberTypes != 0) { var origin = new FieldOrigin(field); - var reflectionContext = new ReflectionPatternContext(_logger, ShouldEnableReflectionPatternReporting(methodBody.OwningMethod), methodBody, offset, origin); + bool shouldEnableReflectionWarnings = !ShouldSuppressAnalysisWarningsForRequires(methodBody.OwningMethod, RequiresUnreferencedCodeAttribute); + var reflectionContext = new ReflectionPatternContext(_logger, shouldEnableReflectionWarnings, methodBody, offset, origin); reflectionContext.AnalyzingPattern(); RequireDynamicallyAccessedMembers(ref reflectionContext, requiredMemberTypes, valueToStore, origin); reflectionContext.Dispose(); } + CheckAndReportRequires(field, new MessageOrigin(methodBody.OwningMethod), RequiresUnreferencedCodeAttribute); + CheckAndReportRequires(field, new MessageOrigin(methodBody.OwningMethod), RequiresDynamicCodeAttribute); } protected override void HandleStoreParameter(MethodIL method, int offset, int index, ValueNode valueToStore) @@ -335,7 +379,8 @@ protected override void HandleStoreParameter(MethodIL method, int offset, int in if (requiredMemberTypes != 0) { Origin parameter = DiagnosticUtilities.GetMethodParameterFromIndex(method.OwningMethod, index); - var reflectionContext = new ReflectionPatternContext(_logger, ShouldEnableReflectionPatternReporting(method.OwningMethod), method, offset, parameter); + bool shouldEnableReflectionWarnings = !ShouldSuppressAnalysisWarningsForRequires(method.OwningMethod, RequiresUnreferencedCodeAttribute); + var reflectionContext = new ReflectionPatternContext(_logger, shouldEnableReflectionWarnings, method, offset, parameter); reflectionContext.AnalyzingPattern(); RequireDynamicallyAccessedMembers(ref reflectionContext, requiredMemberTypes, valueToStore, parameter); reflectionContext.Dispose(); @@ -775,8 +820,7 @@ public override bool HandleCall(MethodIL callingMethodBody, MethodDesc calledMet methodReturnValue = null; var callingMethodDefinition = callingMethodBody.OwningMethod; - bool shouldEnableReflectionWarnings = ShouldEnableReflectionPatternReporting(callingMethodDefinition); - bool shouldEnableAotWarnings = ShouldEnableAotPatternReporting(callingMethodDefinition); + bool shouldEnableReflectionWarnings = !ShouldSuppressAnalysisWarningsForRequires(callingMethodDefinition, RequiresUnreferencedCodeAttribute); var reflectionContext = new ReflectionPatternContext(_logger, shouldEnableReflectionWarnings, callingMethodBody, offset, new MethodOrigin(calledMethod)); DynamicallyAccessedMemberTypes returnValueDynamicallyAccessedMemberTypes = 0; @@ -917,8 +961,7 @@ public override bool HandleCall(MethodIL callingMethodBody, MethodDesc calledMet } } - if (shouldEnableAotWarnings) - LogDynamicCodeWarning(_logger, callingMethodBody, offset, calledMethod); + CheckAndReportRequires(calledMethod, new MessageOrigin(callingMethodBody, offset), RequiresDynamicCodeAttribute); // We don't want to lose track of the type // in case this is e.g. Activator.CreateInstance(typeof(Foo<>).MakeGenericType(...)); @@ -1194,10 +1237,8 @@ public override bool HandleCall(MethodIL callingMethodBody, MethodDesc calledMet _dependencies.Add(_factory.ConstructedTypeSymbol(systemTypeValue.TypeRepresented.MakeArrayType()), "Enum.GetValues"); } } - else if (shouldEnableAotWarnings) - { - LogDynamicCodeWarning(_logger, callingMethodBody, offset, calledMethod); - } + else + CheckAndReportRequires(calledMethod, new MessageOrigin(callingMethodBody, offset),RequiresDynamicCodeAttribute); } } break; @@ -1231,10 +1272,8 @@ public override bool HandleCall(MethodIL callingMethodBody, MethodDesc calledMet _dependencies.Add(_factory.StructMarshallingData((DefType)systemTypeValue.TypeRepresented), "Marshal API"); } } - else if (shouldEnableAotWarnings) - { - LogDynamicCodeWarning(_logger, callingMethodBody, offset, calledMethod); - } + else + CheckAndReportRequires(calledMethod, new MessageOrigin(callingMethodBody, offset), RequiresDynamicCodeAttribute); } } break; @@ -1258,10 +1297,8 @@ public override bool HandleCall(MethodIL callingMethodBody, MethodDesc calledMet _dependencies.Add(_factory.DelegateMarshallingData((DefType)systemTypeValue.TypeRepresented), "Marshal API"); } } - else if (shouldEnableAotWarnings) - { - LogDynamicCodeWarning(_logger, callingMethodBody, offset, calledMethod); - } + else + CheckAndReportRequires(calledMethod, new MessageOrigin(callingMethodBody, offset), RequiresDynamicCodeAttribute); } } break; @@ -2170,8 +2207,7 @@ public override bool HandleCall(MethodIL callingMethodBody, MethodDesc calledMet // MakeGenericMethod doesn't change the identity of the MethodBase we're tracking so propagate to the return value methodReturnValue = methodParams[0]; - if (shouldEnableAotWarnings) - LogDynamicCodeWarning(_logger, callingMethodBody, offset, calledMethod); + CheckAndReportRequires(calledMethod, new MessageOrigin(callingMethodBody, offset), RequiresDynamicCodeAttribute); } break; @@ -2221,28 +2257,10 @@ public override bool HandleCall(MethodIL callingMethodBody, MethodDesc calledMet reflectionContext.RecordHandledPattern(); } - if (shouldEnableReflectionWarnings && - calledMethod.HasCustomAttribute("System.Diagnostics.CodeAnalysis", "RequiresUnreferencedCodeAttribute")) - { - string arg1 = MessageFormat.FormatRequiresAttributeMessageArg(DiagnosticUtilities.GetRequiresAttributeMessage(calledMethod, "RequiresUnreferencedCodeAttribute")); - string arg2 = MessageFormat.FormatRequiresAttributeUrlArg(DiagnosticUtilities.GetRequiresAttributeUrl(calledMethod, "RequiresUnreferencedCodeAttribute")); - - _logger.LogWarning(callingMethodBody, offset, DiagnosticId.RequiresUnreferencedCode, calledMethod.GetDisplayName(), arg1, arg2); - } - - if (shouldEnableAotWarnings && - calledMethod.HasCustomAttribute("System.Diagnostics.CodeAnalysis", "RequiresDynamicCodeAttribute")) - { - LogDynamicCodeWarning(_logger, callingMethodBody, offset, calledMethod); - } - - static void LogDynamicCodeWarning(Logger logger, MethodIL callingMethodBody, int offset, MethodDesc calledMethod) - { - string arg1 = MessageFormat.FormatRequiresAttributeMessageArg(DiagnosticUtilities.GetRequiresAttributeMessage(calledMethod, "RequiresDynamicCodeAttribute")); - string arg2 = MessageFormat.FormatRequiresAttributeUrlArg(DiagnosticUtilities.GetRequiresAttributeUrl(calledMethod, "RequiresDynamicCodeAttribute")); - - logger.LogWarning(callingMethodBody, offset, DiagnosticId.RequiresDynamicCode, calledMethod.GetDisplayName(), arg1, arg2); - } + var origin = new MessageOrigin(callingMethodBody, offset); + CheckAndReportRequires(calledMethod, origin, RequiresUnreferencedCodeAttribute); + CheckAndReportRequires(calledMethod, origin, RequiresDynamicCodeAttribute); + CheckAndReportRequires(calledMethod, origin, RequiresAssemblyFilesAttribute); // To get good reporting of errors we need to track the origin of the value for all method calls // but except Newobj as those are special. @@ -2951,7 +2969,7 @@ void WarnOnReflectionAccess(ref ReflectionPatternContext context, TypeSystemEnti void MarkMethod(ref ReflectionPatternContext reflectionContext, MethodDesc method) { - if (method.HasCustomAttribute("System.Diagnostics.CodeAnalysis", "RequiresUnreferencedCodeAttribute")) + if(method.DoesMethodRequire(RequiresUnreferencedCodeAttribute, out _)) { if (_purpose == ScanningPurpose.GetTypeDataflow) { @@ -2960,7 +2978,7 @@ void MarkMethod(ref ReflectionPatternContext reflectionContext, MethodDesc metho } } - if (_flowAnnotations.ShouldWarnWhenAccessedForReflection(method)) + if (_flowAnnotations.ShouldWarnWhenAccessedForReflection(method) && !ShouldSuppressAnalysisWarningsForRequires(method, RequiresUnreferencedCodeAttribute)) { WarnOnReflectionAccess(ref reflectionContext, method); } @@ -2971,7 +2989,7 @@ void MarkMethod(ref ReflectionPatternContext reflectionContext, MethodDesc metho void MarkField(ref ReflectionPatternContext reflectionContext, FieldDesc field) { - if (_flowAnnotations.ShouldWarnWhenAccessedForReflection(field)) + if (_flowAnnotations.ShouldWarnWhenAccessedForReflection(field) && !ShouldSuppressAnalysisWarningsForRequires(reflectionContext.Source, RequiresUnreferencedCodeAttribute)) { WarnOnReflectionAccess(ref reflectionContext, field); } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/PropertyPseudoDesc.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/PropertyPseudoDesc.cs index 3113c629926c1..0ac2d9ed7d5a5 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/PropertyPseudoDesc.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/PropertyPseudoDesc.cs @@ -42,6 +42,14 @@ public MethodDesc SetMethod } } + public CustomAttributeHandleCollection GetCustomAttributes + { + get + { + return Definition.GetCustomAttributes(); + } + } + public MetadataType OwningType { get diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index 70dc007f78a97..a55507fc53089 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -27,6 +27,7 @@ using CustomAttributeHandle = System.Reflection.Metadata.CustomAttributeHandle; using CustomAttributeTypeProvider = Internal.TypeSystem.Ecma.CustomAttributeTypeProvider; using MetadataExtensions = Internal.TypeSystem.Ecma.MetadataExtensions; +using ILCompiler.Dataflow; namespace ILCompiler { @@ -671,32 +672,33 @@ public bool GeneratesAttributeMetadata(TypeDesc attributeType) public override void NoteOverridingMethod(MethodDesc baseMethod, MethodDesc overridingMethod) { // We validate that the various dataflow/Requires* annotations are consistent across virtual method overrides - - bool baseMethodRequiresUnreferencedCode = baseMethod.HasCustomAttribute("System.Diagnostics.CodeAnalysis", "RequiresUnreferencedCodeAttribute"); - bool overridingMethodRequiresUnreferencedCode = overridingMethod.HasCustomAttribute("System.Diagnostics.CodeAnalysis", "RequiresUnreferencedCodeAttribute"); - - bool baseMethodRequiresDynamicCode = baseMethod.HasCustomAttribute("System.Diagnostics.CodeAnalysis", "RequiresDynamicCodeAttribute"); - bool overridingMethodRequiresDynamicCode = overridingMethod.HasCustomAttribute("System.Diagnostics.CodeAnalysis", "RequiresDynamicCodeAttribute"); - - bool baseMethodRequiresDataflow = FlowAnnotations.RequiresDataflowAnalysis(baseMethod); - bool overridingMethodRequiresDataflow = FlowAnnotations.RequiresDataflowAnalysis(overridingMethod); - - if (baseMethodRequiresUnreferencedCode != overridingMethodRequiresUnreferencedCode) + if (HasMismatchingAttributes(baseMethod, overridingMethod, "RequiresUnreferencedCodeAttribute")) { Logger.LogWarning(overridingMethod, DiagnosticId.RequiresUnreferencedCodeAttributeMismatch, overridingMethod.GetDisplayName(), baseMethod.GetDisplayName()); } - if (baseMethodRequiresDynamicCode != overridingMethodRequiresDynamicCode) + if (HasMismatchingAttributes(baseMethod, overridingMethod, "RequiresDynamicCodeAttribute")) { Logger.LogWarning(overridingMethod, DiagnosticId.RequiresDynamicCodeAttributeMismatch, overridingMethod.GetDisplayName(), baseMethod.GetDisplayName()); } + bool baseMethodRequiresDataflow = FlowAnnotations.RequiresDataflowAnalysis(baseMethod); + bool overridingMethodRequiresDataflow = FlowAnnotations.RequiresDataflowAnalysis(overridingMethod); if (baseMethodRequiresDataflow || overridingMethodRequiresDataflow) { FlowAnnotations.ValidateMethodAnnotationsAreSame(overridingMethod, baseMethod); } } + public static bool HasMismatchingAttributes (MethodDesc baseMethod, MethodDesc overridingMethod, string requiresAttributeName) + { + bool baseMethodCreatesRequirement = baseMethod.DoesMethodRequire(requiresAttributeName, out _); + bool overridingMethodCreatesRequirement = overridingMethod.DoesMethodRequire(requiresAttributeName, out _); + bool baseMethodFulfillsRequirement = baseMethod.IsOverrideInRequiresScope(requiresAttributeName); + bool overridingMethodFulfillsRequirement = overridingMethod.IsOverrideInRequiresScope(requiresAttributeName); + return (baseMethodCreatesRequirement && !overridingMethodFulfillsRequirement) || (overridingMethodCreatesRequirement && !baseMethodFulfillsRequirement); + } + public MetadataManager ToAnalysisBasedMetadataManager() { var reflectableTypes = ReflectableEntityBuilder.Create(); diff --git a/src/coreclr/tools/aot/ILLink.Shared/DiagnosticId.cs b/src/coreclr/tools/aot/ILLink.Shared/DiagnosticId.cs index 5ec2e6ec7c554..e22574dbd335c 100644 --- a/src/coreclr/tools/aot/ILLink.Shared/DiagnosticId.cs +++ b/src/coreclr/tools/aot/ILLink.Shared/DiagnosticId.cs @@ -187,6 +187,7 @@ public enum DiagnosticId AssemblyProducedAOTWarnings = 3053, GenericRecursionCycle = 3054, CorrectnessOfAbstractDelegatesCannotBeGuaranteed = 3055, + RequiresDynamicCodeOnStaticConstructor = 3056, } public static class DiagnosticIdExtensions