From ec1cde128cebef9176d42e55bbca762498f6b2c2 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Thu, 24 Feb 2022 22:41:38 +0000 Subject: [PATCH 1/5] Fix behavior of intrinsics with empty inputs --- .../TrimAnalysis/HandleCallAction.cs | 22 ++++++++++++++--- .../DataFlow/AssemblyQualifiedNameDataflow.cs | 24 +++++++++++++++++++ .../DataFlow/GetInterfaceDataFlow.cs | 9 +++++++ .../DataFlow/TypeHandleDataFlow.cs | 9 +++++++ 4 files changed, 61 insertions(+), 3 deletions(-) diff --git a/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs b/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs index ee8c257e0c93..bcb780427574 100644 --- a/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs +++ b/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs @@ -72,6 +72,11 @@ public bool Invoke (MethodProxy calledMethod, MultiValue instanceValue, IReadOnl break; case IntrinsicId.Type_get_TypeHandle: + if (instanceValue.IsEmpty ()) { + returnValue = MultiValueLattice.Top; + break; + } + foreach (var value in instanceValue) { if (value is SystemTypeValue typeValue) AddReturnValue (new RuntimeTypeHandleValue (typeValue.RepresentedType)); @@ -90,6 +95,11 @@ public bool Invoke (MethodProxy calledMethod, MultiValue instanceValue, IReadOnl // GetInterface (String, bool) // case IntrinsicId.Type_GetInterface: { + if (instanceValue.IsEmpty ()) { + returnValue = MultiValueLattice.Top; + break; + } + var targetValue = GetMethodThisParameterValue (calledMethod, DynamicallyAccessedMemberTypesOverlay.Interfaces); foreach (var value in instanceValue) { // For now no support for marking a single interface by name. We would have to correctly support @@ -115,6 +125,11 @@ public bool Invoke (MethodProxy calledMethod, MultiValue instanceValue, IReadOnl // AssemblyQualifiedName // case IntrinsicId.Type_get_AssemblyQualifiedName: { + if (instanceValue.IsEmpty ()) { + returnValue = MultiValueLattice.Top; + break; + } + foreach (var value in instanceValue) { if (value is ValueWithDynamicallyAccessedMembers valueWithDynamicallyAccessedMembers) { // Currently we don't need to track the difference between Type and String annotated values @@ -159,9 +174,10 @@ public bool Invoke (MethodProxy calledMethod, MultiValue instanceValue, IReadOnl return true; } - // For some intrinsics we just use the return value from the annotations. - if (returnValue == null) - returnValue = calledMethod.ReturnsVoid () ? MultiValueLattice.Top : GetMethodReturnValue (calledMethod, returnValueDynamicallyAccessedMemberTypes); + if (returnValue == null && !calledMethod.ReturnsVoid ()) + throw new InvalidOperationException ("No return value set for intrinsic"); + + returnValue ??= MultiValueLattice.Top; // Validate that the return value has the correct annotations as per the method return value annotations if (returnValueDynamicallyAccessedMemberTypes != 0) { diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/AssemblyQualifiedNameDataflow.cs b/test/Mono.Linker.Tests.Cases/DataFlow/AssemblyQualifiedNameDataflow.cs index 924cd2391169..7ee0b4279bd3 100644 --- a/test/Mono.Linker.Tests.Cases/DataFlow/AssemblyQualifiedNameDataflow.cs +++ b/test/Mono.Linker.Tests.Cases/DataFlow/AssemblyQualifiedNameDataflow.cs @@ -21,6 +21,8 @@ static void Main () TestNull (); TestMultipleValues (); TestUnknownValue (); + TestNoValue (); + TestObjectGetTypeValue (); } [ExpectedWarning ("IL2072", nameof (RequirePublicConstructors))] @@ -92,6 +94,28 @@ static void TestUnknownValue (object[] o = null) RequireNothing (unknown); // shouldn't warn } + static void TestNoValue () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + // t.TypeHandle throws at runtime so don't warn here. + RequirePublicConstructors (noValue.AssemblyQualifiedName); + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] + class AnnotatedType + { + } + + static void TestObjectGetTypeValue (AnnotatedType instance = null) + { + string type = instance.GetType().AssemblyQualifiedName; + // Currently Object.GetType is unimplemented in the analyzer, but + // this still shouldn't warn. + RequirePublicConstructors (type); + RequireNothing (type); + } + private static void RequirePublicParameterlessConstructor ( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string type) diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/GetInterfaceDataFlow.cs b/test/Mono.Linker.Tests.Cases/DataFlow/GetInterfaceDataFlow.cs index 28ad0c355494..7238a50f3ee0 100644 --- a/test/Mono.Linker.Tests.Cases/DataFlow/GetInterfaceDataFlow.cs +++ b/test/Mono.Linker.Tests.Cases/DataFlow/GetInterfaceDataFlow.cs @@ -76,6 +76,14 @@ static void TestMergedValues (int p, [DynamicallyAccessedMembers (DynamicallyAcc type.GetInterface ("ITestInterface").RequiresInterfaces (); } + static void TestNoValue () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + // t.TypeHandle throws at runtime so don't warn here. + noValue.GetInterface ("ITestInterface").RequiresAll (); + } + class GetInterfaceInCtor { public GetInterfaceInCtor ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.Interfaces)] Type type) @@ -93,6 +101,7 @@ public static void Test () TestKnownType (); TestMultipleValues (0, typeof (TestType)); TestMergedValues (0, typeof (TestType)); + TestNoValue (); var _ = new GetInterfaceInCtor (typeof (TestType)); } } diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/TypeHandleDataFlow.cs b/test/Mono.Linker.Tests.Cases/DataFlow/TypeHandleDataFlow.cs index 507f532e5b6b..8c57eb4f4f4b 100644 --- a/test/Mono.Linker.Tests.Cases/DataFlow/TypeHandleDataFlow.cs +++ b/test/Mono.Linker.Tests.Cases/DataFlow/TypeHandleDataFlow.cs @@ -21,6 +21,7 @@ public static void Main () TestGetTypeHandleFromGeneric (); TestUnsupportedPatterns (typeof (TestType)); TestNull (); + TestNoValue (); } [ExpectedWarning ("IL2026", "--" + nameof (TestTypeWithRUCOnMembers.PublicMethodWithRUC) + "--")] @@ -72,6 +73,14 @@ static void TestNull () Type.GetTypeFromHandle (type.TypeHandle).RequiresPublicMethods (); } + static void TestNoValue () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + // t.TypeHandle throws at runtime so don't warn here. + Type.GetTypeFromHandle (noValue.TypeHandle).RequiresPublicMethods (); + } + class TestType { } class TestTypeWithRUCOnMembers From 2e5a8073c3103d26b519ecde3b307fc093d5f7a0 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Thu, 24 Feb 2022 22:51:28 +0000 Subject: [PATCH 2/5] Fix formatting --- .../DataFlow/AssemblyQualifiedNameDataflow.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/AssemblyQualifiedNameDataflow.cs b/test/Mono.Linker.Tests.Cases/DataFlow/AssemblyQualifiedNameDataflow.cs index 7ee0b4279bd3..3b1e7b81f80e 100644 --- a/test/Mono.Linker.Tests.Cases/DataFlow/AssemblyQualifiedNameDataflow.cs +++ b/test/Mono.Linker.Tests.Cases/DataFlow/AssemblyQualifiedNameDataflow.cs @@ -100,16 +100,16 @@ static void TestNoValue () Type noValue = Type.GetTypeFromHandle (t.TypeHandle); // t.TypeHandle throws at runtime so don't warn here. RequirePublicConstructors (noValue.AssemblyQualifiedName); - } + } - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors)] class AnnotatedType { } static void TestObjectGetTypeValue (AnnotatedType instance = null) { - string type = instance.GetType().AssemblyQualifiedName; + string type = instance.GetType ().AssemblyQualifiedName; // Currently Object.GetType is unimplemented in the analyzer, but // this still shouldn't warn. RequirePublicConstructors (type); From ad184a55800eed2295878c59736b238bd585c0a5 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Fri, 25 Feb 2022 23:34:50 +0000 Subject: [PATCH 3/5] Add tests for all intrinsics and tweak a few intrinsics --- .../TrimAnalysis/HandleCallAction.cs | 43 ++++--- .../ReflectionMethodBodyScanner.cs | 10 +- .../DataFlow/GetInterfaceDataFlow.cs | 29 ++++- .../DataFlow/GetTypeDataFlow.cs | 18 +++ .../DataFlow/GetTypeInfoDataFlow.cs | 15 +++ .../DataFlow/MakeGenericDataFlow.cs | 40 +++++++ .../DataFlow/TypeBaseTypeDataFlow.cs | 10 ++ .../DataFlow/TypeInfoAsTypeDataFlow.cs | 16 +++ .../Reflection/ActivatorCreateInstance.cs | 107 ++++++++++++++++++ .../ConstructorUsedViaReflection.cs | 16 +++ .../ConstructorsUsedViaReflection.cs | 9 ++ .../Reflection/EventUsedViaReflection.cs | 18 +++ .../Reflection/EventsUsedViaReflection.cs | 9 ++ .../Reflection/ExpressionCallString.cs | 40 +++++++ .../Reflection/ExpressionFieldString.cs | 40 +++++++ .../Reflection/ExpressionNewType.cs | 17 +++ .../Reflection/ExpressionPropertyString.cs | 40 +++++++ .../Reflection/FieldUsedViaReflection.cs | 18 +++ .../Reflection/FieldsUsedViaReflection.cs | 9 ++ .../Reflection/MemberUsedViaReflection.cs | 31 +++++ .../Reflection/MembersUsedViaReflection.cs | 9 ++ .../Reflection/MethodUsedViaReflection.cs | 18 +++ .../Reflection/MethodsUsedViaReflection.cs | 9 ++ .../Reflection/NestedTypeUsedViaReflection.cs | 32 ++++++ .../NestedTypesUsedViaReflection.cs | 9 ++ .../Reflection/ObjectGetType.cs | 42 +++++++ .../Reflection/PropertiesUsedViaReflection.cs | 9 ++ .../Reflection/PropertyUsedViaReflection.cs | 18 +++ .../RunClassConstructorUsedViaReflection.cs | 9 ++ .../RuntimeReflectionExtensionsCalls.cs | 41 ++++++- .../Reflection/TypeDelegator.cs | 36 +++++- .../Reflection/UnderlyingSystemType.cs | 27 ++++- 32 files changed, 773 insertions(+), 21 deletions(-) diff --git a/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs b/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs index bcb780427574..c9eda8d29451 100644 --- a/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs +++ b/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs @@ -95,28 +95,37 @@ public bool Invoke (MethodProxy calledMethod, MultiValue instanceValue, IReadOnl // GetInterface (String, bool) // case IntrinsicId.Type_GetInterface: { - if (instanceValue.IsEmpty ()) { + if (instanceValue.IsEmpty () || argumentValues[0].IsEmpty ()) { returnValue = MultiValueLattice.Top; break; } var targetValue = GetMethodThisParameterValue (calledMethod, DynamicallyAccessedMemberTypesOverlay.Interfaces); foreach (var value in instanceValue) { - // For now no support for marking a single interface by name. We would have to correctly support - // mangled names for generics to do that correctly. Simply mark all interfaces on the type for now. - - // Require Interfaces annotation - _requireDynamicallyAccessedMembersAction.Invoke (value, targetValue); - - // Interfaces is transitive, so the return values will always have at least Interfaces annotation - DynamicallyAccessedMemberTypes returnMemberTypes = DynamicallyAccessedMemberTypesOverlay.Interfaces; - - // Propagate All annotation across the call - All is a superset of Interfaces - if (value is ValueWithDynamicallyAccessedMembers valueWithDynamicallyAccessedMembers - && valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes == DynamicallyAccessedMemberTypes.All) - returnMemberTypes = DynamicallyAccessedMemberTypes.All; - - AddReturnValue (GetMethodReturnValue (calledMethod, returnMemberTypes)); + foreach (var interfaceName in argumentValues[0]) { + if (interfaceName == NullValue.Instance) { + // Throws on null string, so no return value. + returnValue ??= MultiValueLattice.Top; + } else if (interfaceName is KnownStringValue stringValue && stringValue.Contents.Length == 0) { + AddReturnValue (NullValue.Instance); + } else { + // For now no support for marking a single interface by name. We would have to correctly support + // mangled names for generics to do that correctly. Simply mark all interfaces on the type for now. + + // Require Interfaces annotation + _requireDynamicallyAccessedMembersAction.Invoke (value, targetValue); + + // Interfaces is transitive, so the return values will always have at least Interfaces annotation + DynamicallyAccessedMemberTypes returnMemberTypes = DynamicallyAccessedMemberTypesOverlay.Interfaces; + + // Propagate All annotation across the call - All is a superset of Interfaces + if (value is ValueWithDynamicallyAccessedMembers valueWithDynamicallyAccessedMembers + && valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes == DynamicallyAccessedMemberTypes.All) + returnMemberTypes = DynamicallyAccessedMemberTypes.All; + + AddReturnValue (GetMethodReturnValue (calledMethod, returnMemberTypes)); + } + } } } break; @@ -188,6 +197,8 @@ public bool Invoke (MethodProxy calledMethod, MultiValue instanceValue, IReadOnl } else if (uniqueValue is SystemTypeValue) { // SystemTypeValue can fullfill any requirement, so it's always valid // The requirements will be applied at the point where it's consumed (passed as a method parameter, set as field value, returned from the method) + } else if (uniqueValue == NullValue.Instance) { + // NullValue can fulfill any requirements because reflection access to it will typically throw. } else { throw new InvalidOperationException ($"Internal linker error: in {GetContainingSymbolDisplayName ()} processing call to {calledMethod.GetDisplayName ()} returned value which is not correctly annotated with the expected dynamic member access kinds."); } diff --git a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs index 4ae5b0b9a963..405557829144 100644 --- a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs +++ b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs @@ -1310,7 +1310,7 @@ bool AnalyzeGenericInstantiationTypeArray (in AnalysisContext analysisContext, i bool allIndicesKnown = true; for (int i = 0; i < size.Value; i++) { - if (!array.TryGetValueByIndex (i, out MultiValue value) || value.IsEmpty () || value.AsSingleValue () is UnknownValue) { + if (!array.TryGetValueByIndex (i, out MultiValue value) || value.AsSingleValue () is UnknownValue) { allIndicesKnown = false; break; } @@ -1353,7 +1353,15 @@ void ProcessCreateInstanceByName (in AnalysisContext analysisContext, MethodDefi foreach (var assemblyNameValue in methodParams[methodParamsOffset]) { if (assemblyNameValue is KnownStringValue assemblyNameStringValue) { + if (assemblyNameStringValue.Contents is string assemblyName && assemblyName.Length == 0) { + // Throws exception for zero-length assembly name. + continue; + } foreach (var typeNameValue in methodParams[methodParamsOffset + 1]) { + if (typeNameValue is NullValue) { + // Throws exception for null type name. + continue; + } if (typeNameValue is KnownStringValue typeNameStringValue) { var resolvedAssembly = _context.TryResolve (assemblyNameStringValue.Contents); if (resolvedAssembly == null) { diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/GetInterfaceDataFlow.cs b/test/Mono.Linker.Tests.Cases/DataFlow/GetInterfaceDataFlow.cs index 7238a50f3ee0..2adb76866e4f 100644 --- a/test/Mono.Linker.Tests.Cases/DataFlow/GetInterfaceDataFlow.cs +++ b/test/Mono.Linker.Tests.Cases/DataFlow/GetInterfaceDataFlow.cs @@ -24,6 +24,23 @@ public static void Main () class GetInterface_Name { + static void TestNullName (Type type) + { + type.GetInterface (null); + } + + static void TestEmptyName (Type type) + { + type.GetInterface (string.Empty); + } + + static void TestNoValueName (Type type) + { + Type t = null; + string noValue = t.AssemblyQualifiedName; + type.GetInterface (noValue); + } + [ExpectedWarning ("IL2070", nameof (Type.GetInterface), nameof (DynamicallyAccessedMemberTypes) + "." + nameof (DynamicallyAccessedMemberTypes.Interfaces))] static void TestNoAnnotation (Type type) { @@ -76,12 +93,18 @@ static void TestMergedValues (int p, [DynamicallyAccessedMembers (DynamicallyAcc type.GetInterface ("ITestInterface").RequiresInterfaces (); } + static void TestNullValue () + { + Type t = null; + t.GetInterface ("ITestInterface").RequiresInterfaces (); + } + static void TestNoValue () { Type t = null; Type noValue = Type.GetTypeFromHandle (t.TypeHandle); // t.TypeHandle throws at runtime so don't warn here. - noValue.GetInterface ("ITestInterface").RequiresAll (); + noValue.GetInterface ("ITestInterface").RequiresInterfaces (); } class GetInterfaceInCtor @@ -94,6 +117,9 @@ public GetInterfaceInCtor ([DynamicallyAccessedMembers (DynamicallyAccessedMembe public static void Test () { + TestNullName (typeof (TestType)); + TestEmptyName (typeof (TestType)); + TestNoValueName (typeof (TestType)); TestNoAnnotation (typeof (TestType)); TestWithAnnotation (typeof (TestType)); TestWithAnnotation (typeof (ITestInterface)); @@ -101,6 +127,7 @@ public static void Test () TestKnownType (); TestMultipleValues (0, typeof (TestType)); TestMergedValues (0, typeof (TestType)); + TestNullValue (); TestNoValue (); var _ = new GetInterfaceInCtor (typeof (TestType)); } diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/GetTypeDataFlow.cs b/test/Mono.Linker.Tests.Cases/DataFlow/GetTypeDataFlow.cs index 289916c277d1..4b56efb37cfe 100644 --- a/test/Mono.Linker.Tests.Cases/DataFlow/GetTypeDataFlow.cs +++ b/test/Mono.Linker.Tests.Cases/DataFlow/GetTypeDataFlow.cs @@ -21,6 +21,8 @@ public static void Main () TestPublicParameterlessConstructor (); TestPublicConstructors (); TestConstructors (); + TestNull (); + TestNoValue (); TestUnknownType (); TestTypeNameFromParameter (null); @@ -69,6 +71,22 @@ static void TestConstructors () type.RequiresNone (); } + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions.RequiresAll) + "(Type)", nameof (Type.GetType) + "(String)")] + static void TestNull () + { + // Warns about the return value of GetType, even though this throws at runtime. + Type.GetType (null).RequiresAll (); + } + + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions.RequiresAll) + "(Type)", nameof (Type.GetType) + "(String)")] + static void TestNoValue () + { + Type t = null; + string noValue = t.AssemblyQualifiedName; + // Warns about the return value of GetType, even though AssemblyQualifiedName throws at runtime. + Type.GetType (noValue).RequiresAll (); + } + [ExpectedWarning ("IL2057", nameof (GetType))] static void TestUnknownType () { diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/GetTypeInfoDataFlow.cs b/test/Mono.Linker.Tests.Cases/DataFlow/GetTypeInfoDataFlow.cs index e99d87bc53dc..d56899ec0db8 100644 --- a/test/Mono.Linker.Tests.Cases/DataFlow/GetTypeInfoDataFlow.cs +++ b/test/Mono.Linker.Tests.Cases/DataFlow/GetTypeInfoDataFlow.cs @@ -17,6 +17,8 @@ public static void Main () { TestNoAnnotations (typeof (TestType)); TestWithAnnotations (typeof (TestType)); + TestWithNull (); + TestWithNoValue (); } [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicMethods))] @@ -34,6 +36,19 @@ static void TestWithAnnotations ([DynamicallyAccessedMembers (DynamicallyAccesse t.GetTypeInfo ().RequiresNone (); } + static void TestWithNull () + { + Type t = null; + t.GetTypeInfo ().RequiresPublicMethods (); + } + + static void TestWithNoValue () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + noValue.GetTypeInfo ().RequiresPublicMethods (); + } + class TestType { } } } diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/MakeGenericDataFlow.cs b/test/Mono.Linker.Tests.Cases/DataFlow/MakeGenericDataFlow.cs index 3d3f2551b772..2481fac0d839 100644 --- a/test/Mono.Linker.Tests.Cases/DataFlow/MakeGenericDataFlow.cs +++ b/test/Mono.Linker.Tests.Cases/DataFlow/MakeGenericDataFlow.cs @@ -22,7 +22,10 @@ class MakeGenericType public static void Test () { TestNullType (); + TestNoValueInput (); TestUnknownInput (null); + TestNullTypeArgument (); + TestNoValueTypeArgument (); TestWithUnknownTypeArray (null); TestWithArrayUnknownIndexSet (0); TestWithArrayUnknownLengthSet (1); @@ -53,6 +56,26 @@ static void TestNullType () nullType.MakeGenericType (typeof (TestType)); } + static void TestNoValueInput () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + noValue.MakeGenericType (typeof (TestType)); + } + + static void TestNullTypeArgument () + { + Type t = null; + typeof (GenericWithPublicFieldsArgument<>).MakeGenericType (t); + } + + static void TestNoValueTypeArgument () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + typeof (GenericWithPublicFieldsArgument<>).MakeGenericType (noValue); + } + [ExpectedWarning ("IL2055", nameof (Type.MakeGenericType))] static void TestUnknownInput (Type inputType) { @@ -196,6 +219,8 @@ public static void Test () TestNullMethod (); TestUnknownMethod (null); TestUnknownMethodButNoTypeArguments (null); + TestNullTypeArgument (); + TestNoValueTypeArgument (); TestWithUnknownTypeArray (null); TestWithArrayUnknownIndexSet (0); TestWithArrayUnknownIndexSetByRef (0); @@ -248,6 +273,21 @@ static void TestUnknownMethodButNoTypeArguments (MethodInfo mi) mi.MakeGenericMethod (Type.EmptyTypes); } + static void TestNullTypeArgument () + { + Type t = null; + typeof (MakeGenericMethod).GetMethod (nameof (GenericWithRequirements), BindingFlags.Static) + .MakeGenericMethod (t); + } + + static void TestNoValueTypeArgument () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + typeof (MakeGenericMethod).GetMethod (nameof (GenericWithRequirements), BindingFlags.Static) + .MakeGenericMethod (noValue); + } + [ExpectedWarning ("IL2060", nameof (MethodInfo.MakeGenericMethod))] static void TestWithUnknownTypeArray (Type[] types) { diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/TypeBaseTypeDataFlow.cs b/test/Mono.Linker.Tests.Cases/DataFlow/TypeBaseTypeDataFlow.cs index 0667498f6ff4..5ce16ae04d05 100644 --- a/test/Mono.Linker.Tests.Cases/DataFlow/TypeBaseTypeDataFlow.cs +++ b/test/Mono.Linker.Tests.Cases/DataFlow/TypeBaseTypeDataFlow.cs @@ -44,6 +44,7 @@ public static void Main () TestNoAnnotation (typeof (TestType)); TestAnnotatedAndUnannotated (typeof (TestType), typeof (TestType), 0); TestNull (); + TestNoValue (); Mixed_Derived.Test (typeof (TestType), 0); } @@ -211,6 +212,15 @@ static void TestNull () type.BaseType.RequiresPublicMethods (); } + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + static void TestNoValue () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + // Warns about the base type even though the above throws an exception at runtime. + noValue.BaseType.RequiresPublicMethods (); + } + class Mixed_Base { public static void PublicMethod () { } diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/TypeInfoAsTypeDataFlow.cs b/test/Mono.Linker.Tests.Cases/DataFlow/TypeInfoAsTypeDataFlow.cs index d41bcf284f33..22789cc4170f 100644 --- a/test/Mono.Linker.Tests.Cases/DataFlow/TypeInfoAsTypeDataFlow.cs +++ b/test/Mono.Linker.Tests.Cases/DataFlow/TypeInfoAsTypeDataFlow.cs @@ -17,6 +17,8 @@ public static void Main () { TestNoAnnotations (typeof (TestType).GetTypeInfo ()); TestWithAnnotations (typeof (TestType).GetTypeInfo ()); + TestWithNull (); + TestWithNoValue (); } [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicMethods))] @@ -34,6 +36,20 @@ static void TestWithAnnotations ([DynamicallyAccessedMembers (DynamicallyAccesse t.AsType ().RequiresNone (); } + static void TestWithNull () + { + TypeInfo t = null; + t.AsType ().RequiresPublicMethods (); + } + + static void TestWithNoValue () + { + Type t = null; + Type noValueType = Type.GetTypeFromHandle (t.TypeHandle); + TypeInfo noValue = noValueType.GetTypeInfo (); + noValue.AsType ().RequiresPublicMethods (); + } + class TestType { } } } diff --git a/test/Mono.Linker.Tests.Cases/Reflection/ActivatorCreateInstance.cs b/test/Mono.Linker.Tests.Cases/Reflection/ActivatorCreateInstance.cs index fb8314f9b91e..968d9347176b 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/ActivatorCreateInstance.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/ActivatorCreateInstance.cs @@ -58,6 +58,9 @@ public static void Main () TestNullArgsNonPublicOnly (typeof (TestType)); TestNullArgsNonPublicWithNonPublicAnnotation (typeof (TestType)); + TestNullType (); + TestNoValue (); + CreateInstanceWithGetTypeFromHierarchy.Test (); } @@ -314,6 +317,11 @@ private static void WithAssemblyName () Activator.CreateInstance ("test", "Mono.Linker.Tests.Cases.Reflection.ActivatorCreateInstance+WithAssemblyNamePrivateOnly", false, BindingFlags.NonPublic, null, new object[] { }, null, new object[] { }); WithNullAssemblyName (); + WithEmptyAssemblyName (); + WithNoValueAssemblyName (); + WithAssemblyAndNullTypeName (); + WithAssemblyAndEmptyTypeName (); + WithAssemblyAndNoValueTypeName (); WithNonExistingAssemblyName (); WithAssemblyAndUnknownTypeName (); WithAssemblyAndNonExistingTypeName (); @@ -326,6 +334,40 @@ private static void WithNullAssemblyName () Activator.CreateInstance (null, "Mono.Linker.Tests.Cases.Reflection.ActivatorCreateInstance+WithAssemblyNameParameterless1"); } + [Kept] + private static void WithEmptyAssemblyName () + { + Activator.CreateInstance (string.Empty, "Mono.Linker.Tests.Cases.Reflection.ActivatorCreateInstance+WithAssemblyNameParameterless1"); + } + + [Kept] + private static void WithNoValueAssemblyName () + { + Type t = null; + string noValue = t.AssemblyQualifiedName; + Activator.CreateInstance (noValue, "Mono.Linker.Tests.Cases.Reflection.ActivatorCreateInstance+WithAssemblyNameParameterless1"); + } + + [Kept] + private static void WithAssemblyAndNullTypeName () + { + Activator.CreateInstance ("test", null); + } + + [Kept] + private static void WithAssemblyAndEmptyTypeName () + { + Activator.CreateInstance ("test", string.Empty); + } + + [Kept] + private static void WithAssemblyAndNoValueTypeName () + { + Type t = null; + string noValue = t.AssemblyQualifiedName; + Activator.CreateInstance ("test", noValue); + } + [Kept] [ExpectedWarning ("IL2061", nameof (Activator) + "." + nameof (Activator.CreateInstance), "NonExistingAssembly")] private static void WithNonExistingAssemblyName () @@ -366,6 +408,56 @@ public WithAssemblyPathParameterless (int i, object o) private static void WithAssemblyPath () { Activator.CreateInstanceFrom ("test", "Mono.Linker.Tests.Cases.Reflection.ActivatorCreateInstance+WithAssemblyPathParameterless"); + + WithNullAssemblyPath (); + WithEmptyAssemblyPath (); + WithNoValueAssemblyPath (); + WithAssemblyPathAndNullTypeName (); + WithAssemblyPathAndEmptyTypeName (); + WithAssemblyPathAndNoValueTypeName (); + } + + [Kept] + // Technically this shouldn't warn because CreateInstanceFrom throws if the assembly file path is null. + // However, our implementation is the same for CreateInstance and CreateInstanceFrom. + [ExpectedWarning ("IL2032", nameof (Activator) + "." + nameof (Activator.CreateInstance), "assemblyFile")] + private static void WithNullAssemblyPath () + { + Activator.CreateInstanceFrom (null, "Mono.Linker.Tests.Cases.Reflection.ActivatorCreateInstance+WithAssemblyPathParameterless"); + } + + [Kept] + private static void WithEmptyAssemblyPath () + { + Activator.CreateInstanceFrom (string.Empty, "Mono.Linker.Tests.Cases.Reflection.ActivatorCreateInstance+WithAssemblyPathParameterless"); + } + + [Kept] + private static void WithNoValueAssemblyPath () + { + Type t = null; + string noValue = t.AssemblyQualifiedName; + Activator.CreateInstanceFrom (noValue, "Mono.Linker.Tests.Cases.Reflection.ActivatorCreateInstance+WithAssemblyPathParameterless"); + } + + [Kept] + private static void WithAssemblyPathAndNullTypeName () + { + Activator.CreateInstanceFrom ("test", null); + } + + [Kept] + private static void WithAssemblyPathAndEmptyTypeName () + { + Activator.CreateInstanceFrom ("test", string.Empty); + } + + [Kept] + private static void WithAssemblyPathAndNoValueTypeName () + { + Type t = null; + string noValue = t.AssemblyQualifiedName; + Activator.CreateInstanceFrom ("test", noValue); } [Kept] @@ -532,6 +624,21 @@ private static void TestNullArgsNonPublicWithNonPublicAnnotation ( Activator.CreateInstance (type, nonPublic: true); } + [Kept] + private static void TestNullType () + { + Type t = null; + Activator.CreateInstance (t); + } + + [Kept] + private static void TestNoValue () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + Activator.CreateInstance (noValue); + } + [Kept] class CreateInstanceWithGetTypeFromHierarchy { diff --git a/test/Mono.Linker.Tests.Cases/Reflection/ConstructorUsedViaReflection.cs b/test/Mono.Linker.Tests.Cases/Reflection/ConstructorUsedViaReflection.cs index 5c28deafe9de..a4b01a9ac57c 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/ConstructorUsedViaReflection.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/ConstructorUsedViaReflection.cs @@ -20,6 +20,8 @@ public static void Main () GetConstructor_BindingAttr_Types.Test (); #endif TestNullType (); + TestNoValue (); + TestNullArguments (); TestDataFlowType (); IfElse.TestIfElse (true); } @@ -264,6 +266,20 @@ static void TestNullType () var constructor = type.GetConstructor (new Type[] { }); } + [Kept] + static void TestNoValue () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + var constructor = noValue.GetConstructor (new Type[] { }); + } + + [Kept] + static void TestNullArguments () + { + var constrctor = typeof (TestType).GetConstructor (null); + } + [Kept] static Type FindType () { diff --git a/test/Mono.Linker.Tests.Cases/Reflection/ConstructorsUsedViaReflection.cs b/test/Mono.Linker.Tests.Cases/Reflection/ConstructorsUsedViaReflection.cs index 78ed3dad1972..37ded966d8b2 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/ConstructorsUsedViaReflection.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/ConstructorsUsedViaReflection.cs @@ -16,6 +16,7 @@ public static void Main () TestWithBindingFlags (); TestWithUnknownBindingFlags (BindingFlags.Public); TestNullType (); + TestNoValue (); TestDataFlowType (); TestDataFlowWithAnnotation (typeof (MyType)); TestIfElse (true); @@ -47,6 +48,14 @@ static void TestNullType () var constructors = type.GetConstructors (); } + [Kept] + static void TestNoValue () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + var constructors = noValue.GetConstructors (); + } + [Kept] static Type FindType () { diff --git a/test/Mono.Linker.Tests.Cases/Reflection/EventUsedViaReflection.cs b/test/Mono.Linker.Tests.Cases/Reflection/EventUsedViaReflection.cs index 5dbad0c58339..80405b45e91b 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/EventUsedViaReflection.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/EventUsedViaReflection.cs @@ -21,8 +21,10 @@ public static void Main () TestNameUnknownBindingFlagsAndName (BindingFlags.Public, "DoesntMatter"); TestNullName (); TestEmptyName (); + TestNoValueName (); TestNonExistingName (); TestNullType (); + TestNoValue (); TestDataFlowType (); TestIfElse (1); TestEventInBaseType (); @@ -89,6 +91,14 @@ static void TestEmptyName () var eventInfo = typeof (EventUsedViaReflection).GetEvent (string.Empty); } + [Kept] + static void TestNoValueName () + { + Type t = null; + string noValue = t.AssemblyQualifiedName; + var method = typeof (EventUsedViaReflection).GetEvent (noValue); + } + [Kept] static void TestNonExistingName () { @@ -102,6 +112,14 @@ static void TestNullType () var eventInfo = type.GetEvent ("Event"); } + [Kept] + static void TestNoValue () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + var method = noValue.GetEvent ("Event"); + } + [Kept] static Type FindType () { diff --git a/test/Mono.Linker.Tests.Cases/Reflection/EventsUsedViaReflection.cs b/test/Mono.Linker.Tests.Cases/Reflection/EventsUsedViaReflection.cs index 53a7494f61d2..613d380a0d12 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/EventsUsedViaReflection.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/EventsUsedViaReflection.cs @@ -20,6 +20,7 @@ public static void Main () TestBindingFlags (); TestUnknownBindingFlags (BindingFlags.Public); TestNullType (); + TestNoValue (); TestDataFlowType (); TestDataFlowWithAnnotation (typeof (MyType)); TestIfElse (1); @@ -64,6 +65,14 @@ static void TestNullType () var events = type.GetEvents (); } + [Kept] + static void TestNoValue () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + var methods = noValue.GetEvents (); + } + [Kept] static Type FindType () { diff --git a/test/Mono.Linker.Tests.Cases/Reflection/ExpressionCallString.cs b/test/Mono.Linker.Tests.Cases/Reflection/ExpressionCallString.cs index bb89b436dcec..fad40aecc8a1 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/ExpressionCallString.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/ExpressionCallString.cs @@ -26,6 +26,11 @@ public static void Main () Expression.Call (typeof (UnknownNameMethodClass), GetUnknownString (), Type.EmptyTypes); TestUnknownType.Test (); + TestNullType (); + TestNoValue (); + TestNullString (); + TestEmptyString (); + TestNoValueString (); TestGenericMethods.Test (); } @@ -142,6 +147,41 @@ static Type TriggerUnrecognizedPattern () } } + [Kept] + static void TestNullType () + { + Type t = null; + Expression.Call (t, "This string will not be reached", Type.EmptyTypes); + } + + [Kept] + static void TestNoValue () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + Expression.Call (noValue, "This string will not be reached", Type.EmptyTypes); + } + + [Kept] + static void TestNullString () + { + Expression.Call (typeof (TestType), null, Type.EmptyTypes); + } + + [Kept] + static void TestEmptyString () + { + Expression.Call (typeof (TestType), string.Empty, Type.EmptyTypes); + } + + [Kept] + static void TestNoValueString () + { + Type t = null; + string noValue = t.AssemblyQualifiedName; + Expression.Call (typeof (TestType), noValue, Type.EmptyTypes); + } + [Kept] class UnknownNameMethodClass { diff --git a/test/Mono.Linker.Tests.Cases/Reflection/ExpressionFieldString.cs b/test/Mono.Linker.Tests.Cases/Reflection/ExpressionFieldString.cs index c2399e92c2dc..3f1396000a47 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/ExpressionFieldString.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/ExpressionFieldString.cs @@ -21,6 +21,11 @@ public static void Main () UnknownTypeNoAnnotation.Test (); UnknownString.Test (); Expression.Field (null, GetType (), "This string will not be reached"); // IL2072 + TestNullType (); + TestNoValue (); + TestNullString (); + TestEmptyString (); + TestNoValueString (); } [Kept] @@ -103,6 +108,41 @@ static string GetString () } } + [Kept] + static void TestNullType () + { + Expression.Field (null, null, "This string will not be reached"); + } + + [Kept] + static void TestNoValue () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + Expression.Field (null, noValue, "This string will not be reached"); + } + + [Kept] + static void TestNullString () + { + Expression.Field (null, typeof (Base), null); + } + + [Kept] + static void TestEmptyString () + { + Expression.Field (null, typeof (Base), string.Empty); + } + + [Kept] + static void TestNoValueString () + { + Type t = null; + string noValue = t.AssemblyQualifiedName; + Expression.Field (null, typeof (Base), noValue); + } + + [Kept] class Base { diff --git a/test/Mono.Linker.Tests.Cases/Reflection/ExpressionNewType.cs b/test/Mono.Linker.Tests.Cases/Reflection/ExpressionNewType.cs index 9e06ccb43032..f100129ffb3b 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/ExpressionNewType.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/ExpressionNewType.cs @@ -18,6 +18,8 @@ public static void Main () Branch_NullValueNode (); Branch_MethodParameterValueNode (typeof (C)); Branch_UnrecognizedPatterns (); + TestNullType (); + TestNoValue (); } [Kept] @@ -60,6 +62,21 @@ static void Branch_UnrecognizedPatterns () Expression.New (GetType ()); } + [Kept] + static void TestNullType () + { + Type t = null; + Expression.New (t); + } + + [Kept] + static void TestNoValue () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + Expression.New (noValue); + } + #region Helpers [Kept] class A diff --git a/test/Mono.Linker.Tests.Cases/Reflection/ExpressionPropertyString.cs b/test/Mono.Linker.Tests.Cases/Reflection/ExpressionPropertyString.cs index 13de4491d076..9f9f8591ddef 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/ExpressionPropertyString.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/ExpressionPropertyString.cs @@ -20,6 +20,11 @@ public static void Main () Expression.Property (null, typeof (Derived), "ProtectedPropertyOnBase"); Expression.Property (null, typeof (Derived), "PublicPropertyOnBase"); UnknownType.Test (); + TestNull (); + TestNoValue (); + TestNullString (); + TestEmptyString (); + TestNoValueString (); UnknownString.Test (); Expression.Property (null, GetType (), "This string will not be reached"); // IL2072 } @@ -84,6 +89,21 @@ static Type GetType () } } + [Kept] + static void TestNull () + { + Type t = null; + Expression.Property (null, t, "This string will not be reached"); + } + + [Kept] + static void TestNoValue () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + Expression.Property (null, noValue, "This string will not be reached"); + } + [Kept] class UnknownString { @@ -114,6 +134,26 @@ static string GetString () } } + [Kept] + static void TestNullString () + { + Expression.Property (null, typeof (Base), null); + } + + [Kept] + static void TestEmptyString () + { + Expression.Property (null, typeof (Base), string.Empty); + } + + [Kept] + static void TestNoValueString () + { + Type t = null; + string noValue = t.AssemblyQualifiedName; + Expression.Property (null, typeof (Base), noValue); + } + [Kept] class Base { diff --git a/test/Mono.Linker.Tests.Cases/Reflection/FieldUsedViaReflection.cs b/test/Mono.Linker.Tests.Cases/Reflection/FieldUsedViaReflection.cs index c0fad720631b..6fb42c22cb39 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/FieldUsedViaReflection.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/FieldUsedViaReflection.cs @@ -18,9 +18,11 @@ public static void Main () TestNameUnknownBindingFlags (BindingFlags.Public); TestNameUnknownBindingFlagsAndName (BindingFlags.Public, "DoesntMatter"); TestNullName (); + TestNoValueName (); TestEmptyName (); TestNonExistingName (); TestNullType (); + TestNoValue (); TestDataFlowType (); TestIfElse (1); TestFieldInBaseType (); @@ -75,6 +77,14 @@ static void TestNullName () var field = typeof (FieldUsedViaReflection).GetField (null); } + [Kept] + static void TestNoValueName () + { + Type t = null; + string noValue = t.AssemblyQualifiedName; + var method = typeof (FieldUsedViaReflection).GetField (noValue); + } + [Kept] static void TestEmptyName () { @@ -94,6 +104,14 @@ static void TestNullType () var field = type.GetField ("publicField"); } + [Kept] + static void TestNoValue () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + var method = noValue.GetField ("publicField"); + } + [Kept] static Type FindType () { diff --git a/test/Mono.Linker.Tests.Cases/Reflection/FieldsUsedViaReflection.cs b/test/Mono.Linker.Tests.Cases/Reflection/FieldsUsedViaReflection.cs index ab50a5dac971..04d54abae205 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/FieldsUsedViaReflection.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/FieldsUsedViaReflection.cs @@ -16,6 +16,7 @@ public static void Main () TestBindingFlags (); TestUnknownBindingFlags (BindingFlags.Public); TestNullType (); + TestNoValue (); TestDataFlowType (); TestDataFlowWithAnnotation (typeof (MyType)); TestIfElse (1); @@ -49,6 +50,14 @@ static void TestNullType () var fields = type.GetFields (); } + [Kept] + static void TestNoValue () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + var methods = noValue.GetFields (); + } + [Kept] static Type FindType () { diff --git a/test/Mono.Linker.Tests.Cases/Reflection/MemberUsedViaReflection.cs b/test/Mono.Linker.Tests.Cases/Reflection/MemberUsedViaReflection.cs index 828ce4d77cf6..a6e8b26d039c 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/MemberUsedViaReflection.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/MemberUsedViaReflection.cs @@ -15,11 +15,15 @@ public static void Main () // Normally calls to GetMember use prefix lookup to match multiple values, we took a conservative approach // and preserve not based on the string passed but on the binding flags requirements TestWithName (); + TestWithNullName (); + TestWithEmptyName (); + TestWithNoValueName (); TestWithPrefixLookup (); TestWithBindingFlags (); TestWithUnknownBindingFlags (BindingFlags.Public); TestWithMemberTypes (); TestNullType (); + TestNoValue (); TestDataFlowType (); TestDataFlowWithAnnotation (typeof (MyType)); TestIfElse (true); @@ -31,6 +35,25 @@ static void TestWithName () var members = typeof (SimpleType).GetMember ("memberKept"); } + [Kept] + public static void TestWithNullName () + { + var members = typeof (SimpleType).GetMember (null); + } + + [Kept] + static void TestWithEmptyName () + { + var members = typeof (SimpleType).GetMember (string.Empty); + } + + [Kept] + static void TestWithNoValueName () + { + Type t = null; + string noValue = t.AssemblyQualifiedName; + var members = typeof (SimpleType).GetMember (noValue); + } [Kept] static void TestWithPrefixLookup () @@ -66,6 +89,14 @@ static void TestNullType () var constructor = type.GetMember ("PrefixLookup*"); } + [Kept] + static void TestNoValue () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + var members = noValue.GetMember ("PrefixLookup*"); + } + [Kept] static Type FindType () { diff --git a/test/Mono.Linker.Tests.Cases/Reflection/MembersUsedViaReflection.cs b/test/Mono.Linker.Tests.Cases/Reflection/MembersUsedViaReflection.cs index 8a20f77331f7..20f9bb53906d 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/MembersUsedViaReflection.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/MembersUsedViaReflection.cs @@ -16,6 +16,7 @@ public static void Main () TestWithBindingFlags (); TestWithUnknownBindingFlags (BindingFlags.Public); TestNullType (); + TestNoValue (); TestDataFlowType (); TestDataFlowWithAnnotation (typeof (MyType)); TestIfElse (true); @@ -47,6 +48,14 @@ static void TestNullType () var members = type.GetMembers (); } + [Kept] + static void TestNoValue () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + var members = noValue.GetMembers (); + } + [Kept] static Type FindType () { diff --git a/test/Mono.Linker.Tests.Cases/Reflection/MethodUsedViaReflection.cs b/test/Mono.Linker.Tests.Cases/Reflection/MethodUsedViaReflection.cs index 116cabad031d..393a2ea5e399 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/MethodUsedViaReflection.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/MethodUsedViaReflection.cs @@ -36,8 +36,10 @@ public static void Main () #endif TestNullName (); TestEmptyName (); + TestNoValueName (); TestNonExistingName (); TestNullType (); + TestNoValue (); TestDataFlowType (); IfElse.TestIfElse (1); DerivedAndBase.TestMethodInBaseType (); @@ -593,6 +595,14 @@ static void TestEmptyName () var method = typeof (MethodUsedViaReflection).GetMethod (string.Empty); } + [Kept] + static void TestNoValueName () + { + Type t = null; + string noValue = t.AssemblyQualifiedName; + var method = typeof (MethodUsedViaReflection).GetMethod (noValue); + } + [Kept] static void TestNonExistingName () { @@ -606,6 +616,14 @@ static void TestNullType () var method = type.GetMethod ("OnlyCalledViaReflection", BindingFlags.Static | BindingFlags.Public); } + [Kept] + static void TestNoValue () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + var method = noValue.GetMethod ("OnlyCalledViaReflection", BindingFlags.Static | BindingFlags.Public); + } + [Kept] static Type FindType () { diff --git a/test/Mono.Linker.Tests.Cases/Reflection/MethodsUsedViaReflection.cs b/test/Mono.Linker.Tests.Cases/Reflection/MethodsUsedViaReflection.cs index fcede014b6e0..f25f9ccc1c26 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/MethodsUsedViaReflection.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/MethodsUsedViaReflection.cs @@ -18,6 +18,7 @@ public static void Main () TestBindingFlags (); TestUnknownBindingFlags (BindingFlags.Public); TestNullType (); + TestNoValue (); TestDataFlowType (); TestDataFlowWithAnnotation (typeof (MyType)); TestIfElse (1); @@ -52,6 +53,14 @@ static void TestNullType () var methods = type.GetMethods (BindingFlags.Static | BindingFlags.Public); } + [Kept] + static void TestNoValue () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + var methods = noValue.GetMethods (BindingFlags.Static | BindingFlags.Public); + } + [Kept] static Type FindType () { diff --git a/test/Mono.Linker.Tests.Cases/Reflection/NestedTypeUsedViaReflection.cs b/test/Mono.Linker.Tests.Cases/Reflection/NestedTypeUsedViaReflection.cs index 66256ef78658..b5889ed03edc 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/NestedTypeUsedViaReflection.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/NestedTypeUsedViaReflection.cs @@ -15,11 +15,15 @@ public static void Main () { TestByName (); TestPrivateByName (); + TestNullName (); + TestEmptyName (); + TestNoValueName (); TestByBindingFlags (); TestByUnknownBindingFlags (BindingFlags.Public); TestByUnknownBindingFlagsAndName (BindingFlags.Public, "DoesntMatter"); TestNonExistingName (); TestNullType (); + TestNoValue (); TestIgnoreCaseBindingFlags (); TestFailIgnoreCaseBindingFlags (); TestUnsupportedBindingFlags (); @@ -43,6 +47,26 @@ static void TestPrivateByName () _ = typeof (NestedTypeUsedViaReflection).GetNestedType (nameof (PrivateUnreferencedNestedType), BindingFlags.Public); } + [Kept] + static void TestNullName () + { + _ = typeof (NestedTypeUsedViaReflection).GetNestedType (null); + } + + [Kept] + static void TestEmptyName () + { + _ = typeof (NestedTypeUsedViaReflection).GetNestedType (string.Empty); + } + + [Kept] + static void TestNoValueName () + { + Type t = null; + string noValue = t.AssemblyQualifiedName; + var method = typeof (NestedTypeUsedViaReflection).GetNestedType (noValue); + } + [Kept] public static class PublicNestedType { } @@ -87,6 +111,14 @@ static void TestNullType () _ = type.GetNestedType ("NestedType"); } + [Kept] + static void TestNoValue () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + var method = noValue.GetNestedType ("NestedType"); + } + [Kept] static void TestIgnoreCaseBindingFlags () { diff --git a/test/Mono.Linker.Tests.Cases/Reflection/NestedTypesUsedViaReflection.cs b/test/Mono.Linker.Tests.Cases/Reflection/NestedTypesUsedViaReflection.cs index 4d8683aed550..021776f30bb6 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/NestedTypesUsedViaReflection.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/NestedTypesUsedViaReflection.cs @@ -18,6 +18,7 @@ public static void Main () TestByBindingFlags (); TestByUnknownBindingFlags (BindingFlags.Public); TestNullType (); + TestNoValue (); TestDataFlowType (); TestDataFlowWithAnnotation (typeof (MyType)); TestIgnoreCaseBindingFlags (); @@ -62,6 +63,14 @@ static void TestNullType () _ = type.GetNestedTypes (BindingFlags.Public); } + [Kept] + static void TestNoValue () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + _ = noValue.GetNestedTypes (BindingFlags.Public); + } + [Kept] static Type FindType () { diff --git a/test/Mono.Linker.Tests.Cases/Reflection/ObjectGetType.cs b/test/Mono.Linker.Tests.Cases/Reflection/ObjectGetType.cs index c0b263a3219b..12b8d5a5d0e4 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/ObjectGetType.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/ObjectGetType.cs @@ -53,6 +53,9 @@ public static void Main () EnumerationOverInstances.Test (); DataFlowUnusedGetType.Test (); + + NullValue.Test (); + NoValue.Test (); } [Kept] @@ -1458,5 +1461,44 @@ public static void Test () } } } + + [Kept] + class NullValue + { + [Kept] + class TestType + { + } + + [Kept] + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions.RequiresAll) + "(Type)", nameof (Object.GetType) + "()")] + public static void Test () + { + TestType nullInstance = null; + // Even though this throws at runtime, we warn about the return value of GetType + nullInstance.GetType ().RequiresAll (); + } + } + + [Kept] + class NoValue + { + [Kept] + class TestType + { + } + + [Kept] + static TestType GetInstance () => null; + + [Kept] + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions.RequiresAll) + "(Type)", nameof (Object.GetType) + "()")] + public static void Test () + { + TestType noValue = GetInstance (); + // Even though this throws at runtime, we warn about the return value of GetType + noValue.GetType ().RequiresAll (); + } + } } } diff --git a/test/Mono.Linker.Tests.Cases/Reflection/PropertiesUsedViaReflection.cs b/test/Mono.Linker.Tests.Cases/Reflection/PropertiesUsedViaReflection.cs index 2254d93b66b1..bfaf6659ce41 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/PropertiesUsedViaReflection.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/PropertiesUsedViaReflection.cs @@ -19,6 +19,7 @@ public static void Main () TestUnknownBindingFlags (BindingFlags.Public); TestPropertiesOfArray (); TestNullType (); + TestNoValue (); TestDataFlowType (); TestDataFlowWithAnnotation (typeof (MyType)); TestIfElse (1); @@ -59,6 +60,14 @@ static void TestNullType () var properties = type.GetProperties (BindingFlags.Public); } + [Kept] + static void TestNoValue () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + var methods = noValue.GetProperties (BindingFlags.Public); + } + [Kept] static Type FindType () { diff --git a/test/Mono.Linker.Tests.Cases/Reflection/PropertyUsedViaReflection.cs b/test/Mono.Linker.Tests.Cases/Reflection/PropertyUsedViaReflection.cs index bdd67198790e..62729844c0ef 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/PropertyUsedViaReflection.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/PropertyUsedViaReflection.cs @@ -21,9 +21,11 @@ public static void Main () TestUnknownBindingFlagsAndName (BindingFlags.Public, "IrrelevantName"); TestNullName (); TestEmptyName (); + TestNoValueName (); TestNonExistingName (); TestPropertyOfArray (); TestNullType (); + TestNoValue (); TestDataFlowType (); TestIfElse (1); TestPropertyInBaseType (); @@ -97,6 +99,14 @@ static void TestEmptyName () var property = typeof (PropertyUsedViaReflection).GetProperty (string.Empty); } + [Kept] + static void TestNoValueName () + { + Type t = null; + string noValue = t.AssemblyQualifiedName; + var method = typeof (PropertyUsedViaReflection).GetProperty (noValue); + } + [Kept] static void TestNonExistingName () { @@ -117,6 +127,14 @@ static void TestNullType () var property = type.GetProperty ("GetterOnly"); } + [Kept] + static void TestNoValue () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + var method = noValue.GetProperty ("GetterOnly"); + } + [Kept] static Type FindType () { diff --git a/test/Mono.Linker.Tests.Cases/Reflection/RunClassConstructorUsedViaReflection.cs b/test/Mono.Linker.Tests.Cases/Reflection/RunClassConstructorUsedViaReflection.cs index 0744577ec00a..d40ede4bdd30 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/RunClassConstructorUsedViaReflection.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/RunClassConstructorUsedViaReflection.cs @@ -15,6 +15,7 @@ public static void Main () TestRunClassConstructor (); TestNonKeptStaticConstructor (); TestNull (); + TestNoValue (); TestDataFlowType (); TestIfElseUsingRuntimeTypeHandle (1); TestIfElseUsingType (1); @@ -39,6 +40,14 @@ static void TestNull () RuntimeHelpers.RunClassConstructor (type.TypeHandle); } + [Kept] + static void TestNoValue () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + RuntimeHelpers.RunClassConstructor (noValue.TypeHandle); + } + [Kept] static Type FindType () { diff --git a/test/Mono.Linker.Tests.Cases/Reflection/RuntimeReflectionExtensionsCalls.cs b/test/Mono.Linker.Tests.Cases/Reflection/RuntimeReflectionExtensionsCalls.cs index 6d71c7ad8a33..b440d3f4b963 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/RuntimeReflectionExtensionsCalls.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/RuntimeReflectionExtensionsCalls.cs @@ -15,7 +15,6 @@ public static void Main () TestGetRuntimeField (); TestGetRuntimeMethod (); TestGetRuntimeProperty (); - } #region GetRuntimeEvent @@ -29,6 +28,16 @@ public static void TestGetRuntimeEvent () GetClassWithEvent ().GetRuntimeEvent ("This string will not be reached"); typeof (Derived).GetRuntimeEvent ("Event"); GetUnknownType ().GetRuntimeEvent (GetUnknownString ()); // IL2072 + + Type t = null; + t.GetRuntimeEvent ("This string will not be reached"); + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + noValue.GetRuntimeEvent ("This string will not be reached"); + + typeof (ClassWithKeptMembers).GetRuntimeEvent (null); + typeof (ClassWithKeptMembers).GetRuntimeEvent (string.Empty); + string noValueString = t.AssemblyQualifiedName; + typeof (ClassWithKeptMembers).GetRuntimeEvent (noValueString); } #endregion @@ -43,6 +52,16 @@ public static void TestGetRuntimeField () GetClassWithField ().GetRuntimeField ("This string will not be reached"); typeof (Derived).GetRuntimeField ("Field"); GetUnknownType ().GetRuntimeField (GetUnknownString ()); // IL2072 + + Type t = null; + t.GetRuntimeField ("This string will not be reached"); + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + noValue.GetRuntimeField ("This string will not be reached"); + + typeof (ClassWithKeptMembers).GetRuntimeField (null); + typeof (ClassWithKeptMembers).GetRuntimeField (string.Empty); + string noValueString = t.AssemblyQualifiedName; + typeof (ClassWithKeptMembers).GetRuntimeField (noValueString); } #endregion @@ -57,6 +76,16 @@ public static void TestGetRuntimeMethod () GetClassWithMethod ().GetRuntimeMethod ("This string will not be reached", Type.EmptyTypes); typeof (Derived).GetRuntimeMethod ("Method", Type.EmptyTypes); GetUnknownType ().GetRuntimeMethod (GetUnknownString (), Type.EmptyTypes); // IL2072 + + Type t = null; + t.GetRuntimeMethod ("This string will not be reached", Type.EmptyTypes); + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + noValue.GetRuntimeMethod ("This string will not be reached", Type.EmptyTypes); + + typeof (ClassWithKeptMembers).GetRuntimeMethod (null, Type.EmptyTypes); + typeof (ClassWithKeptMembers).GetRuntimeMethod (string.Empty, Type.EmptyTypes); + string noValueString = t.AssemblyQualifiedName; + typeof (ClassWithKeptMembers).GetRuntimeMethod (noValueString, Type.EmptyTypes); } #endregion @@ -71,6 +100,16 @@ public static void TestGetRuntimeProperty () GetClassWithProperty ().GetRuntimeProperty ("This string will not be reached"); typeof (Derived).GetRuntimeProperty ("Property"); GetUnknownType ().GetRuntimeProperty (GetUnknownString ()); // IL2072 + + Type t = null; + t.GetRuntimeProperty ("This string will not be reached"); + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + noValue.GetRuntimeProperty ("This string will not be reached"); + + typeof (ClassWithKeptMembers).GetRuntimeProperty (null); + typeof (ClassWithKeptMembers).GetRuntimeProperty (string.Empty); + string noValueString = t.AssemblyQualifiedName; + typeof (ClassWithKeptMembers).GetRuntimeProperty (noValueString); } #endregion diff --git a/test/Mono.Linker.Tests.Cases/Reflection/TypeDelegator.cs b/test/Mono.Linker.Tests.Cases/Reflection/TypeDelegator.cs index 583f7e4e9838..9b561d6b4fc6 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/TypeDelegator.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/TypeDelegator.cs @@ -3,6 +3,8 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; using System.Text; using Mono.Linker.Tests.Cases.Expectations.Assertions; @@ -12,7 +14,9 @@ public class TypeDelegator { public static void Main () { - _ = new System.Reflection.TypeDelegator (typeof (TypeUsedWithDelegator)).GetMethod ("Method"); + TestTypeUsedWithDelegator (); + TestNullValue (); + TestNoValue (); } [Kept] @@ -23,5 +27,35 @@ public static void Method () { } public static void UnrelatedMethod () { } } + + [Kept] + static void TestTypeUsedWithDelegator () + { + _ = new System.Reflection.TypeDelegator (typeof (TypeUsedWithDelegator)).GetMethod ("Method"); + } + + [Kept] + static void TestNullValue () + { + var typeDelegator = new System.Reflection.TypeDelegator (null); + RequireAll (typeDelegator); + } + + [Kept] + static void TestNoValue () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + var typeDelegator = new System.Reflection.TypeDelegator (noValue); + RequireAll (typeDelegator); + } + + [Kept] + public static void RequireAll ( + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] + System.Reflection.TypeDelegator t + ) + { } } } diff --git a/test/Mono.Linker.Tests.Cases/Reflection/UnderlyingSystemType.cs b/test/Mono.Linker.Tests.Cases/Reflection/UnderlyingSystemType.cs index 23bd298f735f..567665072d5a 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/UnderlyingSystemType.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/UnderlyingSystemType.cs @@ -4,9 +4,11 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Text; using System.Threading.Tasks; using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; namespace Mono.Linker.Tests.Cases.Reflection { @@ -15,7 +17,9 @@ class UnderlyingSystemType { public static void Main () { - _ = typeof (TypeUsedWithUnderlyingSystemType).UnderlyingSystemType.GetMethod (nameof (TypeUsedWithUnderlyingSystemType.Method)); + TestTypeUsedWithUnderlyingSystemType (); + TestNullValue (); + TestNoValue (); } [Kept] @@ -26,5 +30,26 @@ public static void Method () { } public static void OtherMethod () { } } + + [Kept] + static void TestTypeUsedWithUnderlyingSystemType () + { + _ = typeof (TypeUsedWithUnderlyingSystemType).UnderlyingSystemType.GetMethod (nameof (TypeUsedWithUnderlyingSystemType.Method)); + } + + [Kept] + static void TestNullValue () + { + Type t = null; + t.UnderlyingSystemType.RequiresAll (); + } + + [Kept] + static void TestNoValue () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + t.UnderlyingSystemType.RequiresAll (); + } } } From dcb533eb5cc10664e25181082e3a2589334c21dd Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 28 Feb 2022 18:42:19 +0000 Subject: [PATCH 4/5] PR feedback - Fix test - Replace exception with an assert --- .../TrimAnalysis/HandleCallAction.cs | 4 +--- .../Reflection/ObjectGetType.cs | 24 +++++++++++++++---- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs b/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs index c9eda8d29451..b03cd28a4da2 100644 --- a/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs +++ b/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs @@ -183,9 +183,7 @@ public bool Invoke (MethodProxy calledMethod, MultiValue instanceValue, IReadOnl return true; } - if (returnValue == null && !calledMethod.ReturnsVoid ()) - throw new InvalidOperationException ("No return value set for intrinsic"); - + Debug.Assert (returnValue != null || calledMethod.ReturnsVoid (), "Intrinsics must set a return value."); returnValue ??= MultiValueLattice.Top; // Validate that the return value has the correct annotations as per the method return value annotations diff --git a/test/Mono.Linker.Tests.Cases/Reflection/ObjectGetType.cs b/test/Mono.Linker.Tests.Cases/Reflection/ObjectGetType.cs index 12b8d5a5d0e4..9e908b915116 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/ObjectGetType.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/ObjectGetType.cs @@ -56,6 +56,7 @@ public static void Main () NullValue.Test (); NoValue.Test (); + UnknownValue.Test (); } [Kept] @@ -1484,20 +1485,35 @@ public static void Test () class NoValue { [Kept] + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions.RequiresAll) + "(Type)", nameof (Object.GetType) + "()")] + public static void Test () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + // Even though the above throws at runtime, we warn about the return value of GetType + noValue.GetType ().RequiresAll (); + } + } + + [Kept] + class UnknownValue + { + [Kept] + [KeptMember (".ctor()")] class TestType { } [Kept] - static TestType GetInstance () => null; + static TestType GetInstance () => new TestType (); [Kept] [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions.RequiresAll) + "(Type)", nameof (Object.GetType) + "()")] public static void Test () { - TestType noValue = GetInstance (); - // Even though this throws at runtime, we warn about the return value of GetType - noValue.GetType ().RequiresAll (); + TestType unknownValue = GetInstance (); + // Should warn about the return value of GetType + unknownValue.GetType ().RequiresAll (); } } } From 438503e29b5ae65a504f1d6122900c32dbef019b Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 28 Feb 2022 19:20:00 +0000 Subject: [PATCH 5/5] Add another test and remove assert Some of the new shared intrinsics that don't produce type values need to have some tracked value. Presumably this should really be Unknown, but currently they fall back on the shared logic to track a value with annotations (which for these intrinsics will be None). --- src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs | 5 +++-- .../DataFlow/MakeGenericDataFlow.cs | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs b/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs index dc70387eb308..216d5a1055ea 100644 --- a/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs +++ b/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs @@ -491,8 +491,9 @@ public bool Invoke (MethodProxy calledMethod, MultiValue instanceValue, IReadOnl return true; } - Debug.Assert (returnValue != null || calledMethod.ReturnsVoid (), "Intrinsics must set a return value."); - returnValue ??= MultiValueLattice.Top; + // For now, if the intrinsic doesn't set a return value, fall back on the annotations. + // Note that this will be DynamicallyAccessedMembers.None for the intrinsics which don't return types. + returnValue ??= calledMethod.ReturnsVoid () ? MultiValueLattice.Top : GetMethodReturnValue (calledMethod, returnValueDynamicallyAccessedMemberTypes); // Validate that the return value has the correct annotations as per the method return value annotations if (returnValueDynamicallyAccessedMemberTypes != 0) { diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/MakeGenericDataFlow.cs b/test/Mono.Linker.Tests.Cases/DataFlow/MakeGenericDataFlow.cs index 2481fac0d839..eebea73e0130 100644 --- a/test/Mono.Linker.Tests.Cases/DataFlow/MakeGenericDataFlow.cs +++ b/test/Mono.Linker.Tests.Cases/DataFlow/MakeGenericDataFlow.cs @@ -217,6 +217,7 @@ class MakeGenericMethod public static void Test () { TestNullMethod (); + TestNoValueMethod (); TestUnknownMethod (null); TestUnknownMethodButNoTypeArguments (null); TestNullTypeArgument (); @@ -260,6 +261,14 @@ static void TestNullMethod () mi.MakeGenericMethod (typeof (TestType)); } + [ExpectedWarning ("IL2060", nameof (MethodInfo.MakeGenericMethod) + "(Type[])")] + static void TestNoValueMethod () + { + // GetMethod(null) throws at runtime. + MethodInfo noValue = typeof (MakeGenericMethod).GetMethod (null); + noValue.MakeGenericMethod (typeof (TestType)); + } + [ExpectedWarning ("IL2060", nameof (MethodInfo.MakeGenericMethod))] static void TestUnknownMethod (MethodInfo mi) {