diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.Execute.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.Execute.cs index 5def3ccb8..caf2f213c 100644 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.Execute.cs +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.Execute.cs @@ -1113,13 +1113,13 @@ public static ImmutableArray GetOnPropertyChangeMethods /// /// Gets a instance with the cached args of a specified type. /// - /// The name of the generated type. - /// The argument type name. + /// The name of the generated type. + /// The argument type name. /// The sequence of property names to cache args for. /// A instance with the sequence of cached args, if any. private static CompilationUnitSyntax? GetKnownPropertyChangingOrChangedArgsSyntax( - string ContainingTypeName, - string ArgsTypeName, + string containingTypeName, + string argsTypeName, ImmutableArray names) { if (names.IsEmpty) @@ -1134,6 +1134,10 @@ public static ImmutableArray GetOnPropertyChangeMethods // #nullable enable // namespace CommunityToolkit.Mvvm.ComponentModel.__Internals // { + // /// + // /// A helper type providing cached, reusable instances + // /// for all properties generated with . + // /// // [global::System.CodeDom.Compiler.GeneratedCode("...", "...")] // [global::System.Diagnostics.DebuggerNonUserCode] // [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] @@ -1150,14 +1154,19 @@ public static ImmutableArray GetOnPropertyChangeMethods Comment("// "), Trivia(PragmaWarningDirectiveTrivia(Token(SyntaxKind.DisableKeyword), true)), Trivia(NullableDirectiveTrivia(Token(SyntaxKind.EnableKeyword), true)))).AddMembers( - ClassDeclaration(ContainingTypeName).AddModifiers( + ClassDeclaration(containingTypeName).AddModifiers( Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.StaticKeyword)).AddAttributeLists( AttributeList(SingletonSeparatedList( Attribute(IdentifierName($"global::System.CodeDom.Compiler.GeneratedCode")) .AddArgumentListArguments( AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(typeof(ObservablePropertyGenerator).FullName))), - AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(typeof(ObservablePropertyGenerator).Assembly.GetName().Version.ToString())))))), + AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(typeof(ObservablePropertyGenerator).Assembly.GetName().Version.ToString())))))) + .WithOpenBracketToken(Token(TriviaList( + Comment("/// "), + Comment($"/// A helper type providing cached, reusable instances"), + Comment("/// for all properties generated with ."), + Comment("/// ")), SyntaxKind.OpenBracketToken, TriviaList())), AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.DebuggerNonUserCode")))), AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage")))), AttributeList(SingletonSeparatedList( @@ -1168,31 +1177,32 @@ public static ImmutableArray GetOnPropertyChangeMethods AttributeArgument(LiteralExpression( SyntaxKind.StringLiteralExpression, Literal("This type is not intended to be used directly by user code"))))))) - .AddMembers(names.Select(name => CreateFieldDeclaration(ArgsTypeName, name)).ToArray()))) + .AddMembers(names.Select(name => CreateFieldDeclaration(argsTypeName, name)).ToArray()))) .NormalizeWhitespace(); } /// /// Creates a field declaration for a cached property changing/changed name. /// - /// The field type name (either or ). + /// The field fully qualified type name (either or ). /// The name of the cached property name. /// A instance for the input cached property name. - private static FieldDeclarationSyntax CreateFieldDeclaration(string typeName, string propertyName) + private static FieldDeclarationSyntax CreateFieldDeclaration(string fullyQualifiedTypeName, string propertyName) { // Create a static field with a cached property changed/changing argument for a specified property. // This code produces a field declaration as follows: // + // /// The cached instance for all "" generated properties. // [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] // [global::System.Obsolete("This field is not intended to be referenced directly by user code")] // public static readonly = new(""); return FieldDeclaration( - VariableDeclaration(IdentifierName(typeName)) + VariableDeclaration(IdentifierName(fullyQualifiedTypeName)) .AddVariables( VariableDeclarator(Identifier(propertyName)) .WithInitializer(EqualsValueClause( - ObjectCreationExpression(IdentifierName(typeName)) + ObjectCreationExpression(IdentifierName(fullyQualifiedTypeName)) .AddArgumentListArguments(Argument( LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(propertyName)))))))) .AddModifiers( @@ -1202,7 +1212,10 @@ private static FieldDeclarationSyntax CreateFieldDeclaration(string typeName, st .AddAttributeLists( AttributeList(SingletonSeparatedList( Attribute(IdentifierName("global::System.ComponentModel.EditorBrowsable")).AddArgumentListArguments( - AttributeArgument(ParseExpression("global::System.ComponentModel.EditorBrowsableState.Never"))))), + AttributeArgument(ParseExpression("global::System.ComponentModel.EditorBrowsableState.Never"))))) + .WithOpenBracketToken(Token(TriviaList( + Comment($"/// The cached instance for all \"{propertyName}\" generated properties.")), + SyntaxKind.OpenBracketToken, TriviaList())), AttributeList(SingletonSeparatedList( Attribute(IdentifierName("global::System.Obsolete")).AddArgumentListArguments( AttributeArgument(LiteralExpression( diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableValidatorValidateAllPropertiesGenerator.Execute.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableValidatorValidateAllPropertiesGenerator.Execute.cs index fb85b1836..3ff64228b 100644 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableValidatorValidateAllPropertiesGenerator.Execute.cs +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableValidatorValidateAllPropertiesGenerator.Execute.cs @@ -96,6 +96,9 @@ public static CompilationUnitSyntax GetSyntax(bool isDynamicallyAccessedMembersA // Prepare the base attributes with are always present: // + // /// + // /// A helper type with generated validation stubs for types deriving from . + // /// // [global::System.CodeDom.Compiler.GeneratedCode("...", "...")] // [global::System.Diagnostics.DebuggerNonUserCode] // [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] @@ -105,7 +108,11 @@ public static CompilationUnitSyntax GetSyntax(bool isDynamicallyAccessedMembersA 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()))))))); + AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(typeof(ObservableValidatorValidateAllPropertiesGenerator).Assembly.GetName().Version.ToString())))))) + .WithOpenBracketToken(Token(TriviaList( + Comment("/// "), + Comment("/// A helper type with generated validation stubs for types deriving from ."), + Comment("/// ")), SyntaxKind.OpenBracketToken, TriviaList()))); attributes.Add(AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.DebuggerNonUserCode"))))); attributes.Add(AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage"))))); attributes.Add( @@ -176,8 +183,14 @@ public static CompilationUnitSyntax GetSyntax(ValidationInfo validationInfo) // #pragma warning disable // namespace CommunityToolkit.Mvvm.ComponentModel.__Internals // { + // /// // partial class __ObservableValidatorExtensions // { + // /// + // /// Creates a validation stub for objects. + // /// + // /// Dummy parameter, only used to disambiguate the method signature. + // /// A validation stub for objects. // [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] // [global::System.Obsolete("This method is not intended to be called directly by user code")] // public static global::System.Action CreateAllPropertiesValidator( _) @@ -197,13 +210,20 @@ public static CompilationUnitSyntax GetSyntax(ValidationInfo validationInfo) NamespaceDeclaration(IdentifierName("CommunityToolkit.Mvvm.ComponentModel.__Internals")).WithLeadingTrivia(TriviaList( Comment("// "), Trivia(PragmaWarningDirectiveTrivia(Token(SyntaxKind.DisableKeyword), true)))).AddMembers( - ClassDeclaration("__ObservableValidatorExtensions").AddModifiers(Token(SyntaxKind.PartialKeyword)).AddMembers( + ClassDeclaration("__ObservableValidatorExtensions").AddModifiers( + Token(TriviaList(Comment("/// ")), SyntaxKind.PartialKeyword, TriviaList())).AddMembers( MethodDeclaration( GenericName("global::System.Action").AddTypeArgumentListArguments(PredefinedType(Token(SyntaxKind.ObjectKeyword))), Identifier("CreateAllPropertiesValidator")).AddAttributeLists( AttributeList(SingletonSeparatedList( Attribute(IdentifierName("global::System.ComponentModel.EditorBrowsable")).AddArgumentListArguments( - AttributeArgument(ParseExpression("global::System.ComponentModel.EditorBrowsableState.Never"))))), + AttributeArgument(ParseExpression("global::System.ComponentModel.EditorBrowsableState.Never"))))) + .WithOpenBracketToken(Token(TriviaList( + Comment("/// "), + Comment($"/// Creates a validation stub for objects."), + Comment("/// "), + Comment("/// Dummy parameter, only used to disambiguate the method signature."), + Comment($"/// A validation stub for objects.")), SyntaxKind.OpenBracketToken, TriviaList())), AttributeList(SingletonSeparatedList( Attribute(IdentifierName("global::System.Obsolete")).AddArgumentListArguments( AttributeArgument(LiteralExpression( diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/Messaging/IMessengerRegisterAllGenerator.Execute.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/Messaging/IMessengerRegisterAllGenerator.Execute.cs index 6a50d4b62..516a3bd2d 100644 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/Messaging/IMessengerRegisterAllGenerator.Execute.cs +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/Messaging/IMessengerRegisterAllGenerator.Execute.cs @@ -4,6 +4,7 @@ using System.Collections.Immutable; using System.Linq; +using CommunityToolkit.Mvvm.SourceGenerators.ComponentModel.Models; using CommunityToolkit.Mvvm.SourceGenerators.Extensions; using CommunityToolkit.Mvvm.SourceGenerators.Helpers; using CommunityToolkit.Mvvm.SourceGenerators.Messaging.Models; @@ -74,6 +75,9 @@ public static CompilationUnitSyntax GetSyntax(bool isDynamicallyAccessedMembersA // Prepare the base attributes with are always present: // + // /// + // /// A helper type with generated registration stubs for types implementing . + // /// // [global::System.CodeDom.Compiler.GeneratedCode("...", "...")] // [global::System.Diagnostics.DebuggerNonUserCode] // [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] @@ -83,7 +87,11 @@ public static CompilationUnitSyntax GetSyntax(bool isDynamicallyAccessedMembersA 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()))))))); + AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(typeof(IMessengerRegisterAllGenerator).Assembly.GetName().Version.ToString())))))) + .WithOpenBracketToken(Token(TriviaList( + Comment("/// "), + Comment("/// A helper type with generated registration stubs for types implementing ."), + Comment("/// ")), SyntaxKind.OpenBracketToken, TriviaList()))); attributes.Add(AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.DebuggerNonUserCode"))))); attributes.Add(AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage"))))); attributes.Add( @@ -146,6 +154,11 @@ public static CompilationUnitSyntax GetSyntax(RecipientInfo recipientInfo) // with no tokens, which is the most common scenario and will help particularly with AOT. // This code will produce a syntax tree as follows: // + // /// + // /// Creates a message registration stub for objects. + // /// + // /// Dummy parameter, only used to disambiguate the method signature. + // /// A message registration stub for objects. // [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] // [global::System.Obsolete("This method is not intended to be called directly by user code")] // public static global::System.Action CreateAllMessagesRegistrator( _) @@ -166,7 +179,13 @@ public static CompilationUnitSyntax GetSyntax(RecipientInfo recipientInfo) Identifier("CreateAllMessagesRegistrator")).AddAttributeLists( AttributeList(SingletonSeparatedList( Attribute(IdentifierName("global::System.ComponentModel.EditorBrowsable")).AddArgumentListArguments( - AttributeArgument(ParseExpression("global::System.ComponentModel.EditorBrowsableState.Never"))))), + AttributeArgument(ParseExpression("global::System.ComponentModel.EditorBrowsableState.Never"))))) + .WithOpenBracketToken(Token(TriviaList( + Comment("/// "), + Comment($"/// Creates a message registration stub for objects."), + Comment("/// "), + Comment("/// Dummy parameter, only used to disambiguate the method signature."), + Comment($"/// A message registration stub for objects.")), SyntaxKind.OpenBracketToken, TriviaList())), AttributeList(SingletonSeparatedList( Attribute(IdentifierName("global::System.Obsolete")).AddArgumentListArguments( AttributeArgument(LiteralExpression( @@ -199,6 +218,12 @@ public static CompilationUnitSyntax GetSyntax(RecipientInfo recipientInfo) // Note: the generic overload has a different name to simplify the lookup with reflection. // This code will produce a syntax tree as follows: // + // /// + // /// Creates a message registration stub for objects, with an input token. + // /// + // /// The type of tokens that will be used to register messages. + // /// Dummy parameter, only used to disambiguate the method signature. + // /// A message registration stub for objects. // [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] // [global::System.Obsolete("This method is not intended to be called directly by user code")] // public static global::System.Action CreateAllMessagesRegistratorWithToken( _) @@ -221,7 +246,14 @@ public static CompilationUnitSyntax GetSyntax(RecipientInfo recipientInfo) Identifier("CreateAllMessagesRegistratorWithToken")).AddAttributeLists( AttributeList(SingletonSeparatedList( Attribute(IdentifierName("global::System.ComponentModel.EditorBrowsable")).AddArgumentListArguments( - AttributeArgument(ParseExpression("global::System.ComponentModel.EditorBrowsableState.Never"))))), + AttributeArgument(ParseExpression("global::System.ComponentModel.EditorBrowsableState.Never"))))) + .WithOpenBracketToken(Token(TriviaList( + Comment("/// "), + Comment($"/// Creates a message registration stub for objects."), + Comment("/// "), + Comment("/// The type of tokens that will be used to register messages."), + Comment("/// Dummy parameter, only used to disambiguate the method signature."), + Comment($"/// A message registration stub for objects.")), SyntaxKind.OpenBracketToken, TriviaList())), AttributeList(SingletonSeparatedList( Attribute(IdentifierName("global::System.Obsolete")).AddArgumentListArguments( AttributeArgument(LiteralExpression( @@ -261,6 +293,7 @@ public static CompilationUnitSyntax GetSyntax(RecipientInfo recipientInfo) // #pragma warning disable // namespace CommunityToolkit.Mvvm.Messaging.__Internals // { + // /// // partial class __IMessengerExtensions // { // @@ -271,7 +304,8 @@ public static CompilationUnitSyntax GetSyntax(RecipientInfo recipientInfo) NamespaceDeclaration(IdentifierName("CommunityToolkit.Mvvm.Messaging.__Internals")).WithLeadingTrivia(TriviaList( Comment("// "), Trivia(PragmaWarningDirectiveTrivia(Token(SyntaxKind.DisableKeyword), true)))).AddMembers( - ClassDeclaration("__IMessengerExtensions").AddModifiers(Token(SyntaxKind.PartialKeyword)) + ClassDeclaration("__IMessengerExtensions").AddModifiers( + Token(TriviaList(Comment("/// ")), SyntaxKind.PartialKeyword, TriviaList())) .AddMembers(defaultChannelMethodDeclaration, customChannelMethodDeclaration))) .NormalizeWhitespace(); } diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/Models/HierarchyInfo.Syntax.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/Models/HierarchyInfo.Syntax.cs index e2e52a43c..0d03495d5 100644 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/Models/HierarchyInfo.Syntax.cs +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/Models/HierarchyInfo.Syntax.cs @@ -30,13 +30,14 @@ public CompilationUnitSyntax GetCompilationUnit( // Create the partial type declaration with the given member declarations. // This code produces a class declaration as follows: // + // /// // partial TYPE_NAME> // { // // } TypeDeclarationSyntax typeDeclarationSyntax = Hierarchy[0].GetSyntax() - .AddModifiers(Token(SyntaxKind.PartialKeyword)) + .AddModifiers(Token(TriviaList(Comment("/// ")), SyntaxKind.PartialKeyword, TriviaList())) .AddMembers(memberDeclarations.ToArray()); // Add the base list, if present @@ -50,7 +51,7 @@ public CompilationUnitSyntax GetCompilationUnit( { typeDeclarationSyntax = parentType.GetSyntax() - .AddModifiers(Token(SyntaxKind.PartialKeyword)) + .AddModifiers(Token(TriviaList(Comment("/// ")), SyntaxKind.PartialKeyword, TriviaList())) .AddMembers(typeDeclarationSyntax); } @@ -67,6 +68,10 @@ public CompilationUnitSyntax GetCompilationUnit( if (Namespace is "") { + // Special case for top level types with no namespace: we need to re-add the + // inheritdoc XML comment, as otherwise the call below would remove it. + syntaxTriviaList = syntaxTriviaList.Add(Comment("/// ")); + // If there is no namespace, attach the pragma directly to the declared type, // and skip the namespace declaration. This will produce code as follows: // diff --git a/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Test_SourceGeneratorsCodegen.cs b/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Test_SourceGeneratorsCodegen.cs index aa8aa0d17..0062d61dc 100644 --- a/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Test_SourceGeneratorsCodegen.cs +++ b/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Test_SourceGeneratorsCodegen.cs @@ -42,6 +42,7 @@ partial class MyViewModel : ObservableObject #nullable enable namespace MyApp { + /// partial class MyViewModel { /// @@ -98,6 +99,7 @@ public string Name #nullable enable namespace MyApp { + /// partial class MyViewModel { /// @@ -175,6 +177,7 @@ partial class MyViewModel : ObservableObject #nullable enable namespace MyApp { + /// partial class MyViewModel { /// @@ -252,6 +255,7 @@ partial class MyViewModel : ObservableObject #nullable enable namespace MyApp { + /// partial class MyViewModel { /// @@ -308,6 +312,7 @@ public T Content #nullable enable namespace MyApp { + /// partial class MyViewModel { /// @@ -390,6 +395,7 @@ partial class MyViewModel : ObservableObject #nullable enable namespace MyApp { + /// partial class MyViewModel { /// @@ -446,6 +452,7 @@ public T Content #nullable enable namespace MyApp { + /// partial class MyViewModel { /// @@ -525,6 +532,7 @@ partial class MyViewModel : ObservableObject #nullable enable namespace MyApp { + /// partial class MyViewModel { /// @@ -581,6 +589,7 @@ public T Content #nullable enable namespace MyApp { + /// partial class MyViewModel { /// @@ -659,6 +668,7 @@ partial class MyViewModel : ObservableObject #nullable enable namespace MyApp { + /// partial class MyViewModel { /// @@ -734,6 +744,7 @@ partial class MyViewModel : ObservableObject #nullable enable namespace MyApp { + /// partial class MyViewModel { /// @@ -810,6 +821,7 @@ partial class MyViewModel : ObservableObject #nullable enable namespace MyApp { + /// partial class MyViewModel { /// @@ -886,6 +898,7 @@ partial class MyViewModel : ObservableObject #nullable enable namespace MyApp { + /// partial class MyViewModel { /// @@ -963,6 +976,7 @@ partial class MyViewModel : ObservableObject #nullable enable namespace MyApp { + /// partial class MyViewModel { /// @@ -1040,6 +1054,7 @@ partial class MyViewModel : ObservableObject #nullable enable namespace MyApp { + /// partial class MyViewModel { /// @@ -1133,6 +1148,7 @@ public DefaultValueAttribute(object value) #nullable enable namespace MyApp { + /// partial class MyViewModel { /// @@ -1238,6 +1254,7 @@ public DefaultValueAttribute(object value) #nullable enable namespace MyApp { + /// partial class MyViewModel { /// The backing field for . @@ -1301,6 +1318,7 @@ private partial void Test2() #nullable enable namespace MyApp { + /// partial class MyViewModel { /// The backing field for . @@ -1320,6 +1338,7 @@ partial class MyViewModel #nullable enable namespace MyApp { + /// partial class MyViewModel { /// The backing field for . @@ -1386,6 +1405,7 @@ public ValueAttribute(object value) #nullable enable namespace MyApp { + /// partial class MyViewModel { /// The backing field for . @@ -1409,6 +1429,7 @@ partial class MyViewModel #nullable enable namespace MyApp { + /// partial class MyViewModel { /// The backing field for . @@ -1456,8 +1477,10 @@ partial class MyViewModel : ObservableObject #nullable enable namespace MyApp { + /// partial class Foo { + /// partial class MyViewModel { /// @@ -1515,6 +1538,166 @@ public string? A #endif } + [TestMethod] + public void ObservableProperty_ValidateGeneratedCachedArguments() + { + string source = """ + using System.ComponentModel; + using CommunityToolkit.Mvvm.ComponentModel; + + namespace MyApp; + + partial class MyViewModel : ObservableObject + { + [ObservableProperty] + private string? name; + } + """; + + string result = """ + // + #pragma warning disable + #nullable enable + namespace MyApp + { + /// + partial class MyViewModel + { + /// + [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.2.0.0")] + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + public string? Name + { + get => name; + set + { + if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(name, value)) + { + OnNameChanging(value); + OnNameChanging(default, value); + OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Name); + name = value; + OnNameChanged(value); + OnNameChanged(default, value); + OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Name); + } + } + } + + /// Executes the logic for when is changing. + /// The new property value being set. + /// This method is invoked right before the value of is changed. + [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.2.0.0")] + partial void OnNameChanging(string? value); + /// Executes the logic for when is changing. + /// The previous property value that is being replaced. + /// The new property value being set. + /// This method is invoked right before the value of is changed. + [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.2.0.0")] + partial void OnNameChanging(string? oldValue, string? newValue); + /// Executes the logic for when just changed. + /// The new property value that was set. + /// This method is invoked right after the value of is changed. + [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.2.0.0")] + partial void OnNameChanged(string? value); + /// Executes the logic for when just changed. + /// The previous property value that was replaced. + /// The new property value that was set. + /// This method is invoked right after the value of is changed. + [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.2.0.0")] + partial void OnNameChanged(string? oldValue, string? newValue); + } + } + """; + + string changingArgs = """ + // + #pragma warning disable + #nullable enable + namespace CommunityToolkit.Mvvm.ComponentModel.__Internals + { + /// + /// A helper type providing cached, reusable instances + /// for all properties generated with . + /// + [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.2.0.0")] + [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 class __KnownINotifyPropertyChangingArgs + { + /// The cached instance for all "Name" generated properties. + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + [global::System.Obsolete("This field is not intended to be referenced directly by user code")] + public static readonly global::System.ComponentModel.PropertyChangingEventArgs Name = new global::System.ComponentModel.PropertyChangingEventArgs("Name"); + } + } + """; + + string changedArgs = """ + // + #pragma warning disable + #nullable enable + namespace CommunityToolkit.Mvvm.ComponentModel.__Internals + { + /// + /// A helper type providing cached, reusable instances + /// for all properties generated with . + /// + [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.2.0.0")] + [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 class __KnownINotifyPropertyChangedArgs + { + /// The cached instance for all "Name" generated properties. + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + [global::System.Obsolete("This field is not intended to be referenced directly by user code")] + public static readonly global::System.ComponentModel.PropertyChangedEventArgs Name = new global::System.ComponentModel.PropertyChangedEventArgs("Name"); + } + } + """; + + VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel.g.cs", result), ("__KnownINotifyPropertyChangingArgs.g.cs", changingArgs), ("__KnownINotifyPropertyChangedArgs.g.cs", changedArgs)); + } + + [TestMethod] + public void RelayCommandMethodInTopLevelTypeWithNoNamespace_PreservesInheritdoc() + { + string source = """ + using CommunityToolkit.Mvvm.Input; + + partial class MyViewModel + { + [RelayCommand] + private void Test() + { + } + } + """; + + string result = """ + // + #pragma warning disable + #nullable enable + /// + partial class MyViewModel + { + /// The backing field for . + [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", "8.2.0.0")] + private global::CommunityToolkit.Mvvm.Input.RelayCommand? testCommand; + /// Gets an instance wrapping . + [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", "8.2.0.0")] + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + public global::CommunityToolkit.Mvvm.Input.IRelayCommand TestCommand => testCommand ??= new global::CommunityToolkit.Mvvm.Input.RelayCommand(new global::System.Action(Test)); + } + """; + + VerifyGenerateSources(source, new[] { new RelayCommandGenerator() }, ("MyViewModel.Test.g.cs", result)); + } + /// /// Generates the requested sources ///