diff --git a/src/tools/illink/src/ILLink.Shared/Annotations.cs b/src/tools/illink/src/ILLink.Shared/Annotations.cs index 3eaf8378cc00b5..97d244a712f24d 100644 --- a/src/tools/illink/src/ILLink.Shared/Annotations.cs +++ b/src/tools/illink/src/ILLink.Shared/Annotations.cs @@ -85,6 +85,15 @@ private static DynamicallyAccessedMemberTypes[] GetAllDynamicallyAccessedMemberT public static (DiagnosticId Id, string[] Arguments) GetDiagnosticForAnnotationMismatch (ValueWithDynamicallyAccessedMembers source, ValueWithDynamicallyAccessedMembers target, string missingAnnotations) { + source = source switch { + // FieldValue and MethodReturnValue have only one diagnostic argument, so formatting throws when the source + // is a NullableValueWithDynamicallyAccessedMembers. + // The correct behavior here is to unwrap always, as the underlying type is the one that has the annotations, + // but it is a breaking change for other UnderlyingTypeValues. + // https://github.com/dotnet/runtime/issues/93800 + NullableValueWithDynamicallyAccessedMembers { UnderlyingTypeValue: FieldValue or MethodReturnValue } nullable => nullable.UnderlyingTypeValue, + _ => source + }; DiagnosticId diagnosticId = (source, target) switch { (MethodParameterValue maybeThisSource, MethodParameterValue maybeThisTarget) when maybeThisSource.IsThisParameter () && maybeThisTarget.IsThisParameter () => DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsThisParameter, (MethodParameterValue maybeThis, MethodParameterValue) when maybeThis.IsThisParameter () => DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsParameter, diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/NullableAnnotations.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/NullableAnnotations.cs index 29b8bf650cbffa..b9f4d8fb9e2de0 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/NullableAnnotations.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/NullableAnnotations.cs @@ -65,6 +65,7 @@ public static void Main () GetUnderlyingTypeOnNonNullableKnownType.Test (); MakeGenericTypeWithUnknownValue (new object[2] { 1, 2 }); MakeGenericTypeWithKnowAndUnknownArray (); + RequiresOnNullableMakeGenericType.Test(); // Prevents optimizing away 'as Type' conversion. PreserveSystemType (); @@ -100,6 +101,68 @@ static void RequireAllFromMadeGenericNullableOfTypeWithMethodWithRuc () typeof (Nullable<>).MakeGenericType (typeof (TestStructWithRucMethod)).RequiresAll (); } + public class RequiresOnNullableMakeGenericType + { + [Kept] + static Type UnannotatedField; + [Kept] + [KeptAttributeAttribute(typeof(DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + static Type FieldWithMethods; + [Kept] + [UnexpectedWarning("IL2080", nameof(UnannotatedField), Tool.TrimmerAnalyzerAndNativeAot, "https://github.com/dotnet/runtime/issues/93800")] + static void Field() + { + typeof (Nullable<>).MakeGenericType (UnannotatedField).GetMethods (); + typeof (Nullable<>).MakeGenericType (FieldWithMethods).GetMethods (); + } + + [Kept] + [UnexpectedWarning("IL2090", nameof(unannotated), Tool.TrimmerAnalyzerAndNativeAot, "https://github.com/dotnet/runtime/issues/93800")] + static void Parameter( + Type unannotated, + [KeptAttributeAttribute(typeof(DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type annotated) + { + typeof (Nullable<>).MakeGenericType (unannotated).GetMethods (); + typeof (Nullable<>).MakeGenericType (annotated).GetMethods (); + } + + [Kept] + [ExpectedWarning("IL2090", "TUnannotated")] + static void TypeParameter< + TUnannotated, + [KeptAttributeAttribute(typeof(DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TAnnotated>() + { + typeof (Nullable<>).MakeGenericType (typeof(TUnannotated)).GetMethods (); + typeof (Nullable<>).MakeGenericType (typeof(TAnnotated)).GetMethods (); + } + + [Kept] + [UnexpectedWarning("IL2075", nameof(GetUnannotated), Tool.TrimmerAnalyzerAndNativeAot, "https://github.com/dotnet/runtime/issues/93800")] + static void ReturnValue() + { + typeof (Nullable<>).MakeGenericType (GetUnannotated()).GetMethods (); + typeof (Nullable<>).MakeGenericType (GetAnnotated()).GetMethods (); + } + [Kept] + static Type GetUnannotated() => null; + [Kept] + [return: KeptAttributeAttribute(typeof(DynamicallyAccessedMembersAttribute))] + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] + static Type GetAnnotated() => null; + + [Kept] + public static void Test() + { + Field(); + Parameter(null, null); + TypeParameter(); + ReturnValue(); + } + } + [Kept] static void TestRequireRucMethodThroughNullable () {