diff --git a/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableValidatorValidateAllPropertiesGenerator.Execute.cs b/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableValidatorValidateAllPropertiesGenerator.Execute.cs
index fcad4998..758d3045 100644
--- a/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableValidatorValidateAllPropertiesGenerator.Execute.cs
+++ b/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableValidatorValidateAllPropertiesGenerator.Execute.cs
@@ -108,20 +108,56 @@ public static RecipientInfo GetInfo(INamedTypeSymbol typeSymbol, ImmutableArray<
///
/// Gets the head instance.
///
+ /// Indicates whether [DynamicallyAccessedMembers] should be generated.
/// The head instance with the type attributes.
- public static CompilationUnitSyntax GetSyntax()
+ public static CompilationUnitSyntax GetSyntax(bool isDynamicallyAccessedMembersAttributeAvailable)
{
+ int numberOfAttributes = 5 + (isDynamicallyAccessedMembersAttributeAvailable ? 1 : 0);
+ ImmutableArray.Builder attributes = ImmutableArray.CreateBuilder(numberOfAttributes);
+
+ // Prepare the base attributes with are always present:
+ //
+ // [global::System.CodeDom.Compiler.GeneratedCode("...", "...")]
+ // [global::System.Diagnostics.DebuggerNonUserCode]
+ // [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
+ // [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ // [global::System.Obsolete("This type is not intended to be used directly by user code")]
+ attributes.Add(
+ AttributeList(SingletonSeparatedList(
+ Attribute(IdentifierName($"global::System.CodeDom.Compiler.GeneratedCode")).AddArgumentListArguments(
+ AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(typeof(ObservableValidatorValidateAllPropertiesGenerator).FullName))),
+ AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(typeof(ObservableValidatorValidateAllPropertiesGenerator).Assembly.GetName().Version.ToString())))))));
+ attributes.Add(AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.DebuggerNonUserCode")))));
+ attributes.Add(AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage")))));
+ attributes.Add(
+ AttributeList(SingletonSeparatedList(
+ Attribute(IdentifierName("global::System.ComponentModel.EditorBrowsable")).AddArgumentListArguments(
+ AttributeArgument(ParseExpression("global::System.ComponentModel.EditorBrowsableState.Never"))))));
+ attributes.Add(
+ AttributeList(SingletonSeparatedList(
+ Attribute(IdentifierName("global::System.Obsolete")).AddArgumentListArguments(
+ AttributeArgument(LiteralExpression(
+ SyntaxKind.StringLiteralExpression,
+ Literal("This type is not intended to be used directly by user code")))))));
+
+ if (isDynamicallyAccessedMembersAttributeAvailable)
+ {
+ // Conditionally add the attribute to inform trimming, if the type is available:
+ //
+ // [global::System.CodeDom.Compiler.DynamicallyAccessedMembersAttribute(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods)]
+ attributes.Add(
+ AttributeList(SingletonSeparatedList(
+ Attribute(IdentifierName("global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute")).AddArgumentListArguments(
+ AttributeArgument(ParseExpression("global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods"))))));
+ }
+
// This code produces a compilation unit as follows:
//
// //
// #pragma warning disable
// namespace CommunityToolkit.Mvvm.ComponentModel.__Internals
// {
- // [global::System.CodeDom.Compiler.GeneratedCode("...", "...")]
- // [global::System.Diagnostics.DebuggerNonUserCode]
- // [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
- // [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
- // [global::System.Obsolete("This type is not intended to be used directly by user code")]
+ //
// internal static partial class __ObservableValidatorExtensions
// {
// }
@@ -135,21 +171,7 @@ public static CompilationUnitSyntax GetSyntax()
Token(SyntaxKind.InternalKeyword),
Token(SyntaxKind.StaticKeyword),
Token(SyntaxKind.PartialKeyword))
- .AddAttributeLists(
- AttributeList(SingletonSeparatedList(
- Attribute(IdentifierName($"global::System.CodeDom.Compiler.GeneratedCode")).AddArgumentListArguments(
- AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(typeof(ObservableValidatorValidateAllPropertiesGenerator).FullName))),
- AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(typeof(ObservableValidatorValidateAllPropertiesGenerator).Assembly.GetName().Version.ToString())))))),
- AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.DebuggerNonUserCode")))),
- AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage")))),
- AttributeList(SingletonSeparatedList(
- Attribute(IdentifierName("global::System.ComponentModel.EditorBrowsable")).AddArgumentListArguments(
- AttributeArgument(ParseExpression("global::System.ComponentModel.EditorBrowsableState.Never"))))),
- AttributeList(SingletonSeparatedList(
- Attribute(IdentifierName("global::System.Obsolete")).AddArgumentListArguments(
- AttributeArgument(LiteralExpression(
- SyntaxKind.StringLiteralExpression,
- Literal("This type is not intended to be used directly by user code")))))))))
+ .AddAttributeLists(attributes.MoveToImmutable().ToArray())))
.NormalizeWhitespace();
}
diff --git a/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableValidatorValidateAllPropertiesGenerator.cs b/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableValidatorValidateAllPropertiesGenerator.cs
index 111011e0..965d5fc0 100644
--- a/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableValidatorValidateAllPropertiesGenerator.cs
+++ b/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableValidatorValidateAllPropertiesGenerator.cs
@@ -42,10 +42,19 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
.Collect()
.Select(static (item, _) => item.Length > 0);
+ // Check whether [DynamicallyAccessedMembers] is available
+ IncrementalValueProvider isDynamicallyAccessedMembersAttributeAvailable =
+ context.CompilationProvider
+ .Select(static (item, _) => item.HasAccessibleTypeWithMetadataName("System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute"));
+
+ // Gather the conditional flag and attribute availability
+ IncrementalValueProvider<(bool IsHeaderFileNeeded, bool IsDynamicallyAccessedMembersAttributeAvailable)> headerFileInfo =
+ isHeaderFileNeeded.Combine(isDynamicallyAccessedMembersAttributeAvailable);
+
// Generate the header file with the attributes
- context.RegisterConditionalImplementationSourceOutput(isHeaderFileNeeded, static context =>
+ context.RegisterConditionalImplementationSourceOutput(headerFileInfo, static (context, item) =>
{
- CompilationUnitSyntax compilationUnit = Execute.GetSyntax();
+ CompilationUnitSyntax compilationUnit = Execute.GetSyntax(item);
context.AddSource(
hintName: "__ObservableValidatorExtensions.cs",
diff --git a/CommunityToolkit.Mvvm.SourceGenerators/Extensions/IncrementalGeneratorInitializationContextExtensions.cs b/CommunityToolkit.Mvvm.SourceGenerators/Extensions/IncrementalGeneratorInitializationContextExtensions.cs
index 2ffc4fa8..b39c074d 100644
--- a/CommunityToolkit.Mvvm.SourceGenerators/Extensions/IncrementalGeneratorInitializationContextExtensions.cs
+++ b/CommunityToolkit.Mvvm.SourceGenerators/Extensions/IncrementalGeneratorInitializationContextExtensions.cs
@@ -65,22 +65,22 @@ public static void FilterWithLanguageVersion(
}
///
- /// Conditionally invokes
- /// if the value produced by the input is .
+ /// Conditionally invokes
+ /// if the value produced by the input is , and also supplying a given input state.
///
/// The input value being used.
/// The source instance.
/// The conditional to invoke.
- public static void RegisterConditionalImplementationSourceOutput(
+ public static void RegisterConditionalImplementationSourceOutput(
this IncrementalGeneratorInitializationContext context,
- IncrementalValueProvider source,
- Action action)
+ IncrementalValueProvider<(bool Condition, T State)> source,
+ Action action)
{
- context.RegisterImplementationSourceOutput(source, (context, flag) =>
+ context.RegisterImplementationSourceOutput(source, (context, item) =>
{
- if (flag)
+ if (item.Condition)
{
- action(context);
+ action(context, item.State);
}
});
}
diff --git a/CommunityToolkit.Mvvm.SourceGenerators/Messaging/IMessengerRegisterAllGenerator.Execute.cs b/CommunityToolkit.Mvvm.SourceGenerators/Messaging/IMessengerRegisterAllGenerator.Execute.cs
index 3f1d8200..a9c13cab 100644
--- a/CommunityToolkit.Mvvm.SourceGenerators/Messaging/IMessengerRegisterAllGenerator.Execute.cs
+++ b/CommunityToolkit.Mvvm.SourceGenerators/Messaging/IMessengerRegisterAllGenerator.Execute.cs
@@ -66,20 +66,56 @@ public static RecipientInfo GetInfo(INamedTypeSymbol typeSymbol, ImmutableArray<
///
/// Gets the head instance.
///
+ /// Indicates whether [DynamicallyAccessedMembers] should be generated.
/// The head instance with the type attributes.
- public static CompilationUnitSyntax GetSyntax()
+ public static CompilationUnitSyntax GetSyntax(bool isDynamicallyAccessedMembersAttributeAvailable)
{
+ int numberOfAttributes = 5 + (isDynamicallyAccessedMembersAttributeAvailable ? 1 : 0);
+ ImmutableArray.Builder attributes = ImmutableArray.CreateBuilder(numberOfAttributes);
+
+ // Prepare the base attributes with are always present:
+ //
+ // [global::System.CodeDom.Compiler.GeneratedCode("...", "...")]
+ // [global::System.Diagnostics.DebuggerNonUserCode]
+ // [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
+ // [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ // [global::System.Obsolete("This type is not intended to be used directly by user code")]
+ attributes.Add(
+ AttributeList(SingletonSeparatedList(
+ Attribute(IdentifierName($"global::System.CodeDom.Compiler.GeneratedCode")).AddArgumentListArguments(
+ AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(typeof(IMessengerRegisterAllGenerator).FullName))),
+ AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(typeof(IMessengerRegisterAllGenerator).Assembly.GetName().Version.ToString())))))));
+ attributes.Add(AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.DebuggerNonUserCode")))));
+ attributes.Add(AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage")))));
+ attributes.Add(
+ AttributeList(SingletonSeparatedList(
+ Attribute(IdentifierName("global::System.ComponentModel.EditorBrowsable")).AddArgumentListArguments(
+ AttributeArgument(ParseExpression("global::System.ComponentModel.EditorBrowsableState.Never"))))));
+ attributes.Add(
+ AttributeList(SingletonSeparatedList(
+ Attribute(IdentifierName("global::System.Obsolete")).AddArgumentListArguments(
+ AttributeArgument(LiteralExpression(
+ SyntaxKind.StringLiteralExpression,
+ Literal("This type is not intended to be used directly by user code")))))));
+
+ if (isDynamicallyAccessedMembersAttributeAvailable)
+ {
+ // Conditionally add the attribute to inform trimming, if the type is available:
+ //
+ // [global::System.CodeDom.Compiler.DynamicallyAccessedMembersAttribute(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods)]
+ attributes.Add(
+ AttributeList(SingletonSeparatedList(
+ Attribute(IdentifierName("global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute")).AddArgumentListArguments(
+ AttributeArgument(ParseExpression("global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods"))))));
+ }
+
// This code produces a compilation unit as follows:
//
// //
// #pragma warning disable
// namespace CommunityToolkit.Mvvm.Messaging.__Internals
// {
- // [global::System.CodeDom.Compiler.GeneratedCode("...", "...")]
- // [global::System.Diagnostics.DebuggerNonUserCode]
- // [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
- // [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
- // [global::System.Obsolete("This type is not intended to be used directly by user code")]
+ //
// internal static partial class __IMessengerExtensions
// {
// }
@@ -93,21 +129,7 @@ public static CompilationUnitSyntax GetSyntax()
Token(SyntaxKind.InternalKeyword),
Token(SyntaxKind.StaticKeyword),
Token(SyntaxKind.PartialKeyword))
- .AddAttributeLists(
- AttributeList(SingletonSeparatedList(
- Attribute(IdentifierName($"global::System.CodeDom.Compiler.GeneratedCode")).AddArgumentListArguments(
- AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(typeof(IMessengerRegisterAllGenerator).FullName))),
- AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(typeof(IMessengerRegisterAllGenerator).Assembly.GetName().Version.ToString())))))),
- AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.DebuggerNonUserCode")))),
- AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage")))),
- AttributeList(SingletonSeparatedList(
- Attribute(IdentifierName("global::System.ComponentModel.EditorBrowsable")).AddArgumentListArguments(
- AttributeArgument(ParseExpression("global::System.ComponentModel.EditorBrowsableState.Never"))))),
- AttributeList(SingletonSeparatedList(
- Attribute(IdentifierName("global::System.Obsolete")).AddArgumentListArguments(
- AttributeArgument(LiteralExpression(
- SyntaxKind.StringLiteralExpression,
- Literal("This type is not intended to be used directly by user code")))))))))
+ .AddAttributeLists(attributes.MoveToImmutable().ToArray())))
.NormalizeWhitespace();
}
diff --git a/CommunityToolkit.Mvvm.SourceGenerators/Messaging/IMessengerRegisterAllGenerator.cs b/CommunityToolkit.Mvvm.SourceGenerators/Messaging/IMessengerRegisterAllGenerator.cs
index a84cd4c3..d6de9352 100644
--- a/CommunityToolkit.Mvvm.SourceGenerators/Messaging/IMessengerRegisterAllGenerator.cs
+++ b/CommunityToolkit.Mvvm.SourceGenerators/Messaging/IMessengerRegisterAllGenerator.cs
@@ -57,10 +57,19 @@ item.Symbol.DeclaringSyntaxReferences[0] is SyntaxReference syntaxReference &&
.Collect()
.Select(static (item, _) => item.Length > 0);
+ // Check whether [DynamicallyAccessedMembers] is available
+ IncrementalValueProvider isDynamicallyAccessedMembersAttributeAvailable =
+ context.CompilationProvider
+ .Select(static (item, _) => item.HasAccessibleTypeWithMetadataName("System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute"));
+
+ // Gather the conditional flag and attribute availability
+ IncrementalValueProvider<(bool IsHeaderFileNeeded, bool IsDynamicallyAccessedMembersAttributeAvailable)> headerFileInfo =
+ isHeaderFileNeeded.Combine(isDynamicallyAccessedMembersAttributeAvailable);
+
// Generate the header file with the attributes
- context.RegisterConditionalImplementationSourceOutput(isHeaderFileNeeded, static context =>
+ context.RegisterConditionalImplementationSourceOutput(headerFileInfo, static (context, item) =>
{
- CompilationUnitSyntax compilationUnit = Execute.GetSyntax();
+ CompilationUnitSyntax compilationUnit = Execute.GetSyntax(item);
context.AddSource(
hintName: "__IMessengerExtensions.cs",
diff --git a/tests/CommunityToolkit.Mvvm.UnitTests/Test_ObservableRecipient.cs b/tests/CommunityToolkit.Mvvm.UnitTests/Test_ObservableRecipient.cs
index 8d55af0a..99c674dc 100644
--- a/tests/CommunityToolkit.Mvvm.UnitTests/Test_ObservableRecipient.cs
+++ b/tests/CommunityToolkit.Mvvm.UnitTests/Test_ObservableRecipient.cs
@@ -3,11 +3,18 @@
// See the LICENSE file in the project root for more information.
using System;
+#if !NET6_0_OR_GREATER
+using System.Collections.Generic;
+using System.Linq;
+#endif
+using System.Reflection;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using Microsoft.VisualStudio.TestTools.UnitTesting;
+#pragma warning disable CS0618
+
namespace CommunityToolkit.Mvvm.UnitTests;
[TestClass]
@@ -85,6 +92,23 @@ public void Test_ObservableRecipient_Broadcast(Type type)
Assert.AreEqual(message.PropertyName, nameof(SomeRecipient.Data));
}
+ [TestMethod]
+ public void Test_IRecipient_VerifyTrimmingAnnotation()
+ {
+#if NET6_0_OR_GREATER
+ System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute? attribute =
+ typeof(Messaging.__Internals.__IMessengerExtensions)
+ .GetCustomAttribute();
+
+ Assert.IsNotNull(attribute);
+ Assert.AreEqual(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods, attribute.MemberTypes);
+#else
+ IEnumerable attributes = typeof(Messaging.__Internals.__IMessengerExtensions).GetCustomAttributes();
+
+ Assert.IsFalse(attributes.Any(static a => a.GetType().Name is "DynamicallyAccessedMembersAttribute"));
+#endif
+ }
+
public class SomeRecipient : ObservableRecipient
{
public SomeRecipient()
diff --git a/tests/CommunityToolkit.Mvvm.UnitTests/Test_ObservableValidator.cs b/tests/CommunityToolkit.Mvvm.UnitTests/Test_ObservableValidator.cs
index f10e0483..504e2d88 100644
--- a/tests/CommunityToolkit.Mvvm.UnitTests/Test_ObservableValidator.cs
+++ b/tests/CommunityToolkit.Mvvm.UnitTests/Test_ObservableValidator.cs
@@ -12,6 +12,8 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;
+#pragma warning disable CS0618
+
namespace CommunityToolkit.Mvvm.UnitTests;
[TestClass]
@@ -477,6 +479,23 @@ public void Test_ObservableRecipient_ValidationOnNonValidatableProperties_WithFa
validationFunc(viewmodel.GetType())(viewmodel);
}
+ [TestMethod]
+ public void Test_ObservableValidator_VerifyTrimmingAnnotation()
+ {
+#if NET6_0_OR_GREATER
+ System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute? attribute =
+ typeof(ComponentModel.__Internals.__ObservableValidatorExtensions)
+ .GetCustomAttribute();
+
+ Assert.IsNotNull(attribute);
+ Assert.AreEqual(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods, attribute.MemberTypes);
+#else
+ IEnumerable attributes = typeof(ComponentModel.__Internals.__ObservableValidatorExtensions).GetCustomAttributes();
+
+ Assert.IsFalse(attributes.Any(static a => a.GetType().Name is "DynamicallyAccessedMembersAttribute"));
+#endif
+ }
+
public class Person : ObservableValidator
{
private string? name;