From 2dff7aec4fa7751a3dfd2aa2bc3c933e04b0aaa6 Mon Sep 17 00:00:00 2001 From: Charles Milette Date: Thu, 28 Aug 2025 18:06:56 -0400 Subject: [PATCH 1/3] Narrow AdvancedCollectionView trimming warning so that it only triggers in unsafe usages Fixes #723 --- .../src/AdvancedCollectionView/AdvancedCollectionView.cs | 5 ++--- .../src/AdvancedCollectionView/SortDescription.cs | 4 ++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/components/Collections/src/AdvancedCollectionView/AdvancedCollectionView.cs b/components/Collections/src/AdvancedCollectionView/AdvancedCollectionView.cs index 416d87f8..246f451a 100644 --- a/components/Collections/src/AdvancedCollectionView/AdvancedCollectionView.cs +++ b/components/Collections/src/AdvancedCollectionView/AdvancedCollectionView.cs @@ -13,9 +13,6 @@ namespace CommunityToolkit.WinUI.Collections; /// /// A collection view implementation that supports filtering, sorting and incremental loading /// -#if NET8_0_OR_GREATER -[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Item sorting uses reflection to get property types and may not be AOT compatible.")] -#endif public partial class AdvancedCollectionView : IAdvancedCollectionView, INotifyPropertyChanged, ISupportIncrementalLoading, IComparer { private readonly List _view; @@ -380,6 +377,8 @@ public Predicate Filter /// Object B /// Comparison value #pragma warning disable CA1033 // Interface methods should be callable by child types + [System.Diagnostics.CodeAnalysis.SuppressMessage("Trimming", "IL2065:The method has a DynamicallyAccessedMembersAttribute (which applies to the implicit 'this' parameter), but the value used for the 'this' parameter can not be statically analyzed.", Justification = "Trimmer warnings are surfaced to the user with SortDescription")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Trimming", "IL2075:'this' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to target method. The return value of the source method does not have matching annotations.", Justification = "Trimmer warnings are surfaced to the user with SortDescription")] int IComparer.Compare(object x, object y) #pragma warning restore CA1033 // Interface methods should be callable by child types { diff --git a/components/Collections/src/AdvancedCollectionView/SortDescription.cs b/components/Collections/src/AdvancedCollectionView/SortDescription.cs index abc6b83a..47bb734f 100644 --- a/components/Collections/src/AdvancedCollectionView/SortDescription.cs +++ b/components/Collections/src/AdvancedCollectionView/SortDescription.cs @@ -32,6 +32,7 @@ public class SortDescription /// /// Direction of sort /// Comparer to use. If null, will use default comparer + [System.Diagnostics.CodeAnalysis.SuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "Not using property name")] public SortDescription(SortDirection direction, IComparer? comparer = null) : this(null!, direction, comparer!) { @@ -43,6 +44,9 @@ public SortDescription(SortDirection direction, IComparer? comparer = null) /// Name of property to sort on /// Direction of sort /// Comparer to use. If null, will use default comparer +#if NET8_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Item sorting with the property name uses reflection to get the property and is not trim-safe. Either use SortDescription to preserve the required metadata, or use the other constructor without a property name.")] +#endif public SortDescription(string propertyName, SortDirection direction, IComparer? comparer = null) { PropertyName = propertyName; From 68766f736cc168e381581efd8e9f5c09ec44b25f Mon Sep 17 00:00:00 2001 From: Charles Milette Date: Thu, 28 Aug 2025 18:13:37 -0400 Subject: [PATCH 2/3] Add SortDescription --- .../SortDescription{T}.cs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 components/Collections/src/AdvancedCollectionView/SortDescription{T}.cs diff --git a/components/Collections/src/AdvancedCollectionView/SortDescription{T}.cs b/components/Collections/src/AdvancedCollectionView/SortDescription{T}.cs new file mode 100644 index 00000000..b1cf3192 --- /dev/null +++ b/components/Collections/src/AdvancedCollectionView/SortDescription{T}.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.Diagnostics.CodeAnalysis; + +namespace CommunityToolkit.WinUI.Collections; + +/// +/// A generic version of which preserves the required metadata for reflection-based sorting. +/// +/// The type to sort +public class SortDescription<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T> : SortDescription +{ + /// + /// Initializes a new instance of the class. + /// + /// Name of property to sort on + /// Direction of sort + /// Comparer to use. If null, will use default comparer + [SuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "This class preserves metadata")] + public SortDescription(string propertyName, SortDirection direction, IComparer? comparer = null) : base(propertyName, direction, comparer) + { + } +} From c19cac2af552048bafd081a4b5a0e5b31b88a77d Mon Sep 17 00:00:00 2001 From: Charles Milette Date: Thu, 28 Aug 2025 19:58:27 -0400 Subject: [PATCH 3/3] Gate DynamicallyAccessedMembers to .NET 5 or greater, to allow .NET Native usage --- .../src/AdvancedCollectionView/SortDescription.cs | 2 +- .../src/AdvancedCollectionView/SortDescription{T}.cs | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/components/Collections/src/AdvancedCollectionView/SortDescription.cs b/components/Collections/src/AdvancedCollectionView/SortDescription.cs index 47bb734f..e2bebd4d 100644 --- a/components/Collections/src/AdvancedCollectionView/SortDescription.cs +++ b/components/Collections/src/AdvancedCollectionView/SortDescription.cs @@ -44,7 +44,7 @@ public SortDescription(SortDirection direction, IComparer? comparer = null) /// Name of property to sort on /// Direction of sort /// Comparer to use. If null, will use default comparer -#if NET8_0_OR_GREATER +#if NET5_0_OR_GREATER [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Item sorting with the property name uses reflection to get the property and is not trim-safe. Either use SortDescription to preserve the required metadata, or use the other constructor without a property name.")] #endif public SortDescription(string propertyName, SortDirection direction, IComparer? comparer = null) diff --git a/components/Collections/src/AdvancedCollectionView/SortDescription{T}.cs b/components/Collections/src/AdvancedCollectionView/SortDescription{T}.cs index b1cf3192..b97847ad 100644 --- a/components/Collections/src/AdvancedCollectionView/SortDescription{T}.cs +++ b/components/Collections/src/AdvancedCollectionView/SortDescription{T}.cs @@ -11,7 +11,11 @@ namespace CommunityToolkit.WinUI.Collections; /// A generic version of which preserves the required metadata for reflection-based sorting. /// /// The type to sort -public class SortDescription<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T> : SortDescription +public class SortDescription< +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] +#endif + T> : SortDescription { /// /// Initializes a new instance of the class.