From a2f2a629328a136c2cd18f2cd729e3854de2aeb4 Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Wed, 21 Jul 2021 09:51:16 -0500 Subject: [PATCH] Resolve ILLink warnings in System.Linq.Expressions (Final) (#55856) * Resolve ILLink warnings in System.Linq.Expressions (Final) Suppress ILLink warnings for operator methods now that https://github.com/mono/linker/issues/1821 is resolved. Add TrimmingTests for Linq.Expressions operators. Fix #45623 --- .../ref/System.Linq.Expressions.cs | 5 - .../src/ILLink/ILLink.Suppressions.xml | 17 --- .../System/Dynamic/Utils/TypeExtensions.cs | 6 +- .../src/System/Dynamic/Utils/TypeUtils.cs | 7 +- .../Linq/Expressions/BinaryExpression.cs | 2 + .../Linq/Expressions/UnaryExpression.cs | 11 +- .../TrimmingTests/BinaryOperatorTests.cs | 65 ++++++++++ .../TrimmingTests/ConvertOperatorTests.cs | 91 ++++++++++++++ .../tests/TrimmingTests/EqualOperatorTests.cs | 115 ++++++++++++++++++ ...System.Linq.Expressions.TrimmingTests.proj | 8 ++ 10 files changed, 294 insertions(+), 33 deletions(-) delete mode 100644 src/libraries/System.Linq.Expressions/src/ILLink/ILLink.Suppressions.xml create mode 100644 src/libraries/System.Linq.Expressions/tests/TrimmingTests/BinaryOperatorTests.cs create mode 100644 src/libraries/System.Linq.Expressions/tests/TrimmingTests/ConvertOperatorTests.cs create mode 100644 src/libraries/System.Linq.Expressions/tests/TrimmingTests/EqualOperatorTests.cs diff --git a/src/libraries/System.Linq.Expressions/ref/System.Linq.Expressions.cs b/src/libraries/System.Linq.Expressions/ref/System.Linq.Expressions.cs index b9f19db7f34f8..862a87c1583aa 100644 --- a/src/libraries/System.Linq.Expressions/ref/System.Linq.Expressions.cs +++ b/src/libraries/System.Linq.Expressions/ref/System.Linq.Expressions.cs @@ -451,11 +451,8 @@ protected Expression(System.Linq.Expressions.ExpressionType nodeType, System.Typ public static System.Linq.Expressions.GotoExpression Continue(System.Linq.Expressions.LabelTarget target) { throw null; } public static System.Linq.Expressions.GotoExpression Continue(System.Linq.Expressions.LabelTarget target, System.Type type) { throw null; } public static System.Linq.Expressions.UnaryExpression Convert(System.Linq.Expressions.Expression expression, System.Type type) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Creating Expressions requires unreferenced code because the members being referenced by the Expression may be trimmed.")] public static System.Linq.Expressions.UnaryExpression Convert(System.Linq.Expressions.Expression expression, System.Type type, System.Reflection.MethodInfo? method) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Creating Expressions requires unreferenced code because the members being referenced by the Expression may be trimmed.")] public static System.Linq.Expressions.UnaryExpression ConvertChecked(System.Linq.Expressions.Expression expression, System.Type type) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Creating Expressions requires unreferenced code because the members being referenced by the Expression may be trimmed.")] public static System.Linq.Expressions.UnaryExpression ConvertChecked(System.Linq.Expressions.Expression expression, System.Type type, System.Reflection.MethodInfo? method) { throw null; } public static System.Linq.Expressions.DebugInfoExpression DebugInfo(System.Linq.Expressions.SymbolDocumentInfo document, int startLine, int startColumn, int endLine, int endColumn) { throw null; } public static System.Linq.Expressions.UnaryExpression Decrement(System.Linq.Expressions.Expression expression) { throw null; } @@ -573,9 +570,7 @@ protected Expression(System.Linq.Expressions.ExpressionType nodeType, System.Typ public static System.Linq.Expressions.IndexExpression MakeIndex(System.Linq.Expressions.Expression instance, System.Reflection.PropertyInfo? indexer, System.Collections.Generic.IEnumerable? arguments) { throw null; } public static System.Linq.Expressions.MemberExpression MakeMemberAccess(System.Linq.Expressions.Expression? expression, System.Reflection.MemberInfo member) { throw null; } public static System.Linq.Expressions.TryExpression MakeTry(System.Type? type, System.Linq.Expressions.Expression body, System.Linq.Expressions.Expression? @finally, System.Linq.Expressions.Expression? fault, System.Collections.Generic.IEnumerable? handlers) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Creating Expressions requires unreferenced code because the members being referenced by the Expression may be trimmed.")] public static System.Linq.Expressions.UnaryExpression MakeUnary(System.Linq.Expressions.ExpressionType unaryType, System.Linq.Expressions.Expression operand, System.Type type) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Creating Expressions requires unreferenced code because the members being referenced by the Expression may be trimmed.")] public static System.Linq.Expressions.UnaryExpression MakeUnary(System.Linq.Expressions.ExpressionType unaryType, System.Linq.Expressions.Expression operand, System.Type type, System.Reflection.MethodInfo? method) { throw null; } public static System.Linq.Expressions.MemberMemberBinding MemberBind(System.Reflection.MemberInfo member, System.Collections.Generic.IEnumerable bindings) { throw null; } public static System.Linq.Expressions.MemberMemberBinding MemberBind(System.Reflection.MemberInfo member, params System.Linq.Expressions.MemberBinding[] bindings) { throw null; } diff --git a/src/libraries/System.Linq.Expressions/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Linq.Expressions/src/ILLink/ILLink.Suppressions.xml deleted file mode 100644 index f58a4f028b196..0000000000000 --- a/src/libraries/System.Linq.Expressions/src/ILLink/ILLink.Suppressions.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - ILLink - IL2026 - member - M:System.Linq.Expressions.Expression.Convert(System.Linq.Expressions.Expression,System.Type) - - - ILLink - IL2070 - member - M:System.Dynamic.Utils.TypeExtensions.GetAnyStaticMethodValidated(System.Type,System.String,System.Type[]) - - - diff --git a/src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/TypeExtensions.cs b/src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/TypeExtensions.cs index 5d5390a874351..4b57dba01726a 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/TypeExtensions.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/TypeExtensions.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection; namespace System.Dynamic.Utils @@ -15,7 +16,10 @@ internal static class TypeExtensions /// Returns the matching method if the parameter types are reference /// assignable from the provided type arguments, otherwise null. /// - public static MethodInfo? GetAnyStaticMethodValidated(this Type type, string name, Type[] types) + public static MethodInfo? GetAnyStaticMethodValidated( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] this Type type, + string name, + Type[] types) { Debug.Assert(types != null); MethodInfo? method = type.GetMethod(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly, null, types, null); diff --git a/src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/TypeUtils.cs b/src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/TypeUtils.cs index 3a0fc56030a92..f676a13dce7ee 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/TypeUtils.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/TypeUtils.cs @@ -623,7 +623,8 @@ public static bool IsImplicitlyConvertibleTo(this Type source, Type destination) || IsImplicitBoxingConversion(source, destination) || IsImplicitNullableConversion(source, destination); - [RequiresUnreferencedCode(Expression.ExpressionRequiresUnreferencedCode)] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern", + Justification = "The trimmer doesn't remove operators when System.Linq.Expressions is used. See https://github.com/mono/linker/pull/2125.")] public static MethodInfo? GetUserDefinedCoercionMethod(Type convertFrom, Type convertToType) { Type nnExprType = GetNonNullableType(convertFrom); @@ -829,8 +830,12 @@ private static bool IsImplicitNullableConversion(Type source, Type destination) /// op_False, because we have to do runtime lookup for those. It may /// not work right for unary operators in general. /// + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2067:UnrecognizedReflectionPattern", + Justification = "The trimmer doesn't remove operators when System.Linq.Expressions is used. See https://github.com/mono/linker/pull/2125.")] public static MethodInfo? GetBooleanOperator(Type type, string name) { + Debug.Assert(name == "op_False" || name == "op_True"); + do { MethodInfo? result = type.GetAnyStaticMethodValidated(name, new[] { type }); diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/BinaryExpression.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/BinaryExpression.cs index bccac308a0133..f0581c4e0d0aa 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/BinaryExpression.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/BinaryExpression.cs @@ -710,6 +710,8 @@ private static BinaryExpression GetUserDefinedAssignOperatorOrThrow(ExpressionTy return b; } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2072:UnrecognizedReflectionPattern", + Justification = "The trimmer doesn't remove operators when System.Linq.Expressions is used. See https://github.com/mono/linker/pull/2125.")] private static MethodInfo? GetUserDefinedBinaryOperator(ExpressionType binaryType, Type leftType, Type rightType, string name) { // This algorithm is wrong, we should be checking for uniqueness and erroring if diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/UnaryExpression.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/UnaryExpression.cs index 99fb5c4ba2691..eea96673e50a9 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/UnaryExpression.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/UnaryExpression.cs @@ -279,8 +279,6 @@ private Expression ReduceIndex() /// /// The property of the result. /// This expression if no children changed, or an expression with the updated children. - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "A UnaryExpression has already been created. The original creator will get a warning that it is not trim compatible.")] public UnaryExpression Update(Expression operand) { if (operand == Operand) @@ -302,7 +300,6 @@ public partial class Expression /// The that results from calling the appropriate factory method. /// Thrown when does not correspond to a unary expression. /// Thrown when is null. - [RequiresUnreferencedCode(ExpressionRequiresUnreferencedCode)] public static UnaryExpression MakeUnary(ExpressionType unaryType, Expression operand, Type type) { return MakeUnary(unaryType, operand, type, method: null); @@ -318,7 +315,6 @@ public static UnaryExpression MakeUnary(ExpressionType unaryType, Expression ope /// The that results from calling the appropriate factory method. /// Thrown when does not correspond to a unary expression. /// Thrown when is null. - [RequiresUnreferencedCode(ExpressionRequiresUnreferencedCode)] public static UnaryExpression MakeUnary(ExpressionType unaryType, Expression operand, Type type, MethodInfo? method) => unaryType switch { @@ -356,6 +352,8 @@ private static UnaryExpression GetUserDefinedUnaryOperatorOrThrow(ExpressionType throw Error.UnaryOperatorNotDefined(unaryType, operand.Type); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2072:UnrecognizedReflectionPattern", + Justification = "The trimmer doesn't remove operators when System.Linq.Expressions is used. See https://github.com/mono/linker/pull/2125.")] private static UnaryExpression? GetUserDefinedUnaryOperator(ExpressionType unaryType, string name, Expression operand) { Type operandType = operand.Type; @@ -402,7 +400,6 @@ private static UnaryExpression GetMethodBasedUnaryOperator(ExpressionType unaryT throw Error.OperandTypesDoNotMatchParameters(unaryType, method.Name); } - [RequiresUnreferencedCode(Expression.ExpressionRequiresUnreferencedCode)] private static UnaryExpression GetUserDefinedCoercionOrThrow(ExpressionType coercionType, Expression expression, Type convertToType) { UnaryExpression? u = GetUserDefinedCoercion(coercionType, expression, convertToType); @@ -413,7 +410,6 @@ private static UnaryExpression GetUserDefinedCoercionOrThrow(ExpressionType coer throw Error.CoercionOperatorNotDefined(expression.Type, convertToType); } - [RequiresUnreferencedCode(Expression.ExpressionRequiresUnreferencedCode)] private static UnaryExpression? GetUserDefinedCoercion(ExpressionType coercionType, Expression expression, Type convertToType) { MethodInfo? method = TypeUtils.GetUserDefinedCoercionMethod(expression.Type, convertToType); @@ -746,7 +742,6 @@ public static UnaryExpression Convert(Expression expression, Type type) /// is not null and the method it represents returns void, is not static (Shared in Visual Basic), or does not take exactly one argument. /// More than one method that matches the description was found. /// No conversion operator is defined between .Type and .-or-.Type is not assignable to the argument type of the method represented by .-or-The return type of the method represented by is not assignable to .-or-.Type or is a nullable value type and the corresponding non-nullable value type does not equal the argument type or the return type, respectively, of the method represented by . - [RequiresUnreferencedCode(ExpressionRequiresUnreferencedCode)] public static UnaryExpression Convert(Expression expression, Type type, MethodInfo? method) { ExpressionUtils.RequiresCanRead(expression, nameof(expression)); @@ -771,7 +766,6 @@ public static UnaryExpression Convert(Expression expression, Type type, MethodIn /// /// or is null. /// No conversion operator is defined between .Type and . - [RequiresUnreferencedCode(ExpressionRequiresUnreferencedCode)] public static UnaryExpression ConvertChecked(Expression expression, Type type) { return ConvertChecked(expression, type, method: null); @@ -788,7 +782,6 @@ public static UnaryExpression ConvertChecked(Expression expression, Type type) /// is not null and the method it represents returns void, is not static (Shared in Visual Basic), or does not take exactly one argument. /// More than one method that matches the description was found. /// No conversion operator is defined between .Type and .-or-.Type is not assignable to the argument type of the method represented by .-or-The return type of the method represented by is not assignable to .-or-.Type or is a nullable value type and the corresponding non-nullable value type does not equal the argument type or the return type, respectively, of the method represented by . - [RequiresUnreferencedCode(ExpressionRequiresUnreferencedCode)] public static UnaryExpression ConvertChecked(Expression expression, Type type, MethodInfo? method) { ExpressionUtils.RequiresCanRead(expression, nameof(expression)); diff --git a/src/libraries/System.Linq.Expressions/tests/TrimmingTests/BinaryOperatorTests.cs b/src/libraries/System.Linq.Expressions/tests/TrimmingTests/BinaryOperatorTests.cs new file mode 100644 index 0000000000000..5598b2d92afb7 --- /dev/null +++ b/src/libraries/System.Linq.Expressions/tests/TrimmingTests/BinaryOperatorTests.cs @@ -0,0 +1,65 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Linq.Expressions; + +/// +/// Tests that Expression.Add expressions still work correctly and find +/// the + operator in a trimmed app. +/// +internal class Program +{ + static int Main(string[] args) + { + ParameterExpression leftParameter = Expression.Parameter(typeof(Class1)); + ParameterExpression rightParameter = Expression.Parameter(typeof(Class1)); + ParameterExpression result = Expression.Variable(typeof(Class1)); + + Func func = + Expression.Lambda>( + Expression.Block( + new[] { result }, + Expression.Assign(result, Expression.Add(leftParameter, rightParameter)), + result), + leftParameter, rightParameter) + .Compile(); + + Class1 actual = func(new Class1("left"), new Class1("right")); + if (actual.Name != "left+right") + { + return -1; + } + + // make sure Class2 was trimmed since it wasn't used, even though Class1 has a binary operator using it + int i = 2; + if (typeof(Program).Assembly.GetType("Class" + i) != null) + { + return -2; + } + + return 100; + } +} + +internal class Class1 +{ + public Class1(string name) => Name = name; + + public string Name { get; set; } + + public static Class1 operator +(Class1 left, Class1 right) => + new Class1($"{left.Name}+{right.Name}"); + + public static Class1 operator +(Class1 left, Class2 right) => + new Class1($"{left.Name}+{right.Name}2"); + public static Class2 operator +(Class2 left, Class1 right) => + new Class2($"{left.Name}2+{right.Name}"); +} + +internal class Class2 +{ + public Class2(string name) => Name = name; + + public string Name { get; set; } +} diff --git a/src/libraries/System.Linq.Expressions/tests/TrimmingTests/ConvertOperatorTests.cs b/src/libraries/System.Linq.Expressions/tests/TrimmingTests/ConvertOperatorTests.cs new file mode 100644 index 0000000000000..de29045cb604e --- /dev/null +++ b/src/libraries/System.Linq.Expressions/tests/TrimmingTests/ConvertOperatorTests.cs @@ -0,0 +1,91 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Linq.Expressions; +using System.Reflection; + +/// +/// Tests that Expression.Convert expressions still work correctly and find +/// implicit and explicit operators in a trimmed app. +/// +internal class Program +{ + static int Main(string[] args) + { + Type[] convertTypes = new Type[] { typeof(Class2), typeof(Class3) }; + + ParameterExpression class1Parameter = Expression.Parameter(typeof(Class1), "class1"); + MethodInfo getNameMethodInfo = typeof(Program).GetMethod("GetName"); + foreach (Type convertType in convertTypes) + { + UnaryExpression conversion = Expression.Convert(class1Parameter, convertType); + + Func getNameFunc = + Expression.Lambda>( + Expression.Call(null, getNameMethodInfo, conversion), + class1Parameter) + .Compile(); + + string name = getNameFunc(new Class1() { Name = convertType.Name }); + if (convertType.Name == "Class2") + { + if (name != "Class2_implicit") + { + return -1; + } + } + else if (convertType.Name == "Class3") + { + if (name != "Class3_explicit") + { + return -2; + } + } + else + { + return -3; + } + } + + // make sure Class4 was trimmed since it wasn't used, even though Class1 has a conversion operator to it + int i = 4; + if (typeof(Program).Assembly.GetType("Class" + i) != null) + { + return -4; + } + + return 100; + } + + public static string GetName(IHasName hasName) => hasName.Name; +} + +interface IHasName +{ + string Name { get; } +} + +internal class Class1 : IHasName +{ + public string Name { get; set; } + + public static implicit operator Class2(Class1 class1) => new Class2() { Name = class1.Name + "_implicit" }; + public static explicit operator Class3(Class1 class1) => new Class3() { Name = class1.Name + "_explicit" }; + public static implicit operator Class4(Class1 class1) => new Class4() { Name = class1.Name + "_implicit" }; +} + +internal class Class2 : IHasName +{ + public string Name { get; set; } +} + +internal class Class3 : IHasName +{ + public string Name { get; set; } +} + +internal class Class4 : IHasName +{ + public string Name { get; set; } +} \ No newline at end of file diff --git a/src/libraries/System.Linq.Expressions/tests/TrimmingTests/EqualOperatorTests.cs b/src/libraries/System.Linq.Expressions/tests/TrimmingTests/EqualOperatorTests.cs new file mode 100644 index 0000000000000..41a9d4a47e2cf --- /dev/null +++ b/src/libraries/System.Linq.Expressions/tests/TrimmingTests/EqualOperatorTests.cs @@ -0,0 +1,115 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; + +/// +/// Tests that Expression.Equal/NotEqual expressions still work correctly and find +/// equality operators in a trimmed app. +/// +internal class Program +{ + static int Main(string[] args) + { + List<(object left, object right, ExpressionType expressionType, bool expected)> testData = new() + { + (new Class1("left"), new Class1("right"), ExpressionType.Equal, true), + (new Class1("left"), new Class1("notright"), ExpressionType.Equal, false), + (new Class1("left"), new Class1("right"), ExpressionType.NotEqual, false), + (new Class1("left"), new Class1("notright"), ExpressionType.NotEqual, true), + + (new Class1("left"), new Class2("right"), ExpressionType.Equal, true), + (new Class1("left"), new Class2("notright"), ExpressionType.Equal, false), + (new Class1("left"), new Class2("right"), ExpressionType.NotEqual, false), + (new Class1("left"), new Class2("notright"), ExpressionType.NotEqual, true), + }; + + foreach ((object left, object right, ExpressionType expressionType, bool expected) in testData) + { + ParameterExpression leftParameter = Expression.Parameter(typeof(object)); + ParameterExpression rightParameter = Expression.Parameter(typeof(object)); + + Expression leftConverted = Expression.Convert(leftParameter, left.GetType()); + Expression rightConverted = Expression.Convert(rightParameter, right.GetType()); + Expression condition; + if (expressionType == ExpressionType.Equal) + { + condition = Expression.Equal(leftConverted, rightConverted); + } + else + { + condition = Expression.NotEqual(leftConverted, rightConverted); + } + + ParameterExpression result = Expression.Variable(typeof(bool)); + + Func func = + Expression.Lambda>( + Expression.Block( + new[] { result }, + Expression.IfThenElse( + condition, + Expression.Assign(result, Expression.Constant(true)), + Expression.Assign(result, Expression.Constant(false))), + result), + leftParameter, rightParameter) + .Compile(); + + bool actual = func(left, right); + if (actual != expected) + { + return -1; + } + } + + // make sure Class3 was trimmed since it wasn't used, even though Class1 has equality operators to it + int i = 3; + if (typeof(Program).Assembly.GetType("Class" + i) != null) + { + return -2; + } + + return 100; + } +} + +#pragma warning disable CS0660 // Type defines operator == or operator != but does not override Object.Equals(object o) +#pragma warning disable CS0661 // Type defines operator == or operator != but does not override Object.GetHashCode() +internal class Class1 +#pragma warning restore CS0661 // Type defines operator == or operator != but does not override Object.GetHashCode() +#pragma warning restore CS0660 // Type defines operator == or operator != but does not override Object.Equals(object o) +{ + public Class1(string name) => Name = name; + + public string Name { get; set; } + + // use very unique rules to ensure these operators get invoked + public static bool operator ==(Class1 left, Class1 right) => + left.Name == "left" && right.Name == "right"; + public static bool operator !=(Class1 left, Class1 right) => + !(left.Name == "left" && right.Name == "right"); + + public static bool operator ==(Class1 left, Class2 right) => + left.Name == "left" && right.Name == "right"; + public static bool operator !=(Class1 left, Class2 right) => + !(left.Name == "left" && right.Name == "right"); + + public static bool operator ==(Class1 left, Class3 right) => left.Name == right.Name; + public static bool operator !=(Class1 left, Class3 right) => left.Name == right.Name; +} + +internal class Class2 +{ + public Class2(string name) => Name = name; + + public string Name { get; set; } +} + +internal class Class3 +{ + public Class3(string name) => Name = name; + + public string Name { get; set; } +} diff --git a/src/libraries/System.Linq.Expressions/tests/TrimmingTests/System.Linq.Expressions.TrimmingTests.proj b/src/libraries/System.Linq.Expressions/tests/TrimmingTests/System.Linq.Expressions.TrimmingTests.proj index da4a46f2ae141..a05afbc2b4c88 100644 --- a/src/libraries/System.Linq.Expressions/tests/TrimmingTests/System.Linq.Expressions.TrimmingTests.proj +++ b/src/libraries/System.Linq.Expressions/tests/TrimmingTests/System.Linq.Expressions.TrimmingTests.proj @@ -1,5 +1,13 @@ + + + + + + + +