diff --git a/src/tools/illink/src/linker/Linker.Steps/ValidateVirtualMethodAnnotationsStep.cs b/src/tools/illink/src/linker/Linker.Steps/ValidateVirtualMethodAnnotationsStep.cs index 807a9b03b8a81..ac4b07e6681b4 100644 --- a/src/tools/illink/src/linker/Linker.Steps/ValidateVirtualMethodAnnotationsStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/ValidateVirtualMethodAnnotationsStep.cs @@ -40,10 +40,15 @@ protected override void Process () void ValidateMethodRequiresUnreferencedCodeAreSame (MethodDefinition method, MethodDefinition baseMethod) { var annotations = Context.Annotations; - bool methodHasAttribute = annotations.IsInRequiresUnreferencedCodeScope (method, out _); - if (methodHasAttribute != annotations.IsInRequiresUnreferencedCodeScope (baseMethod, out _)) { - string message = MessageFormat.FormatRequiresAttributeMismatch (methodHasAttribute, - baseMethod.DeclaringType.IsInterface, nameof (RequiresUnreferencedCodeAttribute), method.GetDisplayName (), baseMethod.GetDisplayName ()); + bool methodSatisfies = annotations.IsInRequiresUnreferencedCodeScope (method, out _); + bool baseRequires = annotations.DoesMethodRequireUnreferencedCode (baseMethod, out _); + if ((baseRequires && !methodSatisfies) || (!baseRequires && annotations.DoesMethodRequireUnreferencedCode (method, out _))) { + string message = MessageFormat.FormatRequiresAttributeMismatch ( + methodSatisfies, + baseMethod.DeclaringType.IsInterface, + nameof (RequiresUnreferencedCodeAttribute), + method.GetDisplayName (), + baseMethod.GetDisplayName ()); Context.LogWarning (method, DiagnosticId.RequiresUnreferencedCodeAttributeMismatch, message); } } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/VirtualMethodHierarchyDataflowAnnotationValidation.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/VirtualMethodHierarchyDataflowAnnotationValidation.cs index a7725afafac6c..4b73a66234791 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/VirtualMethodHierarchyDataflowAnnotationValidation.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/VirtualMethodHierarchyDataflowAnnotationValidation.cs @@ -50,6 +50,7 @@ public static void Main () StaticInterfaceMethods.Test (); BaseInPreservedScope.Test (); DirectCall.Test (); + RequiresAndDynamicallyAccessedMembersValidation.Test (); } static void RequirePublicMethods ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type type) @@ -1019,6 +1020,61 @@ public static void Test () CallStaticGvm (); } } + + class RequiresAndDynamicallyAccessedMembersValidation + { + // These tests have both DynamicallyAccessedMembers annotations and Requires annotations. + // This is to reproduce a bug where the virtual method annotations would be validated due to + // the presence of DynamicallyAccessedMembers, but the logic for checking Requires annotations + // was incorrect. The bug didn't manifest with just Requires annotations because the methods wouldn't + // be validated at all for Requires on type. + + class BaseMethodWithRequires + { + [RequiresUnreferencedCode (nameof (MethodWithRequires))] + [RequiresDynamicCode (nameof (MethodWithRequires))] + public virtual void MethodWithRequires ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type t) {} + } + + [RequiresUnreferencedCode (nameof (DerivedTypeWithRequires_BaseMethodWithRequires))] + [RequiresDynamicCode (nameof (DerivedTypeWithRequires_BaseMethodWithRequires))] + class DerivedTypeWithRequires_BaseMethodWithRequires : BaseMethodWithRequires + { + public override void MethodWithRequires ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type t) {} + } + + [ExpectedWarning ("IL2026", nameof (DerivedTypeWithRequires_BaseMethodWithRequires))] + [ExpectedWarning ("IL2026", nameof (DerivedTypeWithRequires_BaseMethodWithRequires.MethodWithRequires))] + [ExpectedWarning ("IL3050", nameof (DerivedTypeWithRequires_BaseMethodWithRequires), ProducedBy = Tool.NativeAot | Tool.Analyzer)] + [ExpectedWarning ("IL3050", nameof (DerivedTypeWithRequires_BaseMethodWithRequires.MethodWithRequires), ProducedBy = Tool.NativeAot | Tool.Analyzer)] + static void Test_DerivedTypeWithRequires_BaseMethodWithRequires () + { + new DerivedTypeWithRequires_BaseMethodWithRequires ().MethodWithRequires (typeof (int)); + } + + class BaseMethodWithoutRequires + { + public virtual void MethodWithoutRequires ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type t) {} + } + + [RequiresUnreferencedCode (nameof (DerivedTypeWithRequires_BaseMethodWithoutRequires))] + class DerivedTypeWithRequires_BaseMethodWithoutRequires : BaseMethodWithoutRequires + { + public override void MethodWithoutRequires ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type t) {} + } + + [ExpectedWarning ("IL2026", nameof (DerivedTypeWithRequires_BaseMethodWithoutRequires))] + static void Test_DerivedTypeWithRequires_BaseMethodWithoutRequires () + { + new DerivedTypeWithRequires_BaseMethodWithoutRequires ().MethodWithoutRequires (typeof (int)); + } + + public static void Test () + { + Test_DerivedTypeWithRequires_BaseMethodWithRequires (); + Test_DerivedTypeWithRequires_BaseMethodWithoutRequires (); + } + } } }