From 4c83a8699a544c694feba5ecd9c2cef6bc5241ab Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Thu, 18 Feb 2021 17:52:11 -0600 Subject: [PATCH] Resolve ILLink warnings in System.Linq.Queryable (#48305) * Resolve ILLink warnings in System.Linq.Queryable Contributes to #45623 * Respond to PR feedback. Annotate public AsQueryable() method as RequiresUnreferencedCode. --- eng/illink.targets | 6 - .../ref/System.Linq.Queryable.cs | 4 + .../src/ILLink/ILLink.Suppressions.xml | 677 --------------------- .../src/System/Linq/CachedReflection.cs | 3 + .../src/System/Linq/EnumerableExecutor.cs | 4 + .../src/System/Linq/EnumerableQuery.cs | 15 + .../src/System/Linq/EnumerableRewriter.cs | 35 +- .../src/System/Linq/Queryable.cs | 4 + .../src/System/Linq/TypeHelper.cs | 9 - .../System.Linq.Queryable/tests/Queryable.cs | 34 -- .../tests/System.Linq.Queryable.Tests.csproj | 1 + .../tests/TrimCompatibilityTests.cs | 116 ++++ .../tests/TrimCompatibilityTests.cs | 4 + 13 files changed, 178 insertions(+), 734 deletions(-) delete mode 100644 src/libraries/System.Linq.Queryable/src/ILLink/ILLink.Suppressions.xml create mode 100644 src/libraries/System.Linq.Queryable/tests/TrimCompatibilityTests.cs diff --git a/eng/illink.targets b/eng/illink.targets index e616570..4d48845 100644 --- a/eng/illink.targets +++ b/eng/illink.targets @@ -47,8 +47,6 @@ $(ILLinkDirectory)ILLink.Suppressions $(ILLinkSuppressionsXmlFilePrefix).xml $(ILLinkSuppressionsXmlFilePrefix).$(Configuration).xml - $(ILLinkSuppressionsXmlFilePrefix).$(TargetOS).xml - $(ILLinkSuppressionsXmlFilePrefix).NonWindows.xml true @@ -87,10 +85,6 @@ Include="$(ILLinkSuppressionsXmlFile)" /> - - diff --git a/src/libraries/System.Linq.Queryable/ref/System.Linq.Queryable.cs b/src/libraries/System.Linq.Queryable/ref/System.Linq.Queryable.cs index 55290e7..060cf4d 100644 --- a/src/libraries/System.Linq.Queryable/ref/System.Linq.Queryable.cs +++ b/src/libraries/System.Linq.Queryable/ref/System.Linq.Queryable.cs @@ -20,7 +20,9 @@ namespace System.Linq } public partial class EnumerableQuery : System.Linq.EnumerableQuery, System.Collections.Generic.IEnumerable, System.Collections.IEnumerable, System.Linq.IOrderedQueryable, System.Linq.IOrderedQueryable, System.Linq.IQueryable, System.Linq.IQueryable, System.Linq.IQueryProvider { + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Enumerating in-memory collections as IQueryable can require unreferenced code because expressions referencing IQueryable extension methods can get rebound to IEnumerable extension methods. The IEnumerable extension methods could be trimmed causing the application to fail at runtime.")] public EnumerableQuery(System.Collections.Generic.IEnumerable enumerable) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Enumerating in-memory collections as IQueryable can require unreferenced code because expressions referencing IQueryable extension methods can get rebound to IEnumerable extension methods. The IEnumerable extension methods could be trimmed causing the application to fail at runtime.")] public EnumerableQuery(System.Linq.Expressions.Expression expression) { } System.Type System.Linq.IQueryable.ElementType { get { throw null; } } System.Linq.Expressions.Expression System.Linq.IQueryable.Expression { get { throw null; } } @@ -42,7 +44,9 @@ namespace System.Linq public static bool Any(this System.Linq.IQueryable source) { throw null; } public static bool Any(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> predicate) { throw null; } public static System.Linq.IQueryable Append(this System.Linq.IQueryable source, TSource element) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Enumerating in-memory collections as IQueryable can require unreferenced code because expressions referencing IQueryable extension methods can get rebound to IEnumerable extension methods. The IEnumerable extension methods could be trimmed causing the application to fail at runtime.")] public static System.Linq.IQueryable AsQueryable(this System.Collections.IEnumerable source) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Enumerating in-memory collections as IQueryable can require unreferenced code because expressions referencing IQueryable extension methods can get rebound to IEnumerable extension methods. The IEnumerable extension methods could be trimmed causing the application to fail at runtime.")] public static System.Linq.IQueryable AsQueryable(this System.Collections.Generic.IEnumerable source) { throw null; } public static decimal Average(this System.Linq.IQueryable source) { throw null; } public static double Average(this System.Linq.IQueryable source) { throw null; } diff --git a/src/libraries/System.Linq.Queryable/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Linq.Queryable/src/ILLink/ILLink.Suppressions.xml deleted file mode 100644 index 2393a6a..0000000 --- a/src/libraries/System.Linq.Queryable/src/ILLink/ILLink.Suppressions.xml +++ /dev/null @@ -1,677 +0,0 @@ - - - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Aggregate_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Aggregate_TSource_TAccumulate_3(System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Aggregate_TSource_TAccumulate_TResult_4(System.Type,System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.All_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Any_TSource_1(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Any_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Append_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Average_Decimal_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Average_Double_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Average_Int32_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Average_Int64_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Average_NullableDecimal_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Average_NullableDouble_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Average_NullableInt32_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Average_NullableInt64_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Average_NullableSingle_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Average_Single_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Cast_TResult_1(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Chunk_TSource_1(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Concat_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Contains_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Contains_TSource_3(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Count_TSource_1(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Count_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.DefaultIfEmpty_TSource_1(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.DefaultIfEmpty_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Distinct_TSource_1(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Distinct_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.ElementAt_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.ElementAtOrDefault_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Except_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Except_TSource_3(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.First_TSource_1(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.First_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.FirstOrDefault_TSource_1(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.FirstOrDefault_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.GroupBy_TSource_TKey_2(System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.GroupBy_TSource_TKey_3(System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.GroupBy_TSource_TKey_TElement_3(System.Type,System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.GroupBy_TSource_TKey_TElement_4(System.Type,System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.GroupBy_TSource_TKey_TElement_TResult_4(System.Type,System.Type,System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.GroupBy_TSource_TKey_TElement_TResult_5(System.Type,System.Type,System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.GroupBy_TSource_TKey_TResult_3(System.Type,System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.GroupBy_TSource_TKey_TResult_4(System.Type,System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.GroupJoin_TOuter_TInner_TKey_TResult_5(System.Type,System.Type,System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.GroupJoin_TOuter_TInner_TKey_TResult_6(System.Type,System.Type,System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Intersect_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Intersect_TSource_3(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Join_TOuter_TInner_TKey_TResult_5(System.Type,System.Type,System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Join_TOuter_TInner_TKey_TResult_6(System.Type,System.Type,System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Last_TSource_1(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Last_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.LastOrDefault_TSource_1(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.LastOrDefault_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.LongCount_TSource_1(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.LongCount_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Max_TSource_1(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Max_TSource_TResult_2(System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Min_TSource_1(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Min_TSource_TResult_2(System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.OfType_TResult_1(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.OrderBy_TSource_TKey_2(System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.OrderBy_TSource_TKey_3(System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.OrderByDescending_TSource_TKey_2(System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.OrderByDescending_TSource_TKey_3(System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Prepend_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Reverse_TSource_1(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Select_Index_TSource_TResult_2(System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Select_TSource_TResult_2(System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.SelectMany_Index_TSource_TCollection_TResult_3(System.Type,System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.SelectMany_Index_TSource_TResult_2(System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.SelectMany_TSource_TCollection_TResult_3(System.Type,System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.SelectMany_TSource_TResult_2(System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.SequenceEqual_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.SequenceEqual_TSource_3(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Single_TSource_1(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Single_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.SingleOrDefault_TSource_1(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.SingleOrDefault_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Skip_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.SkipLast_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.SkipWhile_Index_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.SkipWhile_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Sum_Decimal_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Sum_Double_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Sum_Int32_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Sum_Int64_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Sum_NullableDecimal_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Sum_NullableDouble_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Sum_NullableInt32_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Sum_NullableInt64_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Sum_NullableSingle_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Sum_Single_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Take_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.TakeLast_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.TakeWhile_Index_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.TakeWhile_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.ThenBy_TSource_TKey_2(System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.ThenBy_TSource_TKey_3(System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.ThenByDescending_TSource_TKey_2(System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.ThenByDescending_TSource_TKey_3(System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Union_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Union_TSource_3(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Where_Index_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Where_TSource_2(System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Zip_TFirst_TSecond_2(System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Zip_TFirst_TSecond_TResult_3(System.Type,System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.CachedReflectionInfo.Zip_TFirst_TSecond_TThird_3(System.Type,System.Type,System.Type) - - - ILLink - IL2060 - member - M:System.Linq.EnumerableRewriter.ArgsMatch(System.Reflection.MethodInfo,System.Collections.ObjectModel.ReadOnlyCollection{System.Linq.Expressions.Expression},System.Type[]) - - - ILLink - IL2060 - member - M:System.Linq.EnumerableRewriter.FindEnumerableMethod(System.String,System.Collections.ObjectModel.ReadOnlyCollection{System.Linq.Expressions.Expression},System.Type[]) - - - ILLink - IL2060 - member - M:System.Linq.EnumerableRewriter.FindMethod(System.Type,System.String,System.Collections.ObjectModel.ReadOnlyCollection{System.Linq.Expressions.Expression},System.Type[]) - - - ILLink - IL2067 - member - M:System.Linq.TypeHelper.GetStaticMethods(System.Type) - - - diff --git a/src/libraries/System.Linq.Queryable/src/System/Linq/CachedReflection.cs b/src/libraries/System.Linq.Queryable/src/System/Linq/CachedReflection.cs index 11b615f..f7e9a83 100644 --- a/src/libraries/System.Linq.Queryable/src/System/Linq/CachedReflection.cs +++ b/src/libraries/System.Linq.Queryable/src/System/Linq/CachedReflection.cs @@ -2,11 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Reflection; namespace System.Linq { + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2060:MakeGenericMethod", + Justification = "The methods passed into MakeGenericMethod do not contain trim annotations.")] internal static class CachedReflectionInfo { private static MethodInfo? s_Aggregate_TSource_2; diff --git a/src/libraries/System.Linq.Queryable/src/System/Linq/EnumerableExecutor.cs b/src/libraries/System.Linq.Queryable/src/System/Linq/EnumerableExecutor.cs index d7185e7..5ff04c6 100644 --- a/src/libraries/System.Linq.Queryable/src/System/Linq/EnumerableExecutor.cs +++ b/src/libraries/System.Linq.Queryable/src/System/Linq/EnumerableExecutor.cs @@ -2,12 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; namespace System.Linq { public abstract class EnumerableExecutor { + [RequiresUnreferencedCode(Queryable.InMemoryQueryableExtensionMethodsRequiresUnreferencedCode)] internal abstract object? ExecuteBoxed(); internal EnumerableExecutor() { } @@ -28,8 +30,10 @@ namespace System.Linq _expression = expression; } + [RequiresUnreferencedCode(Queryable.InMemoryQueryableExtensionMethodsRequiresUnreferencedCode)] internal override object? ExecuteBoxed() => Execute(); + [RequiresUnreferencedCode(Queryable.InMemoryQueryableExtensionMethodsRequiresUnreferencedCode)] internal T Execute() { EnumerableRewriter rewriter = new EnumerableRewriter(); diff --git a/src/libraries/System.Linq.Queryable/src/System/Linq/EnumerableQuery.cs b/src/libraries/System.Linq.Queryable/src/System/Linq/EnumerableQuery.cs index 350ae38..3bdd99e 100644 --- a/src/libraries/System.Linq.Queryable/src/System/Linq/EnumerableQuery.cs +++ b/src/libraries/System.Linq.Queryable/src/System/Linq/EnumerableQuery.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; namespace System.Linq @@ -14,12 +15,14 @@ namespace System.Linq internal EnumerableQuery() { } + [RequiresUnreferencedCode(Queryable.InMemoryQueryableExtensionMethodsRequiresUnreferencedCode)] internal static IQueryable Create(Type elementType, IEnumerable sequence) { Type seqType = typeof(EnumerableQuery<>).MakeGenericType(elementType); return (IQueryable)Activator.CreateInstance(seqType, sequence)!; } + [RequiresUnreferencedCode(Queryable.InMemoryQueryableExtensionMethodsRequiresUnreferencedCode)] internal static IQueryable Create(Type elementType, Expression expression) { Type seqType = typeof(EnumerableQuery<>).MakeGenericType(elementType); @@ -34,12 +37,14 @@ namespace System.Linq IQueryProvider IQueryable.Provider => this; + [RequiresUnreferencedCode(Queryable.InMemoryQueryableExtensionMethodsRequiresUnreferencedCode)] public EnumerableQuery(IEnumerable enumerable) { _enumerable = enumerable; _expression = Expression.Constant(this); } + [RequiresUnreferencedCode(Queryable.InMemoryQueryableExtensionMethodsRequiresUnreferencedCode)] public EnumerableQuery(Expression expression) { _expression = expression; @@ -53,6 +58,8 @@ namespace System.Linq Type IQueryable.ElementType => typeof(T); + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This class's ctor is annotated as RequiresUnreferencedCode.")] IQueryable IQueryProvider.CreateQuery(Expression expression) { if (expression == null) @@ -63,6 +70,8 @@ namespace System.Linq return Create(iqType.GetGenericArguments()[0], expression); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This class's ctor is annotated as RequiresUnreferencedCode.")] IQueryable IQueryProvider.CreateQuery(Expression expression) { if (expression == null) @@ -74,6 +83,8 @@ namespace System.Linq return new EnumerableQuery(expression); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This class's ctor is annotated as RequiresUnreferencedCode.")] object? IQueryProvider.Execute(Expression expression) { if (expression == null) @@ -81,6 +92,8 @@ namespace System.Linq return EnumerableExecutor.Create(expression).ExecuteBoxed(); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This class's ctor is annotated as RequiresUnreferencedCode.")] TElement IQueryProvider.Execute(Expression expression) { if (expression == null) @@ -94,6 +107,8 @@ namespace System.Linq IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This class's ctor is annotated as RequiresUnreferencedCode.")] private IEnumerator GetEnumerator() { if (_enumerable == null) diff --git a/src/libraries/System.Linq.Queryable/src/System/Linq/EnumerableRewriter.cs b/src/libraries/System.Linq.Queryable/src/System/Linq/EnumerableRewriter.cs index c7cd1e2..d444837 100644 --- a/src/libraries/System.Linq.Queryable/src/System/Linq/EnumerableRewriter.cs +++ b/src/libraries/System.Linq.Queryable/src/System/Linq/EnumerableRewriter.cs @@ -19,6 +19,13 @@ namespace System.Linq // Finding equivalent types can be relatively expensive, and hitting with the same types repeatedly is quite likely. private Dictionary? _equivalentTypeCache; + [RequiresUnreferencedCode(Queryable.InMemoryQueryableExtensionMethodsRequiresUnreferencedCode)] + public EnumerableRewriter() + { + } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This class's ctor is annotated as RequiresUnreferencedCode.")] protected override Expression VisitMethodCall(MethodCallExpression m) { Expression? obj = Visit(m.Object); @@ -39,7 +46,7 @@ namespace System.Linq else if (mInfo.DeclaringType == typeof(Queryable)) { // convert Queryable method to Enumerable method - MethodInfo seqMethod = FindEnumerableMethod(mInfo.Name, args, typeArgs); + MethodInfo seqMethod = FindEnumerableMethodForQueryable(mInfo.Name, args, typeArgs); args = FixupQuotedArgs(seqMethod, args); return Expression.Call(obj, seqMethod, args); } @@ -208,25 +215,32 @@ namespace System.Linq return c; } - - private static ILookup? s_seqMethods; - private static MethodInfo FindEnumerableMethod(string name, ReadOnlyCollection args, params Type[]? typeArgs) + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2060:MakeGenericMethod", + Justification = "Enumerable methods don't have trim annotations.")] + private static MethodInfo FindEnumerableMethodForQueryable(string name, ReadOnlyCollection args, params Type[]? typeArgs) { if (s_seqMethods == null) { - s_seqMethods = typeof(Enumerable).GetStaticMethods().ToLookup(m => m.Name); + s_seqMethods = GetEnumerableStaticMethods(typeof(Enumerable)).ToLookup(m => m.Name); } MethodInfo? mi = s_seqMethods[name].FirstOrDefault(m => ArgsMatch(m, args, typeArgs)); Debug.Assert(mi != null, "All static methods with arguments on Queryable have equivalents on Enumerable."); if (typeArgs != null) return mi.MakeGenericMethod(typeArgs); return mi; + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", + Justification = "This method is intentionally hiding the Enumerable type from the trimmer so it doesn't preserve all Enumerable's methods. " + + "This is safe because all Queryable methods have a DynamicDependency to the corresponding Enumerable method.")] + static MethodInfo[] GetEnumerableStaticMethods(Type type) => + type.GetMethods(BindingFlags.Public | BindingFlags.Static); } + [RequiresUnreferencedCode(Queryable.InMemoryQueryableExtensionMethodsRequiresUnreferencedCode)] private static MethodInfo FindMethod(Type type, string name, ReadOnlyCollection args, Type[]? typeArgs) { - using (IEnumerator en = type.GetStaticMethods().Where(m => m.Name == name).GetEnumerator()) + using (IEnumerator en = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static).Where(m => m.Name == name).GetEnumerator()) { if (!en.MoveNext()) throw Error.NoMethodOnType(name, type); @@ -259,8 +273,13 @@ namespace System.Linq return false; if (m.GetGenericArguments().Length != typeArgs.Length) return false; - m = m.MakeGenericMethod(typeArgs); - mParams = m.GetParameters(); + + mParams = GetConstrutedGenericParameters(m, typeArgs); + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2060:MakeGenericMethod", + Justification = "MakeGenericMethod is only called to get the parameter types, which are only used to make a 'match' decision. The generic method is not invoked.")] + static ParameterInfo[] GetConstrutedGenericParameters(MethodInfo method, Type[] genericTypes) => + method.MakeGenericMethod(genericTypes).GetParameters(); } for (int i = 0, n = args.Count; i < n; i++) { diff --git a/src/libraries/System.Linq.Queryable/src/System/Linq/Queryable.cs b/src/libraries/System.Linq.Queryable/src/System/Linq/Queryable.cs index 16befe0..a4859f6 100644 --- a/src/libraries/System.Linq.Queryable/src/System/Linq/Queryable.cs +++ b/src/libraries/System.Linq.Queryable/src/System/Linq/Queryable.cs @@ -10,6 +10,9 @@ namespace System.Linq { public static class Queryable { + internal const string InMemoryQueryableExtensionMethodsRequiresUnreferencedCode = "Enumerating in-memory collections as IQueryable can require unreferenced code because expressions referencing IQueryable extension methods can get rebound to IEnumerable extension methods. The IEnumerable extension methods could be trimmed causing the application to fail at runtime."; + + [RequiresUnreferencedCode(InMemoryQueryableExtensionMethodsRequiresUnreferencedCode)] public static IQueryable AsQueryable(this IEnumerable source) { if (source == null) @@ -17,6 +20,7 @@ namespace System.Linq return source as IQueryable ?? new EnumerableQuery(source); } + [RequiresUnreferencedCode(InMemoryQueryableExtensionMethodsRequiresUnreferencedCode)] public static IQueryable AsQueryable(this IEnumerable source) { if (source == null) diff --git a/src/libraries/System.Linq.Queryable/src/System/Linq/TypeHelper.cs b/src/libraries/System.Linq.Queryable/src/System/Linq/TypeHelper.cs index 7f2e0b5..64ef0c4 100644 --- a/src/libraries/System.Linq.Queryable/src/System/Linq/TypeHelper.cs +++ b/src/libraries/System.Linq.Queryable/src/System/Linq/TypeHelper.cs @@ -1,10 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Reflection; - namespace System.Linq { internal static class TypeHelper @@ -31,10 +27,5 @@ namespace System.Linq } return null; } - - internal static IEnumerable GetStaticMethods(this Type type) - { - return type.GetRuntimeMethods().Where(m => m.IsStatic); - } } } diff --git a/src/libraries/System.Linq.Queryable/tests/Queryable.cs b/src/libraries/System.Linq.Queryable/tests/Queryable.cs index 9766b25..5ccedac 100644 --- a/src/libraries/System.Linq.Queryable/tests/Queryable.cs +++ b/src/libraries/System.Linq.Queryable/tests/Queryable.cs @@ -78,40 +78,6 @@ namespace System.Linq.Tests Assert.Equal(2, i); } - /// - /// Verifies that all the Queryable methods contain a DynamicDependency - /// to the corresponding Enumerable method. This ensures the ILLinker will - /// preserve the corresponding Enumerable method when trimming. - /// - [Fact] - public static void QueryableMethodsContainCorrectDynamicDependency() - { - IEnumerable dependentMethods = - typeof(Queryable) - .GetMethods(BindingFlags.Public | BindingFlags.Static) - .Where(m => m.Name != "AsQueryable"); - - foreach (MethodInfo method in dependentMethods) - { - DynamicDependencyAttribute dependency = method.GetCustomAttribute(); - Assert.NotNull(dependency); - Assert.Equal(typeof(Enumerable), dependency.Type); - - int genericArgCount = 0; - string methodName = dependency.MemberSignature; - - int genericSeparator = methodName.IndexOf('`'); - if (genericSeparator != -1) - { - genericArgCount = int.Parse(methodName.Substring(genericSeparator + 1)); - methodName = methodName.Substring(0, genericSeparator); - } - - Assert.Equal(method.GetGenericArguments().Length, genericArgCount); - Assert.Equal(method.Name, methodName); - } - } - [Fact] public static void MatchSequencePattern() { diff --git a/src/libraries/System.Linq.Queryable/tests/System.Linq.Queryable.Tests.csproj b/src/libraries/System.Linq.Queryable/tests/System.Linq.Queryable.Tests.csproj index bceba42..a0124a9 100644 --- a/src/libraries/System.Linq.Queryable/tests/System.Linq.Queryable.Tests.csproj +++ b/src/libraries/System.Linq.Queryable/tests/System.Linq.Queryable.Tests.csproj @@ -51,6 +51,7 @@ + diff --git a/src/libraries/System.Linq.Queryable/tests/TrimCompatibilityTests.cs b/src/libraries/System.Linq.Queryable/tests/TrimCompatibilityTests.cs new file mode 100644 index 0000000..76d7e86 --- /dev/null +++ b/src/libraries/System.Linq.Queryable/tests/TrimCompatibilityTests.cs @@ -0,0 +1,116 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using Xunit; + +namespace System.Linq.Tests +{ + public class TrimCompatibilityTests + { + /// + /// Verifies that all the Queryable methods contain a DynamicDependency + /// to the corresponding Enumerable method. This ensures the ILLinker will + /// preserve the corresponding Enumerable method when trimming. + /// + [Fact] + public static void QueryableMethodsContainCorrectDynamicDependency() + { + IEnumerable dependentMethods = + typeof(Queryable) + .GetMethods(BindingFlags.Public | BindingFlags.Static) + .Where(m => m.Name != "AsQueryable"); + + foreach (MethodInfo method in dependentMethods) + { + DynamicDependencyAttribute dependency = method.GetCustomAttribute(); + Assert.NotNull(dependency); + Assert.Equal(typeof(Enumerable), dependency.Type); + + int genericArgCount = 0; + string methodName = dependency.MemberSignature; + + int genericSeparator = methodName.IndexOf('`'); + if (genericSeparator != -1) + { + genericArgCount = int.Parse(methodName.Substring(genericSeparator + 1)); + methodName = methodName.Substring(0, genericSeparator); + } + + Assert.Equal(method.GetGenericArguments().Length, genericArgCount); + Assert.Equal(method.Name, methodName); + } + } + + /// + /// Verifies that all methods in CachedReflectionInfo that call MakeGenericMethod + /// call it on a method that doesn't contain any trimming annotations (i.e. DynamicallyAccessedMembers). + /// + /// + /// This ensures it is safe to suppress IL2060:MakeGenericMethod warnings in the CachedReflectionInfo class. + /// + [Fact] + public static void CachedReflectionInfoMethodsNoAnnotations() + { + IEnumerable methods = + typeof(Queryable).Assembly + .GetType("System.Linq.CachedReflectionInfo") + .GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static) + .Where(m => m.GetParameters().Length > 0); + + // If you are adding a new method to this class, ensure the method meets these requirements + Assert.Equal(108, methods.Count()); + foreach (MethodInfo method in methods) + { + ParameterInfo[] parameters = method.GetParameters(); + + Type[] args = new Type[parameters.Length]; + for (int i = 0; i < args.Length; i++) + { + args[i] = typeof(object); + } + + MethodInfo resultMethodInfo = (MethodInfo)method.Invoke(null, args); + Assert.True(resultMethodInfo.IsConstructedGenericMethod); + MethodInfo originalGenericDefinition = resultMethodInfo.GetGenericMethodDefinition(); + + EnsureNoTrimAnnotations(originalGenericDefinition); + } + } + + /// + /// Verifies that all methods in Enumerable don't contain any trimming annotations (i.e. DynamicallyAccessedMembers). + /// + /// + /// This ensures it is safe to suppress IL2060:MakeGenericMethod warnings in EnumerableRewriter.FindEnumerableMethodForQueryable. + /// + [Fact] + public static void EnumerableMethodsNoAnnotations() + { + IEnumerable methods = + typeof(Enumerable) + .GetMethods(BindingFlags.Public | BindingFlags.Static) + .Where(m => m.IsGenericMethodDefinition); + + foreach (MethodInfo method in methods) + { + EnsureNoTrimAnnotations(method); + } + } + + private static void EnsureNoTrimAnnotations(MethodInfo method) + { + Type[] genericTypes = method.GetGenericArguments(); + foreach (Type genericType in genericTypes) + { + // The generic type should not have DynamicallyAccessedMembersAttribute on it. + Assert.Null(genericType.GetCustomAttribute()); + + // The generic type should not have a 'where new()' constraint since that will tell the trimmer to keep the ctor + Assert.False(genericType.GenericParameterAttributes.HasFlag(GenericParameterAttributes.DefaultConstructorConstraint)); + } + } + } +} diff --git a/src/libraries/System.Resources.ResourceManager/tests/TrimCompatibilityTests.cs b/src/libraries/System.Resources.ResourceManager/tests/TrimCompatibilityTests.cs index 11bc4ea..6fb3965 100644 --- a/src/libraries/System.Resources.ResourceManager/tests/TrimCompatibilityTests.cs +++ b/src/libraries/System.Resources.ResourceManager/tests/TrimCompatibilityTests.cs @@ -27,7 +27,11 @@ namespace System.Resources.Tests { foreach(Type genericType in genericTypes) { + // The generic type should not have DynamicallyAccessedMembersAttribute on it. Assert.Null(genericType.GetCustomAttribute()); + + // The generic type should not have a 'where new()' constraint since that will tell the trimmer to keep the ctor + Assert.False(genericType.GenericParameterAttributes.HasFlag(GenericParameterAttributes.DefaultConstructorConstraint)); } } } -- 2.7.4