Skip to content

Commit 1fb6148

Browse files
authored
Unwrap NullableValue for diagnostics creation and add try/catch for formatting diagnostics (#110229)
Fixes #109536 See #93800 for more context. When a NullableValue is the source of an assignment, we consider use the TypeArgumentTargetsX diagnostic since the underlying value is a generic argument, but use the source of the generic argument for diagnostics. This leads to some confusing warning messages, and sometimes crashes in formatting the diagnostics. Ideally, we would want to unwrap the NullableValues and use the UnderlyingTypeValue as the source, but this would be a breaking change for the situations that don't crash. FieldValue and MethodReturnValue only provide 1 diagnostic arguments, but the diagnostics for TypeArgumentTargetsX expects 2 arguments from the source. This caused crashes when the situation was encountered. Since they didn't work before at all, this won't be a breaking change to provide the correct warning here.
1 parent 5824c47 commit 1fb6148

File tree

2 files changed

+72
-0
lines changed

2 files changed

+72
-0
lines changed

src/tools/illink/src/ILLink.Shared/Annotations.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,15 @@ private static DynamicallyAccessedMemberTypes[] GetAllDynamicallyAccessedMemberT
8585

8686
public static (DiagnosticId Id, string[] Arguments) GetDiagnosticForAnnotationMismatch (ValueWithDynamicallyAccessedMembers source, ValueWithDynamicallyAccessedMembers target, string missingAnnotations)
8787
{
88+
source = source switch {
89+
// FieldValue and MethodReturnValue have only one diagnostic argument, so formatting throws when the source
90+
// is a NullableValueWithDynamicallyAccessedMembers.
91+
// The correct behavior here is to unwrap always, as the underlying type is the one that has the annotations,
92+
// but it is a breaking change for other UnderlyingTypeValues.
93+
// https://github.com/dotnet/runtime/issues/93800
94+
NullableValueWithDynamicallyAccessedMembers { UnderlyingTypeValue: FieldValue or MethodReturnValue } nullable => nullable.UnderlyingTypeValue,
95+
_ => source
96+
};
8897
DiagnosticId diagnosticId = (source, target) switch {
8998
(MethodParameterValue maybeThisSource, MethodParameterValue maybeThisTarget) when maybeThisSource.IsThisParameter () && maybeThisTarget.IsThisParameter () => DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsThisParameter,
9099
(MethodParameterValue maybeThis, MethodParameterValue) when maybeThis.IsThisParameter () => DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsParameter,

src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/NullableAnnotations.cs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ public static void Main ()
6565
GetUnderlyingTypeOnNonNullableKnownType.Test ();
6666
MakeGenericTypeWithUnknownValue (new object[2] { 1, 2 });
6767
MakeGenericTypeWithKnowAndUnknownArray ();
68+
RequiresOnNullableMakeGenericType.Test();
6869

6970
// Prevents optimizing away 'as Type' conversion.
7071
PreserveSystemType ();
@@ -100,6 +101,68 @@ static void RequireAllFromMadeGenericNullableOfTypeWithMethodWithRuc ()
100101
typeof (Nullable<>).MakeGenericType (typeof (TestStructWithRucMethod)).RequiresAll ();
101102
}
102103

104+
public class RequiresOnNullableMakeGenericType
105+
{
106+
[Kept]
107+
static Type UnannotatedField;
108+
[Kept]
109+
[KeptAttributeAttribute(typeof(DynamicallyAccessedMembersAttribute))]
110+
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)]
111+
static Type FieldWithMethods;
112+
[Kept]
113+
[UnexpectedWarning("IL2080", nameof(UnannotatedField), Tool.TrimmerAnalyzerAndNativeAot, "https://github.com/dotnet/runtime/issues/93800")]
114+
static void Field()
115+
{
116+
typeof (Nullable<>).MakeGenericType (UnannotatedField).GetMethods ();
117+
typeof (Nullable<>).MakeGenericType (FieldWithMethods).GetMethods ();
118+
}
119+
120+
[Kept]
121+
[UnexpectedWarning("IL2090", nameof(unannotated), Tool.TrimmerAnalyzerAndNativeAot, "https://github.com/dotnet/runtime/issues/93800")]
122+
static void Parameter(
123+
Type unannotated,
124+
[KeptAttributeAttribute(typeof(DynamicallyAccessedMembersAttribute))]
125+
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type annotated)
126+
{
127+
typeof (Nullable<>).MakeGenericType (unannotated).GetMethods ();
128+
typeof (Nullable<>).MakeGenericType (annotated).GetMethods ();
129+
}
130+
131+
[Kept]
132+
[ExpectedWarning("IL2090", "TUnannotated")]
133+
static void TypeParameter<
134+
TUnannotated,
135+
[KeptAttributeAttribute(typeof(DynamicallyAccessedMembersAttribute))]
136+
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TAnnotated>()
137+
{
138+
typeof (Nullable<>).MakeGenericType (typeof(TUnannotated)).GetMethods ();
139+
typeof (Nullable<>).MakeGenericType (typeof(TAnnotated)).GetMethods ();
140+
}
141+
142+
[Kept]
143+
[UnexpectedWarning("IL2075", nameof(GetUnannotated), Tool.TrimmerAnalyzerAndNativeAot, "https://github.com/dotnet/runtime/issues/93800")]
144+
static void ReturnValue()
145+
{
146+
typeof (Nullable<>).MakeGenericType (GetUnannotated()).GetMethods ();
147+
typeof (Nullable<>).MakeGenericType (GetAnnotated()).GetMethods ();
148+
}
149+
[Kept]
150+
static Type GetUnannotated() => null;
151+
[Kept]
152+
[return: KeptAttributeAttribute(typeof(DynamicallyAccessedMembersAttribute))]
153+
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]
154+
static Type GetAnnotated() => null;
155+
156+
[Kept]
157+
public static void Test()
158+
{
159+
Field();
160+
Parameter(null, null);
161+
TypeParameter<object, object>();
162+
ReturnValue();
163+
}
164+
}
165+
103166
[Kept]
104167
static void TestRequireRucMethodThroughNullable ()
105168
{

0 commit comments

Comments
 (0)