diff --git a/src/linker/Linker.Steps/MarkStep.cs b/src/linker/Linker.Steps/MarkStep.cs index e25dba452772..a01e10274d94 100644 --- a/src/linker/Linker.Steps/MarkStep.cs +++ b/src/linker/Linker.Steps/MarkStep.cs @@ -3538,6 +3538,8 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c } else { MarkFieldsFromReflectionCall (ref reflectionContext, systemTypeValue.TypeRepresented, stringValue.Contents, staticOnly); } + } else if (stringParam is NullValue) { + reflectionContext.RecordHandledPattern (); } else if (stringParam is MethodParameterValue) { // TODO: Check if parameter is annotated. reflectionContext.RecordUnrecognizedPattern ($"Expression call '{calledMethod.FullName}' inside '{callingMethodBody.Method.FullName}' was detected with 3rd argument which cannot be analyzed"); @@ -3788,6 +3790,60 @@ void MarkMethodsFromReflectionCall (ref ReflectionPatternContext reflectionConte } } + void MarkPropertiesFromReflectionCall (ref ReflectionPatternContext reflectionContext, TypeDefinition declaringType, string name, bool staticOnly = false) + { + bool foundMatch = false; + foreach (var property in declaringType.Properties) { + if (property.Name != name) + continue; + + bool markedAny = false; + var methodCalling = reflectionContext.MethodCalling; + + // It is not easy to reliably detect in the IL code whether the getter or setter (or both) are used. + // Be conservative and mark everything for the property. + var getter = property.GetMethod; + if (getter != null && (!staticOnly || staticOnly && getter.IsStatic)) { + reflectionContext.RecordRecognizedPattern (getter, () => _markStep.MarkIndirectlyCalledMethod (getter, new DependencyInfo (DependencyKind.AccessedViaReflection, methodCalling))); + markedAny = true; + } + + var setter = property.SetMethod; + if (setter != null && (!staticOnly || staticOnly && setter.IsStatic)) { + reflectionContext.RecordRecognizedPattern (setter, () => _markStep.MarkIndirectlyCalledMethod (setter, new DependencyInfo (DependencyKind.AccessedViaReflection, methodCalling))); + markedAny = true; + } + + if (markedAny) { + foundMatch = true; + reflectionContext.RecordRecognizedPattern (property, () => _markStep.MarkProperty (property, new DependencyInfo (DependencyKind.AccessedViaReflection, methodCalling))); + } + } + + if (!foundMatch) + reflectionContext.RecordUnrecognizedPattern ($"Reflection call '{reflectionContext.MethodCalled.FullName}' inside '{reflectionContext.MethodCalling.FullName}' could not resolve property `{name}` on type `{declaringType.FullName}`."); + } + + void MarkFieldsFromReflectionCall (ref ReflectionPatternContext reflectionContext, TypeDefinition declaringType, string name, bool staticOnly = false) + { + bool foundMatch = false; + var methodCalling = reflectionContext.MethodCalling; + foreach (var field in declaringType.Fields) { + if (field.Name != name) + continue; + + if (staticOnly && !field.IsStatic) + continue; + + foundMatch = true; + reflectionContext.RecordRecognizedPattern (field, () => _markStep.MarkField (field, new DependencyInfo (DependencyKind.AccessedViaReflection, methodCalling))); + break; + } + + if (!foundMatch) + reflectionContext.RecordUnrecognizedPattern ($"Reflection call '{reflectionContext.MethodCalled.FullName}' inside '{reflectionContext.MethodCalling.FullName}' could not resolve field `{name}` on type `{declaringType.FullName}`."); + } + string GetValueDescriptionForErrorMessage (ValueNode value) { switch (value) { @@ -3867,58 +3923,6 @@ string GetDynamicallyAccessedMemberKindsDescription (DynamicallyAccessedMemberKi return string.Join (" | ", results.Select (r => r.ToString ())); } - - void MarkPropertiesFromReflectionCall (ref ReflectionPatternContext reflectionContext, TypeDefinition declaringType, string name, bool staticOnly = false) - { - bool foundMatch = false; - foreach (var property in declaringType.Properties) { - if (property.Name != name) - continue; - - bool markedAny = false; - - // It is not easy to reliably detect in the IL code whether the getter or setter (or both) are used. - // Be conservative and mark everything for the property. - var getter = property.GetMethod; - if (getter != null && (!staticOnly || staticOnly && getter.IsStatic)) { - reflectionContext.RecordRecognizedPattern (getter, () => _markStep.MarkIndirectlyCalledMethod (getter)); - markedAny = true; - } - - var setter = property.SetMethod; - if (setter != null && (!staticOnly || staticOnly && setter.IsStatic)) { - reflectionContext.RecordRecognizedPattern (setter, () => _markStep.MarkIndirectlyCalledMethod (setter)); - markedAny = true; - } - - if (markedAny) { - foundMatch = true; - reflectionContext.RecordRecognizedPattern (property, () => _markStep.MarkProperty (property)); - } - } - - if (!foundMatch) - reflectionContext.RecordUnrecognizedPattern ($"Reflection call '{reflectionContext.MethodCalled.FullName}' inside '{reflectionContext.MethodCalling.FullName}' could not resolve property `{name}` on type `{declaringType.FullName}`."); - } - - void MarkFieldsFromReflectionCall (ref ReflectionPatternContext reflectionContext, TypeDefinition declaringType, string name, bool staticOnly = false) - { - bool foundMatch = false; - foreach (var field in declaringType.Fields) { - if (field.Name != name) - continue; - - if (staticOnly && !field.IsStatic) - continue; - - foundMatch = true; - reflectionContext.RecordRecognizedPattern (field, () => _markStep.MarkField (field)); - break; - } - - if (!foundMatch) - reflectionContext.RecordUnrecognizedPattern ($"Reflection call '{reflectionContext.MethodCalled.FullName}' inside '{reflectionContext.MethodCalling.FullName}' could not resolve field `{name}` on type `{declaringType.FullName}`."); - } } } diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/RecognizedReflectionAccessPatternAttribute.cs b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/RecognizedReflectionAccessPatternAttribute.cs index f7e54e71ffce..1edaed3f2401 100644 --- a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/RecognizedReflectionAccessPatternAttribute.cs +++ b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/RecognizedReflectionAccessPatternAttribute.cs @@ -28,7 +28,7 @@ public RecognizedReflectionAccessPatternAttribute (Type reflectionMethodType, st } public RecognizedReflectionAccessPatternAttribute (Type reflectionMethodType, string reflectionMethodName, Type [] reflectionMethodParameters, - Type accessedItemType, string accessedItemName, string [] accessedItemParameters) + Type accessedItemType, string accessedItemName, string [] accessedItemParameters = null) { if (reflectionMethodType == null) throw new ArgumentException ("Value cannot be null or empty.", nameof (reflectionMethodType)); diff --git a/test/Mono.Linker.Tests.Cases/Reflection/ExpressionCallString.cs b/test/Mono.Linker.Tests.Cases/Reflection/ExpressionCallString.cs index e5eec7b3575b..0d9dd5037d81 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/ExpressionCallString.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/ExpressionCallString.cs @@ -30,6 +30,26 @@ static void Branch_SystemTypeValueNode_KnownStringValue () TestNonExistingName (); } + [Kept] + static void Branch_SystemTypeValueNode_NullValueNode () + { + Expression.Call (typeof (ExpressionCallString), null, Type.EmptyTypes); + } + + [Kept] + static void Branch_NullValueNode () + { + Expression.Call ((Type)null, "OnlyCalledViaExpression", Type.EmptyTypes); + } + + [Kept] + static void Branch_MethodParameterValueNode (Type T, string s) + { + TestNonExistingTypeParameter (T); + TestNonExistingNameParameter (s); + } + + #region RecognizedReflectionAccessPatterns [RecognizedReflectionAccessPattern ( typeof (Expression), nameof (Expression.Call), new Type [] { typeof (Type), typeof (string), typeof (Type []), typeof (Expression []) }, typeof (ExpressionCallString), nameof (A), new Type [0])] @@ -39,7 +59,7 @@ static void Branch_SystemTypeValueNode_KnownStringValue () [Kept] static void TestByName (int i) { - string MethodName = string.Empty; + string MethodName = null; switch (i) { case 0: MethodName = "A"; @@ -51,7 +71,7 @@ static void TestByName (int i) break; } - Expression.Call (typeof (ExpressionCallString), MethodName, Type.EmptyTypes); + Expression.Call (typeof (ExpressionCallString), MethodName, null); } [RecognizedReflectionAccessPattern ( @@ -87,33 +107,9 @@ static void TestByNameWithParameters () IQueryable source = null; Expression.Call (typeof (ExpressionCallString), "Count", new Type [] { source.ElementType }, source.Expression); } + #endregion - [Kept] - static void Branch_SystemTypeValueNode_NullValueNode () - { - Expression.Call (typeof (ExpressionCallString), null, Type.EmptyTypes); - } - - [UnrecognizedReflectionAccessPattern ( - typeof (Expression), nameof (Expression.Call), new Type [] { typeof (Type), typeof (string), typeof (Type []), typeof (Expression []) })] - [Kept] - static void TestNonExistingName () - { - Expression.Call (typeof (ExpressionCallString), "NonExisting", Type.EmptyTypes); - } - - [Kept] - static void Branch_NullValueNode () - { - Expression.Call ((Type)null, "OnlyCalledViaExpression", Type.EmptyTypes); - } - - [Kept] - static Type FindType () - { - return typeof (ExpressionCallString); - } - + #region UnrecognizedReflectionAccessPatterns [UnrecognizedReflectionAccessPattern ( typeof (Expression), nameof (Expression.Call), new Type [] { typeof (Type), typeof (string), typeof (Type []), typeof (Expression []) })] [Kept] @@ -122,11 +118,12 @@ static void Branch_UnrecognizedPatterns () Expression.Call (FindType (), "OnlyCalledViaExpression", Type.EmptyTypes); } + [UnrecognizedReflectionAccessPattern ( + typeof (Expression), nameof (Expression.Call), new Type [] { typeof (Type), typeof (string), typeof (Type []), typeof (Expression []) })] [Kept] - static void Branch_MethodParameterValueNode (Type T, string s) + static void TestNonExistingName () { - TestNonExistingTypeParameter (T); - TestNonExistingNameParameter (s); + Expression.Call (typeof (ExpressionCallString), "NonExisting", Type.EmptyTypes); } [UnrecognizedReflectionAccessPattern ( @@ -144,6 +141,14 @@ static void TestNonExistingNameParameter (string s) { Expression.Call (typeof (ExpressionCallString), s, Type.EmptyTypes); } + #endregion + + #region Helpers + [Kept] + static Type FindType () + { + return typeof (ExpressionCallString); + } [Kept] static void A () { } @@ -170,5 +175,6 @@ protected static T Count (T t) { return default (T); } + #endregion } } diff --git a/test/Mono.Linker.Tests.Cases/Reflection/ExpressionFieldString.cs b/test/Mono.Linker.Tests.Cases/Reflection/ExpressionFieldString.cs index 2e2181259ec5..fe31e9ae599f 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/ExpressionFieldString.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/ExpressionFieldString.cs @@ -12,68 +12,54 @@ public static void Main () { Branch_SystemTypeValueNode_KnownStringValue_NonStatic (); Branch_SystemTypeValueNode_KnownStringValue_SaticOnly (); - Branch_SystemTypeValueNode_UnknownStringValue (); Branch_NullValueNode (); + Branch_SystemTypeValueNode_UnknownStringValue (); Branch_MethodParameterValueNode (typeof (ExpressionFieldString), "Foo"); Branch_UnrecognizedPatterns (); } [Kept] - private static int TestOnlyStatic1; - - private int TestOnlyStatic2; - - [Kept] - private int TestName1; - - [Kept] - private int TestName2; - - private int TestName3; - - private int TestName4; - - private int TestName5; - - [Kept] - class A - { - [Kept] - static int Foo; - } - - [Kept] - class B + static void Branch_SystemTypeValueNode_KnownStringValue_NonStatic () { - [Kept] - static int Foo; + TestFieldName (0); + TestFieldName (1); + TestType (0); + TestType (1); + StaticFieldExpected (); } [Kept] - static string UnknownString () + static void Branch_NullValueNode () { - return "unknownstring"; + var expr = Expression.Field (null, (Type)null, "TestName1"); } + #region RecognizedReflectionAccessPatterns + [RecognizedReflectionAccessPattern ( + typeof (Expression), nameof (Expression.Field), new Type [] { typeof (Expression), typeof (Type), typeof (string) }, + typeof (ExpressionFieldString), nameof (TestOnlyStatic1))] [Kept] - static void Branch_SystemTypeValueNode_KnownStringValue_NonStatic () + static void Branch_SystemTypeValueNode_KnownStringValue_SaticOnly () { - TestFieldName (0); - TestFieldName (1); - TestType (0); - TestType (1); + var expr = Expression.Field (null, typeof (ExpressionFieldString), "TestOnlyStatic1"); } + [RecognizedReflectionAccessPattern ( + typeof (Expression), nameof (Expression.Field), new Type [] { typeof (Expression), typeof (Type), typeof (string) }, + typeof (ExpressionFieldString), nameof (TestName2))] + [RecognizedReflectionAccessPattern ( + typeof (Expression), nameof (Expression.Field), new Type [] { typeof (Expression), typeof (Type), typeof (string) }, + typeof (ExpressionFieldString), nameof (TestName3))] [Kept] static void TestFieldName (int i) { - string FieldName = string.Empty; + string FieldName = null; switch (i) { case 0: - FieldName = "TestName1"; + FieldName = "TestName2"; break; case 1: - FieldName = "TestName2"; + FieldName = "TestName3"; break; default: break; @@ -82,6 +68,12 @@ static void TestFieldName (int i) Expression.Field (Expression.Parameter (typeof (int), "somename"), typeof (ExpressionFieldString), FieldName); } + [RecognizedReflectionAccessPattern ( + typeof (Expression), nameof (Expression.Field), new Type [] { typeof (Expression), typeof (Type), typeof (string) }, + typeof (A), "Foo")] + [RecognizedReflectionAccessPattern ( + typeof (Expression), nameof (Expression.Field), new Type [] { typeof (Expression), typeof (Type), typeof (string) }, + typeof (B), "Foo")] [Kept] static void TestType (int i) { @@ -99,14 +91,18 @@ static void TestType (int i) Expression.Field (null, T, "Foo"); } + #endregion + #region UnrecognizedReflectionAccessPatterns + [UnrecognizedReflectionAccessPattern ( + typeof (Expression), nameof (Expression.Field), new Type [] { typeof (Expression), typeof (Type), typeof (string) })] [Kept] - static void Branch_SystemTypeValueNode_KnownStringValue_SaticOnly () + static void StaticFieldExpected () { - var expr = Expression.Field (null, typeof (ExpressionFieldString), "TestOnlyStatic1"); - expr = Expression.Field (null, typeof (ExpressionFieldString), "TestOnlyStatic2"); + var expr = Expression.Field (null, typeof (ExpressionFieldString), "TestOnlyStatic2"); } + [UnrecognizedReflectionAccessPattern ( typeof (Expression), nameof (Expression.Field), new Type [] { typeof (Expression), typeof (Type), typeof (string) })] [Kept] @@ -115,12 +111,8 @@ static void Branch_SystemTypeValueNode_UnknownStringValue () var expr = Expression.Field (null, typeof (ExpressionFieldString), UnknownString ()); } - [Kept] - static void Branch_NullValueNode () - { - var expr = Expression.Field (null, (Type)null, "TestName3"); - } - + [UnrecognizedReflectionAccessPattern ( + typeof (Expression), nameof (Expression.Field), new Type [] { typeof (Expression), typeof (Type), typeof (string) })] [UnrecognizedReflectionAccessPattern ( typeof (Expression), nameof (Expression.Field), new Type [] { typeof (Expression), typeof (Type), typeof (string) })] [Kept] @@ -130,6 +122,8 @@ static void Branch_MethodParameterValueNode (Type T, string s) expr = Expression.Field (null, typeof (ExpressionFieldString), s); } + [UnrecognizedReflectionAccessPattern ( + typeof (Type), nameof (Type.GetType), new Type [] { typeof (string) })] [UnrecognizedReflectionAccessPattern ( typeof (Expression), nameof (Expression.Field), new Type [] { typeof (Expression), typeof (Type), typeof (string) })] [Kept] @@ -137,5 +131,45 @@ static void Branch_UnrecognizedPatterns () { var expr = Expression.Field (null, Type.GetType ("Foo"), "TestName5"); } + #endregion + + #region Helpers + [Kept] + private static int TestOnlyStatic1; + + private int TestOnlyStatic2; + + private int TestName1; + + [Kept] + private int TestName2; + + [Kept] + private int TestName3; + + private int TestName4; + + private int TestName5; + + [Kept] + class A + { + [Kept] + static int Foo; + } + + [Kept] + class B + { + [Kept] + static int Foo; + } + + [Kept] + static string UnknownString () + { + return "unknownstring"; + } + #endregion } } diff --git a/test/Mono.Linker.Tests.Cases/Reflection/ExpressionNewType.cs b/test/Mono.Linker.Tests.Cases/Reflection/ExpressionNewType.cs index fbde31c6f218..522e6ea32136 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/ExpressionNewType.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/ExpressionNewType.cs @@ -20,33 +20,16 @@ public static void Main () } [Kept] - class A - { - [Kept] - A () { } - } - - [Kept] - class B - { - [Kept] - B () { } - } - - [Kept] - class C { } - - [Kept] - class D { } - - class RemovedType { } - - [Kept] - static Type GetType () + static void Branch_NullValueNode () { - return typeof (D); + Expression.New (5 + 7 == 12 ? null : typeof (RemovedType)); } + #region RecognizedReflectionAccessPatterns + [RecognizedReflectionAccessPattern ( + typeof (Expression), nameof (Expression.New), new Type [] { typeof (Type) }, typeof (A), ".ctor", new Type [0])] + [RecognizedReflectionAccessPattern ( + typeof (Expression), nameof (Expression.New), new Type [] { typeof (Type) }, typeof (B), ".ctor", new Type [0])] [Kept] static void Branch_SystemTypeValueNode (int i) { @@ -64,13 +47,9 @@ static void Branch_SystemTypeValueNode (int i) Expression.New (T); } + #endregion - [Kept] - static void Branch_NullValueNode () - { - Expression.New (5 + 7 == 12 ? null : typeof (RemovedType)); - } - + #region UnrecognizedReflectionAccessPatterns [UnrecognizedReflectionAccessPattern (typeof (Expression), nameof (Expression.New), new Type [] { typeof (Type) })] [Kept] static void Branch_MethodParameterValueNode (Type T) @@ -78,6 +57,9 @@ static void Branch_MethodParameterValueNode (Type T) Expression.New (T); } + [UnrecognizedReflectionAccessPattern ( + typeof (Type), nameof (Type.GetType), new Type [] { typeof (string) })] + [UnrecognizedReflectionAccessPattern (typeof (Expression), nameof (Expression.New), new Type [] { typeof (Type) })] [UnrecognizedReflectionAccessPattern (typeof (Expression), nameof (Expression.New), new Type [] { typeof (Type) })] [Kept] static void Branch_UnrecognizedPatterns () @@ -85,5 +67,36 @@ static void Branch_UnrecognizedPatterns () Expression.New (Type.GetType ("RemovedType")); Expression.New (GetType ()); } + #endregion + + #region Helpers + [Kept] + class A + { + [Kept] + A () { } + } + + [Kept] + class B + { + [Kept] + B () { } + } + + [Kept] + class C { } + + [Kept] + class D { } + + class RemovedType { } + + [Kept] + static Type GetType () + { + return typeof (D); + } + #endregion } } diff --git a/test/Mono.Linker.Tests.Cases/Reflection/ExpressionPropertyString.cs b/test/Mono.Linker.Tests.Cases/Reflection/ExpressionPropertyString.cs index af31e2d9ecf1..45ab976b5856 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/ExpressionPropertyString.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/ExpressionPropertyString.cs @@ -27,66 +27,47 @@ public static void Main () class Foo { [Kept] - [KeptBackingField] - private static int TestOnlyStatic1 { [Kept] get; [Kept] set; } - - private int TestOnlyStatic2 { get; } - - [Kept] - [KeptBackingField] - private int TestName1 { [Kept] get; } - - [Kept] - [KeptBackingField] - private int TestName2 { [Kept] get; } - - private int TestName3 { get; } - - private int TestName4 { get; } - - private int TestName5 { get; } - - [Kept] - class A - { - [Kept] - [KeptBackingField] - static int Foo { [Kept] get; } - } - - [Kept] - class B + public void Branch_SystemTypeValueNode_KnownStringValue_NonStatic () { - [Kept] - [KeptBackingField] - static int Foo { [Kept] get; } + TestPropertyName (0); + TestPropertyName (1); + TestByType (0); + TestByType (1); + StaticPropertyExpected (); } [Kept] - private string UnknownString () + public void Branch_NullValueNode () { - return "unknownstring"; + var expr = Expression.Property (null, (Type)null, "TestName1"); } + #region RecognizedReflectionAccessPattern + [RecognizedReflectionAccessPattern ( + typeof (Expression), nameof (Expression.Property), new Type [] { typeof (Expression), typeof (Type), typeof (string) }, + typeof (Foo), nameof (TestOnlyStatic1))] [Kept] - public void Branch_SystemTypeValueNode_KnownStringValue_NonStatic () + public void Branch_SystemTypeValueNode_KnownStringValue_SaticOnly () { - TestPropertyName (0); - TestPropertyName (1); - TestType (0); - TestType (1); + var expr = Expression.Property (null, typeof (Foo), "TestOnlyStatic1"); } + [RecognizedReflectionAccessPattern ( + typeof (Expression), nameof (Expression.Property), new Type [] { typeof (Expression), typeof (Type), typeof (string) }, + typeof (Foo), nameof (TestName2))] + [RecognizedReflectionAccessPattern ( + typeof (Expression), nameof (Expression.Property), new Type [] { typeof (Expression), typeof (Type), typeof (string) }, + typeof (Foo), nameof (TestName3))] [Kept] static void TestPropertyName (int i) { - string PropertyName = string.Empty; + string PropertyName = null; switch (i) { case 0: - PropertyName = "TestName1"; + PropertyName = "TestName2"; break; case 1: - PropertyName = "TestName2"; + PropertyName = "TestName3"; break; default: break; @@ -95,8 +76,14 @@ static void TestPropertyName (int i) Expression.Property (Expression.Parameter (typeof (int), "somename"), typeof (Foo), PropertyName); } + [RecognizedReflectionAccessPattern ( + typeof (Expression), nameof (Expression.Property), new Type [] { typeof (Expression), typeof (Type), typeof (string) }, + typeof (A), "Foo")] + [RecognizedReflectionAccessPattern ( + typeof (Expression), nameof (Expression.Property), new Type [] { typeof (Expression), typeof (Type), typeof (string) }, + typeof (B), "Foo")] [Kept] - static void TestType (int i) + static void TestByType (int i) { Type T = (Type)null; switch (i) { @@ -112,12 +99,15 @@ static void TestType (int i) Expression.Property (null, T, "Foo"); } + #endregion + #region UnrecognizedReflectionAccessPatterns + [UnrecognizedReflectionAccessPattern ( + typeof (Expression), nameof (Expression.Property), new Type [] { typeof (Expression), typeof (Type), typeof (string) })] [Kept] - public void Branch_SystemTypeValueNode_KnownStringValue_SaticOnly () + public void StaticPropertyExpected () { - var expr = Expression.Property (null, typeof (Foo), "TestOnlyStatic1"); - expr = Expression.Property (null, typeof (Foo), "TestOnlyStatic2"); + var expr = Expression.Property (null, typeof (Foo), "TestOnlyStatic2"); } [UnrecognizedReflectionAccessPattern ( @@ -130,12 +120,6 @@ public void Branch_SystemTypeValueNode_UnknownStringValue () [UnrecognizedReflectionAccessPattern ( typeof (Expression), nameof (Expression.Property), new Type [] { typeof (Expression), typeof (Type), typeof (string) })] - [Kept] - public void Branch_NullValueNode () - { - var expr = Expression.Property (null, (Type)null, "TestName3"); - } - [UnrecognizedReflectionAccessPattern ( typeof (Expression), nameof (Expression.Property), new Type [] { typeof (Expression), typeof (Type), typeof (string) })] [Kept] @@ -145,6 +129,8 @@ public void Branch_MethodParameterValueNode (Type T, string s) expr = Expression.Property (null, typeof (Foo), s); } + [UnrecognizedReflectionAccessPattern ( + typeof (Type), nameof (Type.GetType), new Type [] { typeof (string) })] [UnrecognizedReflectionAccessPattern ( typeof (Expression), nameof (Expression.Property), new Type [] { typeof (Expression), typeof (Type), typeof (string) })] [Kept] @@ -152,6 +138,51 @@ public void Branch_UnrecognizedPatterns () { var expr = Expression.Property (null, Type.GetType ("Foo"), "TestName5"); } + #endregion + + #region Helpers + [Kept] + class A + { + [Kept] + [KeptBackingField] + static int Foo { [Kept] get; } + } + + [Kept] + class B + { + [Kept] + [KeptBackingField] + static int Foo { [Kept] get; } + } + + [Kept] + [KeptBackingField] + private static int TestOnlyStatic1 { [Kept] get; [Kept] set; } + + private int TestOnlyStatic2 { get; } + + private int TestName1 { get; } + + [Kept] + [KeptBackingField] + private int TestName2 { [Kept] get; } + + [Kept] + [KeptBackingField] + private int TestName3 { [Kept] get; } + + private int TestName4 { get; } + + private int TestName5 { get; } + + [Kept] + private string UnknownString () + { + return "unknownstring"; + } + #endregion } } } \ No newline at end of file diff --git a/test/Mono.Linker.Tests/TestCasesRunner/TestCaseMetadaProvider.cs b/test/Mono.Linker.Tests/TestCasesRunner/TestCaseMetadaProvider.cs index 5e2964897933..edda8ac2f407 100644 --- a/test/Mono.Linker.Tests/TestCasesRunner/TestCaseMetadaProvider.cs +++ b/test/Mono.Linker.Tests/TestCasesRunner/TestCaseMetadaProvider.cs @@ -86,22 +86,7 @@ public virtual void CustomizeLinker (LinkerDriver linker, LinkerCustomizations c }; } - if (_testCaseTypeDefinition.CustomAttributes.Any (attr => - attr.AttributeType.Name == nameof (VerifyAllReflectionAccessPatternsAreValidatedAttribute)) - || _testCaseTypeDefinition.AllMethods ().Any (method => method.CustomAttributes.Any (attr => - attr.AttributeType.Name == nameof (RecognizedReflectionAccessPatternAttribute) || - attr.AttributeType.Name == nameof (UnrecognizedReflectionAccessPatternAttribute)))) { - customizations.ReflectionPatternRecorder = new TestReflectionPatternRecorder (); - customizations.CustomizeContext += context => { - context.ReflectionPatternRecorder = customizations.ReflectionPatternRecorder; - }; - } else if (_testCaseTypeDefinition.HasNestedTypes - && _testCaseTypeDefinition.NestedTypes.Any (nestedType => - nestedType.CustomAttributes.Any (attr => - attr.AttributeType.Name == nameof (VerifyAllReflectionAccessPatternsAreValidatedAttribute) - || _testCaseTypeDefinition.AllMethods ().Any (method => method.CustomAttributes.Any (attr => - attr.AttributeType.Name == nameof (RecognizedReflectionAccessPatternAttribute) || - attr.AttributeType.Name == nameof (UnrecognizedReflectionAccessPatternAttribute)))))) { + if (ValidatesReflectionAccessPatterns(_testCaseTypeDefinition)) { customizations.ReflectionPatternRecorder = new TestReflectionPatternRecorder (); customizations.CustomizeContext += context => { context.ReflectionPatternRecorder = customizations.ReflectionPatternRecorder; @@ -109,6 +94,26 @@ public virtual void CustomizeLinker (LinkerDriver linker, LinkerCustomizations c } } + private bool ValidatesReflectionAccessPatterns (TypeDefinition testCaseTypeDefinition) + { + if (testCaseTypeDefinition.HasNestedTypes) { + var nestedTypes = new Queue (testCaseTypeDefinition.NestedTypes.ToList ()); + while (nestedTypes.Count > 0) { + if (ValidatesReflectionAccessPatterns (nestedTypes.Dequeue ())) + return true; + } + } + + if (testCaseTypeDefinition.CustomAttributes.Any (attr => + attr.AttributeType.Name == nameof (VerifyAllReflectionAccessPatternsAreValidatedAttribute)) + || testCaseTypeDefinition.AllMethods ().Any (method => method.CustomAttributes.Any (attr => + attr.AttributeType.Name == nameof (RecognizedReflectionAccessPatternAttribute) || + attr.AttributeType.Name == nameof (UnrecognizedReflectionAccessPatternAttribute)))) + return true; + + return false; + } + #if NETCOREAPP public static IEnumerable GetTrustedPlatformAssemblies () {