diff --git a/src/Compatibility/Core/src/Android/AndroidAppIndexProvider.cs b/src/Compatibility/Core/src/Android/AndroidAppIndexProvider.cs index 47de56645ba4..1ae6318d2790 100644 --- a/src/Compatibility/Core/src/Android/AndroidAppIndexProvider.cs +++ b/src/Compatibility/Core/src/Android/AndroidAppIndexProvider.cs @@ -1,10 +1,14 @@ using System; +using System.Diagnostics.CodeAnalysis; using Android.Content; namespace Microsoft.Maui.Controls.Compatibility.Platform.Android { public class AndroidAppIndexProvider : IAppIndexingProvider { + [UnconditionalSuppressMessage ("Trimming", "IL2035", Justification = AppLinksAssemblyName + ".dll is not always present.")] + [UnconditionalSuppressMessage ("Trimming", "IL2072", Justification = AppLinksAssemblyName + ".dll is not always present.")] + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicConstructors, AppLinksAssemblyName + "." + AppLinksClassName, AppLinksAssemblyName)] public AndroidAppIndexProvider(Context context) { var fullyQualifiedName = $"{AppLinksAssemblyName}.{AppLinksClassName}, {AppLinksAssemblyName}"; diff --git a/src/Compatibility/Core/src/Android/Deserializer.cs b/src/Compatibility/Core/src/Android/Deserializer.cs index 63959b272a54..24413027a46f 100644 --- a/src/Compatibility/Core/src/Android/Deserializer.cs +++ b/src/Compatibility/Core/src/Android/Deserializer.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; -using System.IO.IsolatedStorage; using System.Runtime.Serialization; using System.Threading.Tasks; using System.Xml; @@ -17,36 +17,38 @@ internal class Deserializer : IDeserializer static string GetFilePath() => Path.Combine(Essentials.FileSystem.CacheDirectory, PropertyStoreFile); - public Task> DeserializePropertiesAsync() + [RequiresUnreferencedCode(TrimmerConstants.SerializerTrimmerWarning)] + public Task> DeserializePropertiesAsync() => Task.Factory.StartNew(DeserializeProperties); + + [RequiresUnreferencedCode(TrimmerConstants.SerializerTrimmerWarning)] + IDictionary DeserializeProperties() { // Deserialize property dictionary to local storage // Make sure to use Internal - return Task.Run(() => - { - var path = GetFilePath(); + var path = GetFilePath(); - if (!File.Exists(path)) - return null; + if (!File.Exists(path)) + return null; - using var stream = File.OpenRead(path); - using var xmlReader = XmlReader.Create(stream); - using var reader = XmlDictionaryReader.CreateDictionaryReader(xmlReader); + using var stream = File.OpenRead(path); + using var xmlReader = XmlReader.Create(stream); + using var reader = XmlDictionaryReader.CreateDictionaryReader(xmlReader); - try - { - var dcs = new DataContractSerializer(typeof(Dictionary)); - return (IDictionary)dcs.ReadObject(reader); - } - catch (Exception e) - { - Debug.WriteLine("Could not deserialize properties: " + e.Message); - Log.Warning("Microsoft.Maui.Controls.Compatibility PropertyStore", $"Exception while reading Application properties: {e}"); - } + try + { + var dcs = new DataContractSerializer(typeof(Dictionary)); + return (IDictionary)dcs.ReadObject(reader); + } + catch (Exception e) + { + Debug.WriteLine("Could not deserialize properties: " + e.Message); + Log.Warning("Microsoft.Maui.Controls.Compatibility PropertyStore", $"Exception while reading Application properties: {e}"); + } - return null; - }); + return null; } + [RequiresUnreferencedCode(TrimmerConstants.SerializerTrimmerWarning)] public Task SerializePropertiesAsync(IDictionary properties) { properties = new Dictionary(properties); @@ -55,29 +57,32 @@ public Task SerializePropertiesAsync(IDictionary properties) if (properties.Count <= 0) return Task.CompletedTask; + return Task.Factory.StartNew (SerializeProperties, properties); + } + + [RequiresUnreferencedCode(TrimmerConstants.SerializerTrimmerWarning)] + void SerializeProperties(object properties) + { // Serialize property dictionary to local storage // Make sure to use Internal - return Task.Run(() => - { - var path = GetFilePath(); + var path = GetFilePath(); - using var stream = File.Create(path); - using var xmlWriter = XmlWriter.Create(stream); - using var writer = XmlDictionaryWriter.CreateDictionaryWriter(xmlWriter); + using var stream = File.Create(path); + using var xmlWriter = XmlWriter.Create(stream); + using var writer = XmlDictionaryWriter.CreateDictionaryWriter(xmlWriter); - try - { - var dcs = new DataContractSerializer(typeof(Dictionary)); - dcs.WriteObject(writer, properties); - writer.Flush(); - } - catch (Exception e) - { - Debug.WriteLine("Could not serialize properties: " + e.Message); - Log.Warning("Microsoft.Maui.Controls.Compatibility PropertyStore", $"Exception while writing Application properties: {e}"); - return; - } - }); + try + { + var dcs = new DataContractSerializer(typeof(Dictionary)); + dcs.WriteObject(writer, properties); + writer.Flush(); + } + catch (Exception e) + { + Debug.WriteLine("Could not serialize properties: " + e.Message); + Log.Warning("Microsoft.Maui.Controls.Compatibility PropertyStore", $"Exception while writing Application properties: {e}"); + return; + } } } } \ No newline at end of file diff --git a/src/Compatibility/Core/src/Android/NativeBindingservice.cs b/src/Compatibility/Core/src/Android/NativeBindingservice.cs index 1133112bd7f6..2ed8523c618b 100644 --- a/src/Compatibility/Core/src/Android/NativeBindingservice.cs +++ b/src/Compatibility/Core/src/Android/NativeBindingservice.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using Microsoft.Maui.Controls.Xaml.Internals; using AView = Android.Views.View; @@ -8,6 +9,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android { class NativeBindingService : INativeBindingService { + [UnconditionalSuppressMessage ("Trimming", "IL2075", Justification = TrimmerConstants.NativeBindingService)] public bool TrySetBinding(object target, string propertyName, BindingBase binding) { var view = target as AView; diff --git a/src/Compatibility/Core/src/Android/ResourceManager.cs b/src/Compatibility/Core/src/Android/ResourceManager.cs index 00792751c915..8466d717410b 100644 --- a/src/Compatibility/Core/src/Android/ResourceManager.cs +++ b/src/Compatibility/Core/src/Android/ResourceManager.cs @@ -1,7 +1,7 @@ using System; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.IO; -using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; @@ -23,23 +23,39 @@ public static class ResourceManager static ImageCache GetCache() => _lruCache.Value; static Assembly _assembly; + [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Resource.designer.cs is in the root application assembly, which should be preserved.")] + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] static Type FindType(string name, string altName) { - return _assembly?.GetTypes().FirstOrDefault(x => x.Name == name || x.Name == altName); + if (_assembly != null) + { + foreach (var type in _assembly.GetTypes()) + { + if (type.Name == name || type.Name == altName) + return type; + } + } + return null; } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] static Type _drawableClass; + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] static Type _resourceClass; + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] static Type _styleClass; + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] static Type _layoutClass; public static Type DrawableClass { + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] get { if (_drawableClass == null) _drawableClass = FindType("Drawable", "Resource_Drawable"); return _drawableClass; } + [param: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] set { _drawableClass = value; @@ -48,12 +64,14 @@ public static Type DrawableClass public static Type ResourceClass { + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] get { if (_resourceClass == null) _resourceClass = FindType("Id", "Resource_Id"); return _resourceClass; } + [param: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] set { _resourceClass = value; @@ -62,12 +80,14 @@ public static Type ResourceClass public static Type StyleClass { + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] get { if (_styleClass == null) _styleClass = FindType("Style", "Resource_Style"); return _styleClass; } + [param: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] set { _styleClass = value; @@ -76,12 +96,14 @@ public static Type StyleClass public static Type LayoutClass { + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] get { if (_layoutClass == null) _layoutClass = FindType("Layout", "Resource_Layout"); return _layoutClass; } + [param: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] set { _layoutClass = value; @@ -359,17 +381,17 @@ public static void Init(Assembly mainAssembly) _assembly = mainAssembly; } - static int IdFromTitle(string title, Type resourceType, string defType, Resources resource) + static int IdFromTitle(string title, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] Type resourceType, string defType, Resources resource) { return IdFromTitle(title, resourceType, defType, resource, Platform.GetPackageName()); } - static int IdFromTitle(string title, Type resourceType, string defType, Context context) + static int IdFromTitle(string title, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] Type resourceType, string defType, Context context) { return IdFromTitle(title, resourceType, defType, context?.Resources, context?.PackageName); } - static int IdFromTitle(string title, Type resourceType, string defType, Resources resource, string packageName) + static int IdFromTitle(string title, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] Type resourceType, string defType, Resources resource, string packageName) { int id = 0; if (title == null) @@ -409,7 +431,7 @@ int SearchByIdentifier(string n, string d, Resources r, string p) return GetId(resourceType, name); } - static int GetId(Type type, string memberName) + static int GetId([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] Type type, string memberName) { // This may legitimately be null in designer scenarios if (type == null) diff --git a/src/Compatibility/Core/src/AppHostBuilderExtensions.cs b/src/Compatibility/Core/src/AppHostBuilderExtensions.cs index 409eef83d2e1..0e0a18460817 100644 --- a/src/Compatibility/Core/src/AppHostBuilderExtensions.cs +++ b/src/Compatibility/Core/src/AppHostBuilderExtensions.cs @@ -1,5 +1,6 @@ #nullable enable using System; +using System.Diagnostics.CodeAnalysis; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; @@ -43,7 +44,7 @@ namespace Microsoft.Maui.Controls.Hosting { public static class MauiAppBuilderExtensions { - public static MauiAppBuilder UseMauiApp(this MauiAppBuilder builder) + public static MauiAppBuilder UseMauiApp<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApp>(this MauiAppBuilder builder) where TApp : class, IApplication { builder.Services.TryAddSingleton(); @@ -51,7 +52,7 @@ public static MauiAppBuilder UseMauiApp(this MauiAppBuilder builder) return builder; } - public static MauiAppBuilder UseMauiApp(this MauiAppBuilder builder, Func implementationFactory) + public static MauiAppBuilder UseMauiApp<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApp>(this MauiAppBuilder builder, Func implementationFactory) where TApp : class, IApplication { builder.Services.TryAddSingleton(implementationFactory); diff --git a/src/Compatibility/Core/src/GTK/GtkSerializer.cs b/src/Compatibility/Core/src/GTK/GtkSerializer.cs index 11d5dfef3b1e..92964cddd245 100644 --- a/src/Compatibility/Core/src/GTK/GtkSerializer.cs +++ b/src/Compatibility/Core/src/GTK/GtkSerializer.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.IO.IsolatedStorage; using System.Runtime.Serialization; @@ -13,6 +14,7 @@ internal sealed class GtkSerializer : IDeserializer { const string PropertyStoreFile = "PropertyStore.forms"; + [RequiresUnreferencedCode(TrimmerConstants.SerializerTrimmerWarning)] public Task> DeserializePropertiesAsync() { try @@ -54,6 +56,7 @@ public Task> DeserializePropertiesAsync() } } + [RequiresUnreferencedCode(TrimmerConstants.SerializerTrimmerWarning)] public Task SerializePropertiesAsync(IDictionary properties) { try diff --git a/src/Compatibility/Core/src/Properties/AssemblyInfo.cs b/src/Compatibility/Core/src/Properties/AssemblyInfo.cs index a1851c4d4bcb..b8068f876c3a 100644 --- a/src/Compatibility/Core/src/Properties/AssemblyInfo.cs +++ b/src/Compatibility/Core/src/Properties/AssemblyInfo.cs @@ -1,4 +1,6 @@ +using System.Reflection; using System.Runtime.CompilerServices; +[assembly: AssemblyMetadata ("IsTrimmable", "True")] [assembly: InternalsVisibleTo("Compatibility.Windows.UnitTests")] [assembly: InternalsVisibleTo("Compatibility.Android.UnitTests")] \ No newline at end of file diff --git a/src/Compatibility/Core/src/Tizen/Deserializer.cs b/src/Compatibility/Core/src/Tizen/Deserializer.cs index 4dc8ef283cc3..a8e7a690ab31 100644 --- a/src/Compatibility/Core/src/Tizen/Deserializer.cs +++ b/src/Compatibility/Core/src/Tizen/Deserializer.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO.IsolatedStorage; using System.Runtime.Serialization; using System.Threading.Tasks; @@ -12,85 +13,91 @@ internal class Deserializer : Internals.IDeserializer { const string PropertyStoreFile = "PropertyStore.forms"; - public Task> DeserializePropertiesAsync() + public Task> DeserializePropertiesAsync() => Task.Factory.StartNew(DeserializeProperties); + + [RequiresUnreferencedCode(TrimmerConstants.SerializerTrimmerWarning)] + IDictionary DeserializeProperties() { // Deserialize property dictionary to local storage // Make sure to use Internal - return Task.Run(() => + using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication()) { - using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication()) + if (!store.FileExists(PropertyStoreFile)) + return null; + + using (IsolatedStorageFileStream stream = store.OpenFile(PropertyStoreFile, System.IO.FileMode.Open, System.IO.FileAccess.Read)) + using (XmlDictionaryReader reader = XmlDictionaryReader.CreateBinaryReader(stream, XmlDictionaryReaderQuotas.Max)) { - if (!store.FileExists(PropertyStoreFile)) + if (stream.Length == 0) return null; - using (IsolatedStorageFileStream stream = store.OpenFile(PropertyStoreFile, System.IO.FileMode.Open, System.IO.FileAccess.Read)) - using (XmlDictionaryReader reader = XmlDictionaryReader.CreateBinaryReader(stream, XmlDictionaryReaderQuotas.Max)) + try { - if (stream.Length == 0) - return null; - - try - { - var dcs = new DataContractSerializer(typeof(Dictionary)); - return (IDictionary)dcs.ReadObject(reader); - } - catch (Exception e) - { - Debug.WriteLine("Could not deserialize properties: " + e.Message); - Internals.Log.Warning("Microsoft.Maui.Controls.Compatibility PropertyStore", $"Exception while reading Application properties: {e}"); - } + var dcs = new DataContractSerializer(typeof(Dictionary)); + return (IDictionary)dcs.ReadObject(reader); + } + catch (Exception e) + { + Debug.WriteLine("Could not deserialize properties: " + e.Message); + Internals.Log.Warning("Microsoft.Maui.Controls.Compatibility PropertyStore", $"Exception while reading Application properties: {e}"); } - } + } - return null; - }); + return null; } + [RequiresUnreferencedCode(TrimmerConstants.SerializerTrimmerWarning)] public Task SerializePropertiesAsync(IDictionary properties) { properties = new Dictionary(properties); - // Serialize property dictionary to local storage - // Make sure to use Internal - return Task.Run(() => + + // No need to write 0 properties if no file exists + if (properties.Count <= 0) + return Task.CompletedTask; + + return Task.Factory.StartNew(SerializeProperties, properties); + } + + [RequiresUnreferencedCode(TrimmerConstants.SerializerTrimmerWarning)] + void SerializeProperties(object properties) + { + using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication()) { - using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication()) + // No need to write 0 properties if no file exists + if (properties.Count == 0 && !store.FileExists(PropertyStoreFile)) + { + return; + } + using (IsolatedStorageFileStream stream = store.OpenFile(PropertyStoreFile + ".tmp", System.IO.FileMode.OpenOrCreate)) + using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(stream)) { - // No need to write 0 properties if no file exists - if (properties.Count == 0 && !store.FileExists(PropertyStoreFile)) - { - return; - } - using (IsolatedStorageFileStream stream = store.OpenFile(PropertyStoreFile + ".tmp", System.IO.FileMode.OpenOrCreate)) - using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(stream)) - { - try - { - var dcs = new DataContractSerializer(typeof(Dictionary)); - dcs.WriteObject(writer, properties); - writer.Flush(); - } - catch (Exception e) - { - Debug.WriteLine("Could not serialize properties: " + e.Message); - Internals.Log.Warning("Microsoft.Maui.Controls.Compatibility PropertyStore", $"Exception while writing Application properties: {e}"); - return; - } - } - try { - if (store.FileExists(PropertyStoreFile)) - store.DeleteFile(PropertyStoreFile); - store.MoveFile(PropertyStoreFile + ".tmp", PropertyStoreFile); + var dcs = new DataContractSerializer(typeof(Dictionary)); + dcs.WriteObject(writer, properties); + writer.Flush(); } catch (Exception e) { - Debug.WriteLine("Could not move new serialized property file over old: " + e.Message); + Debug.WriteLine("Could not serialize properties: " + e.Message); Internals.Log.Warning("Microsoft.Maui.Controls.Compatibility PropertyStore", $"Exception while writing Application properties: {e}"); + return; } } - }); + + try + { + if (store.FileExists(PropertyStoreFile)) + store.DeleteFile(PropertyStoreFile); + store.MoveFile(PropertyStoreFile + ".tmp", PropertyStoreFile); + } + catch (Exception e) + { + Debug.WriteLine("Could not move new serialized property file over old: " + e.Message); + Internals.Log.Warning("Microsoft.Maui.Controls.Compatibility PropertyStore", $"Exception while writing Application properties: {e}"); + } + } } } } \ No newline at end of file diff --git a/src/Compatibility/Core/src/Tizen/NativeBindingService.cs b/src/Compatibility/Core/src/Tizen/NativeBindingService.cs index a4a064c83d33..adfc61627ccf 100644 --- a/src/Compatibility/Core/src/Tizen/NativeBindingService.cs +++ b/src/Compatibility/Core/src/Tizen/NativeBindingService.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Microsoft.Maui.Controls.Compatibility.Internals; using Microsoft.Maui.Controls.Compatibility.Xaml.Internals; @@ -7,6 +8,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Tizen { class NativeBindingService : INativeBindingService { + [UnconditionalSuppressMessage ("Trimming", "IL2075", Justification = TrimmerConstants.NativeBindingService)] public bool TrySetBinding(object target, string propertyName, BindingBase binding) { var view = target as EObject; diff --git a/src/Compatibility/Core/src/WPF/Deserializer.cs b/src/Compatibility/Core/src/WPF/Deserializer.cs index 3131bcc87575..9a4153e3373d 100644 --- a/src/Compatibility/Core/src/WPF/Deserializer.cs +++ b/src/Compatibility/Core/src/WPF/Deserializer.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.IO.IsolatedStorage; using System.Linq; @@ -15,6 +16,7 @@ internal sealed class Deserializer : IDeserializer { const string PropertyStoreFile = "PropertyStore.forms"; + [RequiresUnreferencedCode(TrimmerConstants.SerializerTrimmerWarning)] public Task> DeserializePropertiesAsync() { IsolatedStorageFile isoStore = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly, null, null); @@ -42,6 +44,7 @@ public Task> DeserializePropertiesAsync() } } + [RequiresUnreferencedCode(TrimmerConstants.SerializerTrimmerWarning)] public async Task SerializePropertiesAsync(IDictionary properties) { try diff --git a/src/Compatibility/Core/src/Windows/NativeBindingService.cs b/src/Compatibility/Core/src/Windows/NativeBindingService.cs index 068d2073892b..4bf901db4a51 100644 --- a/src/Compatibility/Core/src/Windows/NativeBindingService.cs +++ b/src/Compatibility/Core/src/Windows/NativeBindingService.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Reflection; using Microsoft.UI.Xaml; using Microsoft.Maui.Controls.Xaml.Internals; @@ -8,6 +9,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.UWP { public class NativeBindingService : INativeBindingService { + [UnconditionalSuppressMessage ("Trimming", "IL2075", Justification = TrimmerConstants.NativeBindingService)] public bool TrySetBinding(object target, string propertyName, BindingBase binding) { var view = target as FrameworkElement; diff --git a/src/Compatibility/Core/src/Windows/WindowsSerializer.cs b/src/Compatibility/Core/src/Windows/WindowsSerializer.cs index 650fb8c3fc32..44ad6f2c94bf 100644 --- a/src/Compatibility/Core/src/Windows/WindowsSerializer.cs +++ b/src/Compatibility/Core/src/Windows/WindowsSerializer.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Runtime.Serialization; using System.Threading.Tasks; @@ -16,36 +17,38 @@ internal sealed class WindowsSerializer : IDeserializer static string GetFilePath() => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), PropertyStoreFile); - public Task> DeserializePropertiesAsync() + [RequiresUnreferencedCode(TrimmerConstants.SerializerTrimmerWarning)] + public Task> DeserializePropertiesAsync() => Task.Factory.StartNew(DeserializeProperties); + + [RequiresUnreferencedCode(TrimmerConstants.SerializerTrimmerWarning)] + IDictionary DeserializeProperties() { // Deserialize property dictionary to local storage // Make sure to use Internal - return Task.Run(() => - { - var path = GetFilePath(); + var path = GetFilePath(); - if (!File.Exists(path)) - return null; + if (!File.Exists(path)) + return null; - using var stream = File.OpenRead(path); - using var xmlReader = XmlReader.Create(stream); - using var reader = XmlDictionaryReader.CreateDictionaryReader(xmlReader); + using var stream = File.OpenRead(path); + using var xmlReader = XmlReader.Create(stream); + using var reader = XmlDictionaryReader.CreateDictionaryReader(xmlReader); - try - { - var dcs = new DataContractSerializer(typeof(Dictionary)); - return (IDictionary)dcs.ReadObject(reader); - } - catch (Exception e) - { - Debug.WriteLine("Could not deserialize properties: " + e.Message); - Log.Warning("Microsoft.Maui.Controls.Compatibility PropertyStore", $"Exception while reading Application properties: {e}"); - } + try + { + var dcs = new DataContractSerializer(typeof(Dictionary)); + return (IDictionary)dcs.ReadObject(reader); + } + catch (Exception e) + { + Debug.WriteLine("Could not deserialize properties: " + e.Message); + Log.Warning("Microsoft.Maui.Controls.Compatibility PropertyStore", $"Exception while reading Application properties: {e}"); + } - return null; - }); + return null; } + [RequiresUnreferencedCode(TrimmerConstants.SerializerTrimmerWarning)] public Task SerializePropertiesAsync(IDictionary properties) { properties = new Dictionary(properties); @@ -54,29 +57,32 @@ public Task SerializePropertiesAsync(IDictionary properties) if (properties.Count <= 0) return Task.CompletedTask; + return Task.Factory.StartNew(SerializeProperties, properties); + } + + [RequiresUnreferencedCode(TrimmerConstants.SerializerTrimmerWarning)] + void SerializeProperties(object properties) + { // Serialize property dictionary to local storage // Make sure to use Internal - return Task.Run(() => - { - var path = GetFilePath(); + var path = GetFilePath(); - using var stream = File.Create(path); - using var xmlWriter = XmlWriter.Create(stream); - using var writer = XmlDictionaryWriter.CreateDictionaryWriter(xmlWriter); + using var stream = File.Create(path); + using var xmlWriter = XmlWriter.Create(stream); + using var writer = XmlDictionaryWriter.CreateDictionaryWriter(xmlWriter); - try - { - var dcs = new DataContractSerializer(typeof(Dictionary)); - dcs.WriteObject(writer, properties); - writer.Flush(); - } - catch (Exception e) - { - Debug.WriteLine("Could not serialize properties: " + e.Message); - Log.Warning("Microsoft.Maui.Controls.Compatibility PropertyStore", $"Exception while writing Application properties: {e}"); - return; - } - }); + try + { + var dcs = new DataContractSerializer(typeof(Dictionary)); + dcs.WriteObject(writer, properties); + writer.Flush(); + } + catch (Exception e) + { + Debug.WriteLine("Could not serialize properties: " + e.Message); + Log.Warning("Microsoft.Maui.Controls.Compatibility PropertyStore", $"Exception while writing Application properties: {e}"); + return; + } } } } \ No newline at end of file diff --git a/src/Compatibility/Core/src/iOS/Deserializer.cs b/src/Compatibility/Core/src/iOS/Deserializer.cs index 0d3e76df160b..d14edd9227da 100644 --- a/src/Compatibility/Core/src/iOS/Deserializer.cs +++ b/src/Compatibility/Core/src/iOS/Deserializer.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; -using System.IO.IsolatedStorage; using System.Runtime.Serialization; using System.Threading.Tasks; using System.Xml; @@ -36,35 +36,37 @@ void SaveSerialized(string str) ud.SetString(str, PropertyStoreKey); } - public Task> DeserializePropertiesAsync() + [RequiresUnreferencedCode(TrimmerConstants.SerializerTrimmerWarning)] + public Task> DeserializePropertiesAsync() => Task.Factory.StartNew(DeserializeProperties); + + [RequiresUnreferencedCode(TrimmerConstants.SerializerTrimmerWarning)] + IDictionary DeserializeProperties() { // Deserialize property dictionary to local storage // Make sure to use Internal - return Task.Run(() => - { - var str = LoadSerialized(); + var str = LoadSerialized(); - if (string.IsNullOrEmpty(str)) - return null; + if (string.IsNullOrEmpty(str)) + return null; - using var stringReader = new StringReader(str); - using var reader = XmlReader.Create(stringReader); + using var stringReader = new StringReader(str); + using var reader = XmlReader.Create(stringReader); - try - { - var dcs = new DataContractSerializer(typeof(Dictionary)); - return (IDictionary)dcs.ReadObject(reader); - } - catch (Exception e) - { - Debug.WriteLine("Could not deserialize properties: " + e.Message); - Log.Warning("Microsoft.Maui.Controls.Compatibility PropertyStore", $"Exception while reading Application properties: {e}"); - } + try + { + var dcs = new DataContractSerializer(typeof(Dictionary)); + return (IDictionary)dcs.ReadObject(reader); + } + catch (Exception e) + { + Debug.WriteLine("Could not deserialize properties: " + e.Message); + Log.Warning("Microsoft.Maui.Controls.Compatibility PropertyStore", $"Exception while reading Application properties: {e}"); + } - return null; - }); + return null; } + [RequiresUnreferencedCode(TrimmerConstants.SerializerTrimmerWarning)] public Task SerializePropertiesAsync(IDictionary properties) { properties = new Dictionary(properties); @@ -73,30 +75,33 @@ public Task SerializePropertiesAsync(IDictionary properties) if (properties.Count <= 0) return Task.CompletedTask; + return Task.Factory.StartNew(SerializeProperties, properties); + } + + [RequiresUnreferencedCode(TrimmerConstants.SerializerTrimmerWarning)] + void SerializeProperties(object properties) + { // Serialize property dictionary to local storage // Make sure to use Internal - return Task.Run(() => + using var stringWriter = new StringWriter(); + using var xmlWriter = XmlWriter.Create(stringWriter); + + try { - using var stringWriter = new StringWriter(); - using var xmlWriter = XmlWriter.Create(stringWriter); - - try - { - var dcs = new DataContractSerializer(typeof(Dictionary)); - dcs.WriteObject(xmlWriter, properties); - xmlWriter.Flush(); - - var str = stringWriter.ToString(); - - SaveSerialized(str); - } - catch (Exception e) - { - Debug.WriteLine("Could not serialize properties: " + e.Message); - Log.Warning("Microsoft.Maui.Controls.Compatibility PropertyStore", $"Exception while writing Application properties: {e}"); - return; - } - }); + var dcs = new DataContractSerializer(typeof(Dictionary)); + dcs.WriteObject(xmlWriter, properties); + xmlWriter.Flush(); + + var str = stringWriter.ToString(); + + SaveSerialized(str); + } + catch (Exception e) + { + Debug.WriteLine("Could not serialize properties: " + e.Message); + Log.Warning("Microsoft.Maui.Controls.Compatibility PropertyStore", $"Exception while writing Application properties: {e}"); + return; + } } } } \ No newline at end of file diff --git a/src/Compatibility/Core/src/iOS/NativeBindingService.cs b/src/Compatibility/Core/src/iOS/NativeBindingService.cs index f51ced5b5582..40166238df00 100644 --- a/src/Compatibility/Core/src/iOS/NativeBindingService.cs +++ b/src/Compatibility/Core/src/iOS/NativeBindingService.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Microsoft.Maui.Controls.Internals; using Microsoft.Maui.Controls.Xaml.Internals; using UIKit; @@ -9,6 +10,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.iOS [Preserve(AllMembers = true)] class NativeBindingService : INativeBindingService { + [UnconditionalSuppressMessage ("Trimming", "IL2075", Justification = TrimmerConstants.NativeBindingService)] public bool TrySetBinding(object target, string propertyName, BindingBase binding) { var view = target as UIView; diff --git a/src/Controls/src/Core/DependencyService.cs b/src/Controls/src/Core/DependencyService.cs index a779dbcb94ec..9ba5b2353cb7 100644 --- a/src/Controls/src/Core/DependencyService.cs +++ b/src/Controls/src/Core/DependencyService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using Microsoft.Maui.Controls.Internals; @@ -16,14 +17,14 @@ public static class DependencyService static readonly List DependencyTypes = new List(); static readonly Dictionary DependencyImplementations = new Dictionary(); - public static T Resolve(DependencyFetchTarget fallbackFetchTarget = DependencyFetchTarget.GlobalInstance) where T : class + public static T Resolve<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(DependencyFetchTarget fallbackFetchTarget = DependencyFetchTarget.GlobalInstance) where T : class { var result = DependencyResolver.Resolve(typeof(T)) as T; return result ?? Get(fallbackFetchTarget); } - public static T Get(DependencyFetchTarget fetchTarget = DependencyFetchTarget.GlobalInstance) where T : class + public static T Get<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(DependencyFetchTarget fetchTarget = DependencyFetchTarget.GlobalInstance) where T : class { Initialize(); @@ -58,14 +59,14 @@ public static T Get(DependencyFetchTarget fetchTarget = DependencyFetchTarget return (T)Activator.CreateInstance(dependencyImplementation.ImplementorType); } - public static void Register() where T : class + public static void Register<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>() where T : class { Type type = typeof(T); if (!DependencyTypes.Contains(type)) DependencyTypes.Add(type); } - public static void Register() where T : class where TImpl : class, T + public static void Register() where T : class where TImpl : class, T { Type targetType = typeof(T); Type implementorType = typeof(TImpl); diff --git a/src/Controls/src/Core/IDeserializer.cs b/src/Controls/src/Core/IDeserializer.cs index 6b90db802402..d93c6656ee0b 100644 --- a/src/Controls/src/Core/IDeserializer.cs +++ b/src/Controls/src/Core/IDeserializer.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; namespace Microsoft.Maui.Controls.Internals @@ -7,7 +8,9 @@ namespace Microsoft.Maui.Controls.Internals [EditorBrowsable(EditorBrowsableState.Never)] public interface IDeserializer { + [RequiresUnreferencedCode(TrimmerConstants.SerializerTrimmerWarning)] Task> DeserializePropertiesAsync(); + [RequiresUnreferencedCode(TrimmerConstants.SerializerTrimmerWarning)] Task SerializePropertiesAsync(IDictionary properties); } } \ No newline at end of file diff --git a/src/Controls/src/Core/TrimmerConstants.cs b/src/Controls/src/Core/TrimmerConstants.cs new file mode 100644 index 000000000000..c094e541c5ad --- /dev/null +++ b/src/Controls/src/Core/TrimmerConstants.cs @@ -0,0 +1,9 @@ +namespace Microsoft.Maui.Controls; + +class TrimmerConstants +{ + // https://github.com/dotnet/runtime/blob/f130138b337b57342e94dabf499b818531effed5/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContract.cs#L31-L32 + internal const string SerializerTrimmerWarning = "Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved."; + + internal const string NativeBindingService = "This method properly handles missing properties, and there is not a way to preserve them from this method."; +} \ No newline at end of file diff --git a/src/Core/src/System.Diagnostics.CodeAnalysis/DynamicDependencyAttribute.cs b/src/Core/src/System.Diagnostics.CodeAnalysis/DynamicDependencyAttribute.cs new file mode 100644 index 000000000000..05c0ceb456b2 --- /dev/null +++ b/src/Core/src/System.Diagnostics.CodeAnalysis/DynamicDependencyAttribute.cs @@ -0,0 +1,131 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#if NETSTANDARD2_0 || NETSTANDARD2_1 +namespace System.Diagnostics.CodeAnalysis +{ + /// + /// States a dependency that one member has on another. + /// + /// + /// This can be used to inform tooling of a dependency that is otherwise not evident purely from + /// metadata and IL, for example a member relied on via reflection. + /// + [AttributeUsage( + AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Method, + AllowMultiple = true, Inherited = false)] + internal sealed class DynamicDependencyAttribute : Attribute + { + /// + /// Initializes a new instance of the class + /// with the specified signature of a member on the same type as the consumer. + /// + /// The signature of the member depended on. + public DynamicDependencyAttribute(string memberSignature) + { + MemberSignature = memberSignature; + } + + /// + /// Initializes a new instance of the class + /// with the specified signature of a member on a . + /// + /// The signature of the member depended on. + /// The containing . + public DynamicDependencyAttribute(string memberSignature, Type type) + { + MemberSignature = memberSignature; + Type = type; + } + + /// + /// Initializes a new instance of the class + /// with the specified signature of a member on a type in an assembly. + /// + /// The signature of the member depended on. + /// The full name of the type containing the specified member. + /// The assembly name of the type containing the specified member. + public DynamicDependencyAttribute(string memberSignature, string typeName, string assemblyName) + { + MemberSignature = memberSignature; + TypeName = typeName; + AssemblyName = assemblyName; + } + + /// + /// Initializes a new instance of the class + /// with the specified types of members on a . + /// + /// The types of members depended on. + /// The containing the specified members. + public DynamicDependencyAttribute(DynamicallyAccessedMemberTypes memberTypes, Type type) + { + MemberTypes = memberTypes; + Type = type; + } + + /// + /// Initializes a new instance of the class + /// with the specified types of members on a type in an assembly. + /// + /// The types of members depended on. + /// The full name of the type containing the specified members. + /// The assembly name of the type containing the specified members. + public DynamicDependencyAttribute(DynamicallyAccessedMemberTypes memberTypes, string typeName, string assemblyName) + { + MemberTypes = memberTypes; + TypeName = typeName; + AssemblyName = assemblyName; + } + + /// + /// Gets the signature of the member depended on. + /// + /// + /// Either must be a valid string or + /// must not equal , but not both. + /// + public string? MemberSignature { get; } + + /// + /// Gets the which specifies the type + /// of members depended on. + /// + /// + /// Either must be a valid string or + /// must not equal , but not both. + /// + public DynamicallyAccessedMemberTypes MemberTypes { get; } + + /// + /// Gets the containing the specified member. + /// + /// + /// If neither nor are specified, + /// the type of the consumer is assumed. + /// + public Type? Type { get; } + + /// + /// Gets the full name of the type containing the specified member. + /// + /// + /// If neither nor are specified, + /// the type of the consumer is assumed. + /// + public string? TypeName { get; } + + /// + /// Gets the assembly name of the specified type. + /// + /// + /// is only valid when is specified. + /// + public string? AssemblyName { get; } + + /// + /// Gets or sets the condition in which the dependency is applicable, e.g. "DEBUG". + /// + public string? Condition { get; set; } + } +} +#endif diff --git a/src/Core/src/System.Diagnostics.CodeAnalysis/DynamicallyAccessedMemberTypes.cs b/src/Core/src/System.Diagnostics.CodeAnalysis/DynamicallyAccessedMemberTypes.cs new file mode 100644 index 000000000000..59b523ccd428 --- /dev/null +++ b/src/Core/src/System.Diagnostics.CodeAnalysis/DynamicallyAccessedMemberTypes.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. +#if NETSTANDARD2_0 || NETSTANDARD2_1 +namespace System.Diagnostics.CodeAnalysis +{ + /// + /// Specifies the types of members that are dynamically accessed. + /// + /// This enumeration has a attribute that allows a + /// bitwise combination of its member values. + /// + [Flags] + internal enum DynamicallyAccessedMemberTypes + { + /// + /// Specifies no members. + /// + None = 0, + + /// + /// Specifies the default, parameterless public constructor. + /// + PublicParameterlessConstructor = 0x0001, + + /// + /// Specifies all public constructors. + /// + PublicConstructors = 0x0002 | PublicParameterlessConstructor, + + /// + /// Specifies all non-public constructors. + /// + NonPublicConstructors = 0x0004, + + /// + /// Specifies all public methods. + /// + PublicMethods = 0x0008, + + /// + /// Specifies all non-public methods. + /// + NonPublicMethods = 0x0010, + + /// + /// Specifies all public fields. + /// + PublicFields = 0x0020, + + /// + /// Specifies all non-public fields. + /// + NonPublicFields = 0x0040, + + /// + /// Specifies all public nested types. + /// + PublicNestedTypes = 0x0080, + + /// + /// Specifies all non-public nested types. + /// + NonPublicNestedTypes = 0x0100, + + /// + /// Specifies all public properties. + /// + PublicProperties = 0x0200, + + /// + /// Specifies all non-public properties. + /// + NonPublicProperties = 0x0400, + + /// + /// Specifies all public events. + /// + PublicEvents = 0x0800, + + /// + /// Specifies all non-public events. + /// + NonPublicEvents = 0x1000, + + /// + /// Specifies all members. + /// + All = ~None + } +} +#endif diff --git a/src/Core/src/System.Diagnostics.CodeAnalysis/DynamicallyAccessedMembersAttribute.cs b/src/Core/src/System.Diagnostics.CodeAnalysis/DynamicallyAccessedMembersAttribute.cs new file mode 100644 index 000000000000..9243e06fe7a2 --- /dev/null +++ b/src/Core/src/System.Diagnostics.CodeAnalysis/DynamicallyAccessedMembersAttribute.cs @@ -0,0 +1,57 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#if NETSTANDARD2_0 || NETSTANDARD2_1 +namespace System.Diagnostics.CodeAnalysis +{ + /// + /// Indicates that certain members on a specified are accessed dynamically, + /// for example through . + /// + /// + /// This allows tools to understand which members are being accessed during the execution + /// of a program. + /// + /// This attribute is valid on members whose type is or . + /// + /// When this attribute is applied to a location of type , the assumption is + /// that the string represents a fully qualified type name. + /// + /// When this attribute is applied to a class, interface, or struct, the members specified + /// can be accessed dynamically on instances returned from calling + /// on instances of that class, interface, or struct. + /// + /// If the attribute is applied to a method it's treated as a special case and it implies + /// the attribute should be applied to the "this" parameter of the method. As such the attribute + /// should only be used on instance methods of types assignable to System.Type (or string, but no methods + /// will use it there). + /// + [AttributeUsage( + AttributeTargets.Field | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter | + AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Method | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct, + Inherited = false)] +#if SYSTEM_PRIVATE_CORELIB + public +#else + internal +#endif + sealed class DynamicallyAccessedMembersAttribute : Attribute + { + /// + /// Initializes a new instance of the class + /// with the specified member types. + /// + /// The types of members dynamically accessed. + public DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes memberTypes) + { + MemberTypes = memberTypes; + } + + /// + /// Gets the which specifies the type + /// of members dynamically accessed. + /// + public DynamicallyAccessedMemberTypes MemberTypes { get; } + } +} +#endif diff --git a/src/Core/src/System.Diagnostics.CodeAnalysis/RequiresUnreferencedCodeAttribute.cs b/src/Core/src/System.Diagnostics.CodeAnalysis/RequiresUnreferencedCodeAttribute.cs new file mode 100644 index 000000000000..d82d4c703420 --- /dev/null +++ b/src/Core/src/System.Diagnostics.CodeAnalysis/RequiresUnreferencedCodeAttribute.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable +#if NETSTANDARD2_0 || NETSTANDARD2_1 +namespace System.Diagnostics.CodeAnalysis +{ + /// + /// Indicates that the specified method requires dynamic access to code that is not referenced + /// statically, for example through . + /// + /// + /// This allows tools to understand which methods are unsafe to call when removing unreferenced + /// code from an application. + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class, Inherited = false)] +#if SYSTEM_PRIVATE_CORELIB + public +#else + internal +#endif + sealed class RequiresUnreferencedCodeAttribute : Attribute + { + /// + /// Initializes a new instance of the class + /// with the specified message. + /// + /// + /// A message that contains information about the usage of unreferenced code. + /// + public RequiresUnreferencedCodeAttribute(string message) + { + Message = message; + } + + /// + /// Gets a message that contains information about the usage of unreferenced code. + /// + public string Message { get; } + + /// + /// Gets or sets an optional URL that contains more information about the method, + /// why it requries unreferenced code, and what options a consumer has to deal with it. + /// + public string? Url { get; set; } + } +} +#endif diff --git a/src/Core/src/System.Diagnostics.CodeAnalysis/UnconditionalSuppressMessageAttribute.cs b/src/Core/src/System.Diagnostics.CodeAnalysis/UnconditionalSuppressMessageAttribute.cs new file mode 100644 index 000000000000..4ac333f96c0b --- /dev/null +++ b/src/Core/src/System.Diagnostics.CodeAnalysis/UnconditionalSuppressMessageAttribute.cs @@ -0,0 +1,88 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable +#if NETSTANDARD2_0 || NETSTANDARD2_1 +namespace System.Diagnostics.CodeAnalysis +{ + /// + /// Suppresses reporting of a specific rule violation, allowing multiple suppressions on a + /// single code artifact. + /// + /// + /// is different than + /// in that it doesn't have a + /// . So it is always preserved in the compiled assembly. + /// + [AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)] + internal sealed class UnconditionalSuppressMessageAttribute : Attribute + { + /// + /// Initializes a new instance of the + /// class, specifying the category of the tool and the identifier for an analysis rule. + /// + /// The category for the attribute. + /// The identifier of the analysis rule the attribute applies to. + public UnconditionalSuppressMessageAttribute(string category, string checkId) + { + Category = category; + CheckId = checkId; + } + + /// + /// Gets the category identifying the classification of the attribute. + /// + /// + /// The property describes the tool or tool analysis category + /// for which a message suppression attribute applies. + /// + public string Category { get; } + + /// + /// Gets the identifier of the analysis tool rule to be suppressed. + /// + /// + /// Concatenated together, the and + /// properties form a unique check identifier. + /// + public string CheckId { get; } + + /// + /// Gets or sets the scope of the code that is relevant for the attribute. + /// + /// + /// The Scope property is an optional argument that specifies the metadata scope for which + /// the attribute is relevant. + /// + public string? Scope { get; set; } + + /// + /// Gets or sets a fully qualified path that represents the target of the attribute. + /// + /// + /// The property is an optional argument identifying the analysis target + /// of the attribute. An example value is "System.IO.Stream.ctor():System.Void". + /// Because it is fully qualified, it can be long, particularly for targets such as parameters. + /// The analysis tool user interface should be capable of automatically formatting the parameter. + /// + public string? Target { get; set; } + + /// + /// Gets or sets an optional argument expanding on exclusion criteria. + /// + /// + /// The property is an optional argument that specifies additional + /// exclusion where the literal metadata target is not sufficiently precise. For example, + /// the cannot be applied within a method, + /// and it may be desirable to suppress a violation against a statement in the method that will + /// give a rule violation, but not against all statements in the method. + /// + public string? MessageId { get; set; } + + /// + /// Gets or sets the justification for suppressing the code analysis message. + /// + public string? Justification { get; set; } + } +} +#endif