From 353e0f8329b75f800251a5dc772510e7fd292f2c Mon Sep 17 00:00:00 2001 From: Chris Pulman Date: Wed, 24 Dec 2025 13:02:42 +0000 Subject: [PATCH 1/4] Add AlsoNotify support to ReactiveAttribute and generator Introduces the AlsoNotify parameter to the ReactiveAttribute, allowing additional property change notifications to be specified. Updates the source generator to handle AlsoNotify, emitting RaisePropertyChanged calls for the specified properties in generated setters. Includes new and updated tests to verify the new behavior, and updates documentation to describe the new feature. --- README.md | 6 +- ...rceGenerators.AccessModifier.g.verified.cs | 58 ++++++++++++++++++ ...Generators.ReactiveAttribute.g.verified.cs | 51 ++++++++++++++++ ...ify#TestNs.TestVM.Properties.g.verified.cs | 28 +++++++++ ...Generators.ReactiveAttribute.g.verified.cs | 13 ++++ ...Generators.ReactiveAttribute.g.verified.cs | 13 ++++ ...tes#TestNs.TestVM.Properties.g.verified.cs | 5 +- ...Generators.ReactiveAttribute.g.verified.cs | 13 ++++ ...ies#TestNs.TestVM.Properties.g.verified.cs | 5 +- ...Generators.ReactiveAttribute.g.verified.cs | 13 ++++ ...lue#TestNs.TestVM.Properties.g.verified.cs | 5 +- ...Generators.ReactiveAttribute.g.verified.cs | 13 ++++ ...ess#TestNs.TestVM.Properties.g.verified.cs | 5 +- ...Generators.ReactiveAttribute.g.verified.cs | 13 ++++ ...nce#TestNs.TestVM.Properties.g.verified.cs | 5 +- ...Generators.ReactiveAttribute.g.verified.cs | 13 ++++ ...ss#TestNs1.TestVM.Properties.g.verified.cs | 5 +- ...ss#TestNs2.TestVM.Properties.g.verified.cs | 5 +- ...Generators.ReactiveAttribute.g.verified.cs | 13 ++++ ...nit#TestNs.TestVM.Properties.g.verified.cs | 5 +- ...Generators.ReactiveAttribute.g.verified.cs | 13 ++++ ...3+TestInnerClass1.Properties.g.verified.cs | 10 +++- ...2+TestInnerClass3.Properties.g.verified.cs | 10 +++- ...3+TestInnerClass2.Properties.g.verified.cs | 10 +++- ...s1.TestViewModel3.Properties.g.verified.cs | 10 +++- .../UnitTests/ReactiveGeneratorTests.cs | 28 +++++++++ .../TestViewModel.cs | 2 +- .../AttributeDefinitions.cs | 26 ++++++++ .../Reactive/Models/PropertyInfo.cs | 3 +- .../Reactive/ReactiveGenerator.Execute.cs | 60 +++++++++++++++++-- 30 files changed, 436 insertions(+), 23 deletions(-) create mode 100644 src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveFromPartialWithAlsoNotify#ReactiveUI.SourceGenerators.AccessModifier.g.verified.cs create mode 100644 src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveFromPartialWithAlsoNotify#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs create mode 100644 src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveFromPartialWithAlsoNotify#TestNs.TestVM.Properties.g.verified.cs diff --git a/README.md b/README.md index 6d5a239..8ed5a8a 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ ReactiveUI Source Generators automatically generate ReactiveUI objects to stream - `[Reactive(SetModifier = AccessModifier.Protected)]` With field and access modifiers, (Not Required for partial properties, configure set accessor with the property decalaration). - `[Reactive(Inheritance = InheritanceModifier.Virtual)]` With field and access modifiers. This will generate a virtual property. - `[Reactive(UseRequired = true)]` With field and access modifiers. This will generate a required property, (Not Required for partial properties, use required keyword for property decalaration). +- `[Reactive(nameof(RaiseProperty1), nameof(RaiseProperty2))]` With field and property changed notification for additional properties. - `[ObservableAsProperty]` With field, method, Observable property and partial property support (C# 13 Visual Studio Version 17.12.0) - `[ObservableAsProperty(ReadOnly = false)]` Removes readonly keyword from the generated helper field - `[ObservableAsProperty(PropertyName = "ReadOnlyPropertyName")]` @@ -91,6 +92,9 @@ Generates a derived list from a `ReadOnlyObservableCollection` backing field. ### `[ReactiveCollection]` Generates property changed notifications on add, remove, and new actions on an `ObservableCollection` backing field. +### `[IReactiveObject]` +Generates `IReactiveObject` implementation for classes not able to inherit from `ReactiveObject`. + ## Historical Approach ### Read-Write Properties @@ -685,7 +689,7 @@ public partial class MyReactiveClass : ReactiveObject } ``` -### ReactiveObject implementation for classes not able to inherit from ReactiveObject +### IReactiveObject implementation for classes not able to inherit from ReactiveObject ```csharp using ReactiveUI; using ReactiveUI.SourceGenerators; diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveFromPartialWithAlsoNotify#ReactiveUI.SourceGenerators.AccessModifier.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveFromPartialWithAlsoNotify#ReactiveUI.SourceGenerators.AccessModifier.g.verified.cs new file mode 100644 index 0000000..b46677d --- /dev/null +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveFromPartialWithAlsoNotify#ReactiveUI.SourceGenerators.AccessModifier.g.verified.cs @@ -0,0 +1,58 @@ +//HintName: ReactiveUI.SourceGenerators.AccessModifier.g.cs +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +// +#pragma warning disable +#nullable enable +namespace ReactiveUI.SourceGenerators; + +/// +/// AccessModifier. +/// +internal enum AccessModifier +{ + Public, + Protected, + Internal, + Private, + InternalProtected, + PrivateProtected, + Init, +} + +/// +/// Property Access Modifier. +/// +internal enum PropertyAccessModifier +{ + Public, + Protected, + Internal, + Private, + InternalProtected, + PrivateProtected, +} + +/// +/// InheritanceModifier. +/// +internal enum InheritanceModifier +{ + None, + Virtual, + Override, + New, +} + +internal enum SplatRegistrationType +{ + None, + LazySingleton, + Constant, + PerRequest, +} +#nullable restore +#pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveFromPartialWithAlsoNotify#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveFromPartialWithAlsoNotify#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs new file mode 100644 index 0000000..dcdaf7c --- /dev/null +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveFromPartialWithAlsoNotify#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs @@ -0,0 +1,51 @@ +//HintName: ReactiveUI.SourceGenerators.ReactiveAttribute.g.cs +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +// +#pragma warning disable +#nullable enable +namespace ReactiveUI.SourceGenerators; + +/// +/// ReactiveAttribute. +/// +/// +[global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property, AllowMultiple = false, Inherited = false)] +internal sealed class ReactiveAttribute : global::System.Attribute +{ + /// + /// Initializes a new instance of the class. + /// + public ReactiveAttribute(params string[] alsoNotify) + { + AlsoNotify = alsoNotify; + } + + /// + /// Gets the AccessModifier of the set property. + /// + /// + /// The AccessModifier of the set property. + /// + public AccessModifier SetModifier { get; init; } + + /// + /// Gets the InheritanceModifier of the property. + /// + public InheritanceModifier Inheritance { get; init; } + + /// + /// Use Required attribute to indicate that the property is required. + /// + public bool UseRequired { get; init; } + + /// + /// Gets the AlsoNotify properties to raise change notifications for. + /// + public string[]? AlsoNotify { get; } +} +#nullable restore +#pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveFromPartialWithAlsoNotify#TestNs.TestVM.Properties.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveFromPartialWithAlsoNotify#TestNs.TestVM.Properties.g.verified.cs new file mode 100644 index 0000000..02e65d6 --- /dev/null +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveFromPartialWithAlsoNotify#TestNs.TestVM.Properties.g.verified.cs @@ -0,0 +1,28 @@ +//HintName: TestNs.TestVM.Properties.g.cs +// +using ReactiveUI; + +#pragma warning disable +#nullable enable + +namespace TestNs +{ + + public partial class TestVM + { + + /// + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + public int Test4 + { + get => _test4; + [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_test4")] + set + { + this.RaiseAndSetIfChanged(ref _test4, value); + } + } + } +} +#nullable restore +#pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePartialProperties#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePartialProperties#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs index d4c80bd..dcdaf7c 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePartialProperties#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePartialProperties#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs @@ -16,6 +16,14 @@ namespace ReactiveUI.SourceGenerators; [global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property, AllowMultiple = false, Inherited = false)] internal sealed class ReactiveAttribute : global::System.Attribute { + /// + /// Initializes a new instance of the class. + /// + public ReactiveAttribute(params string[] alsoNotify) + { + AlsoNotify = alsoNotify; + } + /// /// Gets the AccessModifier of the set property. /// @@ -33,6 +41,11 @@ internal sealed class ReactiveAttribute : global::System.Attribute /// Use Required attribute to indicate that the property is required. /// public bool UseRequired { get; init; } + + /// + /// Gets the AlsoNotify properties to raise change notifications for. + /// + public string[]? AlsoNotify { get; } } #nullable restore #pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperiesWithAttributes#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperiesWithAttributes#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs index d4c80bd..dcdaf7c 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperiesWithAttributes#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperiesWithAttributes#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs @@ -16,6 +16,14 @@ namespace ReactiveUI.SourceGenerators; [global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property, AllowMultiple = false, Inherited = false)] internal sealed class ReactiveAttribute : global::System.Attribute { + /// + /// Initializes a new instance of the class. + /// + public ReactiveAttribute(params string[] alsoNotify) + { + AlsoNotify = alsoNotify; + } + /// /// Gets the AccessModifier of the set property. /// @@ -33,6 +41,11 @@ internal sealed class ReactiveAttribute : global::System.Attribute /// Use Required attribute to indicate that the property is required. /// public bool UseRequired { get; init; } + + /// + /// Gets the AlsoNotify properties to raise change notifications for. + /// + public string[]? AlsoNotify { get; } } #nullable restore #pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperiesWithAttributes#TestNs.TestVM.Properties.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperiesWithAttributes#TestNs.TestVM.Properties.g.verified.cs index cd0ffd2..4fd7e72 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperiesWithAttributes#TestNs.TestVM.Properties.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperiesWithAttributes#TestNs.TestVM.Properties.g.verified.cs @@ -19,7 +19,10 @@ public int Test3 { get => _test3; [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_test3")] - set => this.RaiseAndSetIfChanged(ref _test3, value); + set + { + this.RaiseAndSetIfChanged(ref _test3, value); + } } } } diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperties#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperties#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs index d4c80bd..dcdaf7c 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperties#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperties#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs @@ -16,6 +16,14 @@ namespace ReactiveUI.SourceGenerators; [global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property, AllowMultiple = false, Inherited = false)] internal sealed class ReactiveAttribute : global::System.Attribute { + /// + /// Initializes a new instance of the class. + /// + public ReactiveAttribute(params string[] alsoNotify) + { + AlsoNotify = alsoNotify; + } + /// /// Gets the AccessModifier of the set property. /// @@ -33,6 +41,11 @@ internal sealed class ReactiveAttribute : global::System.Attribute /// Use Required attribute to indicate that the property is required. /// public bool UseRequired { get; init; } + + /// + /// Gets the AlsoNotify properties to raise change notifications for. + /// + public string[]? AlsoNotify { get; } } #nullable restore #pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperties#TestNs.TestVM.Properties.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperties#TestNs.TestVM.Properties.g.verified.cs index 49b034f..a4ace90 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperties#TestNs.TestVM.Properties.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperties#TestNs.TestVM.Properties.g.verified.cs @@ -17,7 +17,10 @@ public int Test1 { get => _test1; [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_test1")] - set => this.RaiseAndSetIfChanged(ref _test1, value); + set + { + this.RaiseAndSetIfChanged(ref _test1, value); + } } } } diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesCalledValue#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesCalledValue#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs index d4c80bd..dcdaf7c 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesCalledValue#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesCalledValue#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs @@ -16,6 +16,14 @@ namespace ReactiveUI.SourceGenerators; [global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property, AllowMultiple = false, Inherited = false)] internal sealed class ReactiveAttribute : global::System.Attribute { + /// + /// Initializes a new instance of the class. + /// + public ReactiveAttribute(params string[] alsoNotify) + { + AlsoNotify = alsoNotify; + } + /// /// Gets the AccessModifier of the set property. /// @@ -33,6 +41,11 @@ internal sealed class ReactiveAttribute : global::System.Attribute /// Use Required attribute to indicate that the property is required. /// public bool UseRequired { get; init; } + + /// + /// Gets the AlsoNotify properties to raise change notifications for. + /// + public string[]? AlsoNotify { get; } } #nullable restore #pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesCalledValue#TestNs.TestVM.Properties.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesCalledValue#TestNs.TestVM.Properties.g.verified.cs index 283ab8d..7b92f8d 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesCalledValue#TestNs.TestVM.Properties.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesCalledValue#TestNs.TestVM.Properties.g.verified.cs @@ -17,7 +17,10 @@ public string Value { get => value; [global::System.Diagnostics.CodeAnalysis.MemberNotNull("this.value")] - set => this.RaiseAndSetIfChanged(ref this.value, value); + set + { + this.RaiseAndSetIfChanged(ref this.value, value); + } } } } diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAccess#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAccess#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs index d4c80bd..dcdaf7c 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAccess#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAccess#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs @@ -16,6 +16,14 @@ namespace ReactiveUI.SourceGenerators; [global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property, AllowMultiple = false, Inherited = false)] internal sealed class ReactiveAttribute : global::System.Attribute { + /// + /// Initializes a new instance of the class. + /// + public ReactiveAttribute(params string[] alsoNotify) + { + AlsoNotify = alsoNotify; + } + /// /// Gets the AccessModifier of the set property. /// @@ -33,6 +41,11 @@ internal sealed class ReactiveAttribute : global::System.Attribute /// Use Required attribute to indicate that the property is required. /// public bool UseRequired { get; init; } + + /// + /// Gets the AlsoNotify properties to raise change notifications for. + /// + public string[]? AlsoNotify { get; } } #nullable restore #pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAccess#TestNs.TestVM.Properties.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAccess#TestNs.TestVM.Properties.g.verified.cs index 89940cb..cd60891 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAccess#TestNs.TestVM.Properties.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAccess#TestNs.TestVM.Properties.g.verified.cs @@ -17,7 +17,10 @@ public int Test2 { get => _test2; [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_test2")] - set => this.RaiseAndSetIfChanged(ref _test2, value); + set + { + this.RaiseAndSetIfChanged(ref _test2, value); + } } } } diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAttributesAccessAndInheritance#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAttributesAccessAndInheritance#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs index d4c80bd..dcdaf7c 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAttributesAccessAndInheritance#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAttributesAccessAndInheritance#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs @@ -16,6 +16,14 @@ namespace ReactiveUI.SourceGenerators; [global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property, AllowMultiple = false, Inherited = false)] internal sealed class ReactiveAttribute : global::System.Attribute { + /// + /// Initializes a new instance of the class. + /// + public ReactiveAttribute(params string[] alsoNotify) + { + AlsoNotify = alsoNotify; + } + /// /// Gets the AccessModifier of the set property. /// @@ -33,6 +41,11 @@ internal sealed class ReactiveAttribute : global::System.Attribute /// Use Required attribute to indicate that the property is required. /// public bool UseRequired { get; init; } + + /// + /// Gets the AlsoNotify properties to raise change notifications for. + /// + public string[]? AlsoNotify { get; } } #nullable restore #pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAttributesAccessAndInheritance#TestNs.TestVM.Properties.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAttributesAccessAndInheritance#TestNs.TestVM.Properties.g.verified.cs index 92267be..325f145 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAttributesAccessAndInheritance#TestNs.TestVM.Properties.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAttributesAccessAndInheritance#TestNs.TestVM.Properties.g.verified.cs @@ -19,7 +19,10 @@ public string? Name { get => _name; [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_name")] - set => this.RaiseAndSetIfChanged(ref _name, value); + set + { + this.RaiseAndSetIfChanged(ref _name, value); + } } } } diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithIdenticalClass#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithIdenticalClass#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs index d4c80bd..dcdaf7c 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithIdenticalClass#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithIdenticalClass#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs @@ -16,6 +16,14 @@ namespace ReactiveUI.SourceGenerators; [global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property, AllowMultiple = false, Inherited = false)] internal sealed class ReactiveAttribute : global::System.Attribute { + /// + /// Initializes a new instance of the class. + /// + public ReactiveAttribute(params string[] alsoNotify) + { + AlsoNotify = alsoNotify; + } + /// /// Gets the AccessModifier of the set property. /// @@ -33,6 +41,11 @@ internal sealed class ReactiveAttribute : global::System.Attribute /// Use Required attribute to indicate that the property is required. /// public bool UseRequired { get; init; } + + /// + /// Gets the AlsoNotify properties to raise change notifications for. + /// + public string[]? AlsoNotify { get; } } #nullable restore #pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithIdenticalClass#TestNs1.TestVM.Properties.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithIdenticalClass#TestNs1.TestVM.Properties.g.verified.cs index 0e7ba6b..76ced8e 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithIdenticalClass#TestNs1.TestVM.Properties.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithIdenticalClass#TestNs1.TestVM.Properties.g.verified.cs @@ -19,7 +19,10 @@ public string? Name { get => _name; [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_name")] - set => this.RaiseAndSetIfChanged(ref _name, value); + set + { + this.RaiseAndSetIfChanged(ref _name, value); + } } } } diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithIdenticalClass#TestNs2.TestVM.Properties.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithIdenticalClass#TestNs2.TestVM.Properties.g.verified.cs index f27308e..172c7a9 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithIdenticalClass#TestNs2.TestVM.Properties.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithIdenticalClass#TestNs2.TestVM.Properties.g.verified.cs @@ -19,7 +19,10 @@ public string? Name { get => _name; [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_name")] - set => this.RaiseAndSetIfChanged(ref _name, value); + set + { + this.RaiseAndSetIfChanged(ref _name, value); + } } } } diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithInit#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithInit#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs index d4c80bd..dcdaf7c 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithInit#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithInit#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs @@ -16,6 +16,14 @@ namespace ReactiveUI.SourceGenerators; [global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property, AllowMultiple = false, Inherited = false)] internal sealed class ReactiveAttribute : global::System.Attribute { + /// + /// Initializes a new instance of the class. + /// + public ReactiveAttribute(params string[] alsoNotify) + { + AlsoNotify = alsoNotify; + } + /// /// Gets the AccessModifier of the set property. /// @@ -33,6 +41,11 @@ internal sealed class ReactiveAttribute : global::System.Attribute /// Use Required attribute to indicate that the property is required. /// public bool UseRequired { get; init; } + + /// + /// Gets the AlsoNotify properties to raise change notifications for. + /// + public string[]? AlsoNotify { get; } } #nullable restore #pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithInit#TestNs.TestVM.Properties.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithInit#TestNs.TestVM.Properties.g.verified.cs index f1b9a0e..7fae151 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithInit#TestNs.TestVM.Properties.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithInit#TestNs.TestVM.Properties.g.verified.cs @@ -17,7 +17,10 @@ public string MustBeSet { get => _mustBeSet; [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_mustBeSet")] - set => this.RaiseAndSetIfChanged(ref _mustBeSet, value); + set + { + this.RaiseAndSetIfChanged(ref _mustBeSet, value); + } } } } diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs index d4c80bd..dcdaf7c 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs @@ -16,6 +16,14 @@ namespace ReactiveUI.SourceGenerators; [global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property, AllowMultiple = false, Inherited = false)] internal sealed class ReactiveAttribute : global::System.Attribute { + /// + /// Initializes a new instance of the class. + /// + public ReactiveAttribute(params string[] alsoNotify) + { + AlsoNotify = alsoNotify; + } + /// /// Gets the AccessModifier of the set property. /// @@ -33,6 +41,11 @@ internal sealed class ReactiveAttribute : global::System.Attribute /// Use Required attribute to indicate that the property is required. /// public bool UseRequired { get; init; } + + /// + /// Gets the AlsoNotify properties to raise change notifications for. + /// + public string[]? AlsoNotify { get; } } #nullable restore #pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3+TestInnerClass1.Properties.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3+TestInnerClass1.Properties.g.verified.cs index f716a5c..8649744 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3+TestInnerClass1.Properties.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3+TestInnerClass1.Properties.g.verified.cs @@ -19,7 +19,10 @@ public int TestInner1 { get => _testInner1; [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_testInner1")] - set => this.RaiseAndSetIfChanged(ref _testInner1, value); + set + { + this.RaiseAndSetIfChanged(ref _testInner1, value); + } } /// @@ -28,7 +31,10 @@ public int TestInner11 { get => _testInner11; [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_testInner11")] - set => this.RaiseAndSetIfChanged(ref _testInner11, value); + set + { + this.RaiseAndSetIfChanged(ref _testInner11, value); + } } } } diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3+TestInnerClass2+TestInnerClass3.Properties.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3+TestInnerClass2+TestInnerClass3.Properties.g.verified.cs index 21bede2..458d475 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3+TestInnerClass2+TestInnerClass3.Properties.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3+TestInnerClass2+TestInnerClass3.Properties.g.verified.cs @@ -21,7 +21,10 @@ public int TestInner3 { get => _testInner3; [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_testInner3")] - set => this.RaiseAndSetIfChanged(ref _testInner3, value); + set + { + this.RaiseAndSetIfChanged(ref _testInner3, value); + } } /// @@ -30,7 +33,10 @@ public int TestInner33 { get => _testInner33; [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_testInner33")] - set => this.RaiseAndSetIfChanged(ref _testInner33, value); + set + { + this.RaiseAndSetIfChanged(ref _testInner33, value); + } } } } diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3+TestInnerClass2.Properties.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3+TestInnerClass2.Properties.g.verified.cs index ba38f3a..c40e062 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3+TestInnerClass2.Properties.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3+TestInnerClass2.Properties.g.verified.cs @@ -19,7 +19,10 @@ public int TestInner2 { get => _testInner2; [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_testInner2")] - set => this.RaiseAndSetIfChanged(ref _testInner2, value); + set + { + this.RaiseAndSetIfChanged(ref _testInner2, value); + } } /// @@ -28,7 +31,10 @@ public int TestInner22 { get => _testInner22; [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_testInner22")] - set => this.RaiseAndSetIfChanged(ref _testInner22, value); + set + { + this.RaiseAndSetIfChanged(ref _testInner22, value); + } } } } diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3.Properties.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3.Properties.g.verified.cs index 898c290..da230e4 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3.Properties.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3.Properties.g.verified.cs @@ -17,7 +17,10 @@ public float TestVM3Property { get => _testVM3Property; [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_testVM3Property")] - set => this.RaiseAndSetIfChanged(ref _testVM3Property, value); + set + { + this.RaiseAndSetIfChanged(ref _testVM3Property, value); + } } /// @@ -26,7 +29,10 @@ public float TestVM3Property2 { get => _testVM3Property2; [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_testVM3Property2")] - set => this.RaiseAndSetIfChanged(ref _testVM3Property2, value); + set + { + this.RaiseAndSetIfChanged(ref _testVM3Property2, value); + } } } } diff --git a/src/ReactiveUI.SourceGenerator.Tests/UnitTests/ReactiveGeneratorTests.cs b/src/ReactiveUI.SourceGenerator.Tests/UnitTests/ReactiveGeneratorTests.cs index 832455a..d1f30cb 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/UnitTests/ReactiveGeneratorTests.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/UnitTests/ReactiveGeneratorTests.cs @@ -325,4 +325,32 @@ public partial class TestVM : ReactiveObject // Act: Initialize the helper and run the generator. Assert: Verify the generated code. return TestHelper.TestPass(sourceCode); } + + /// + /// Froms the reactive from partial with also notify. + /// + /// A task to monitor the async. + [Test] + public Task FromReactiveFromPartialWithAlsoNotify() + { + // Arrange: Setup the source code that matches the generator input expectations. + const string sourceCode = """ + using System; + using ReactiveUI; + using ReactiveUI.SourceGenerators; + using System.Reactive.Linq; + namespace TestNs; + public partial class TestVM : ReactiveObject + { + [Reactive(nameof(OtherNotifyProperty))] + private int _test4; + + // This property is notified when Test4 changes + public int OtherNotifyProperty { get; set; } + } + """; + + // Act: Initialize the helper and run the generator. Assert: Verify the generated code. + return TestHelper.TestPass(sourceCode); + } } diff --git a/src/ReactiveUI.SourceGenerators.Execute/TestViewModel.cs b/src/ReactiveUI.SourceGenerators.Execute/TestViewModel.cs index e0f51a7..7e3a739 100644 --- a/src/ReactiveUI.SourceGenerators.Execute/TestViewModel.cs +++ b/src/ReactiveUI.SourceGenerators.Execute/TestViewModel.cs @@ -70,7 +70,7 @@ public partial class TestViewModel : ReactiveObject, IActivatableViewModel, IDis [Reactive(Inheritance = InheritanceModifier.Virtual, SetModifier = AccessModifier.Protected)] private string? _name; - [Reactive(SetModifier = AccessModifier.Init, UseRequired = true)] + [Reactive(nameof(MyDoubleProperty), nameof(MyStringProperty), SetModifier = AccessModifier.Init, UseRequired = true)] private string _mustBeSet; [Reactive] diff --git a/src/ReactiveUI.SourceGenerators.Roslyn/AttributeDefinitions.cs b/src/ReactiveUI.SourceGenerators.Roslyn/AttributeDefinitions.cs index 2cdfdd8..45e104b 100644 --- a/src/ReactiveUI.SourceGenerators.Roslyn/AttributeDefinitions.cs +++ b/src/ReactiveUI.SourceGenerators.Roslyn/AttributeDefinitions.cs @@ -184,6 +184,14 @@ namespace ReactiveUI.SourceGenerators; [global::System.AttributeUsage(global::System.AttributeTargets.Field | global::System.AttributeTargets.Property, AllowMultiple = false, Inherited = false)] internal sealed class ReactiveAttribute : global::System.Attribute { + /// + /// Initializes a new instance of the class. + /// + public ReactiveAttribute(params string[] alsoNotify) + { + AlsoNotify = alsoNotify; + } + /// /// Gets the AccessModifier of the set property. /// @@ -201,6 +209,11 @@ internal sealed class ReactiveAttribute : global::System.Attribute /// Use Required attribute to indicate that the property is required. /// public bool UseRequired { get; init; } + + /// + /// Gets the AlsoNotify properties to raise change notifications for. + /// + public string[]? AlsoNotify { get; } } #nullable restore #pragma warning restore @@ -225,6 +238,14 @@ namespace ReactiveUI.SourceGenerators; [global::System.AttributeUsage(global::System.AttributeTargets.Field, AllowMultiple = false, Inherited = false)] internal sealed class ReactiveAttribute : global::System.Attribute { + /// + /// Initializes a new instance of the class. + /// + public ReactiveAttribute(params string[] alsoNotify) + { + AlsoNotify = alsoNotify; + } + /// /// Gets the AccessModifier of the set property. /// @@ -242,6 +263,11 @@ internal sealed class ReactiveAttribute : global::System.Attribute /// Use Required attribute to indicate that the property is required. /// public bool UseRequired { get; init; } + + /// + /// Gets the AlsoNotify properties to raise change notifications for. + /// + public string[]? AlsoNotify { get; } } #nullable restore #pragma warning restore diff --git a/src/ReactiveUI.SourceGenerators.Roslyn/Reactive/Models/PropertyInfo.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Reactive/Models/PropertyInfo.cs index 0488b5b..963ae10 100644 --- a/src/ReactiveUI.SourceGenerators.Roslyn/Reactive/Models/PropertyInfo.cs +++ b/src/ReactiveUI.SourceGenerators.Roslyn/Reactive/Models/PropertyInfo.cs @@ -23,4 +23,5 @@ internal sealed record PropertyInfo( string Inheritance, string UseRequired, bool IsProperty, - string PropertyAccessModifier); + string PropertyAccessModifier, + EquatableArray AlsoNotify); diff --git a/src/ReactiveUI.SourceGenerators.Roslyn/Reactive/ReactiveGenerator.Execute.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Reactive/ReactiveGenerator.Execute.cs index 9424d57..a6efcaa 100644 --- a/src/ReactiveUI.SourceGenerators.Roslyn/Reactive/ReactiveGenerator.Execute.cs +++ b/src/ReactiveUI.SourceGenerators.Roslyn/Reactive/ReactiveGenerator.Execute.cs @@ -149,6 +149,21 @@ public sealed partial class ReactiveGenerator token.ThrowIfCancellationRequested(); + var alsoNotify = attributeData.GetConstructorArguments(); + using var alsoNotifyBuilder = ImmutableArrayBuilder.Rent(); + if (alsoNotify is not null) + { + foreach (var notify in alsoNotify) + { + if (!string.IsNullOrEmpty(notify) && !string.Equals(notify, propertyName, StringComparison.Ordinal)) + { + alsoNotifyBuilder.Add(notify!); + } + } + } + + token.ThrowIfCancellationRequested(); + // Get the containing type info var targetInfo = TargetInfo.From(propertySymbol.ContainingType); @@ -167,7 +182,8 @@ public sealed partial class ReactiveGenerator inheritance, useRequired, true, - propertyAccessModifier!), + propertyAccessModifier!, + alsoNotifyBuilder.ToImmutable()), builder.ToImmutable()); } #endif @@ -275,6 +291,21 @@ public sealed partial class ReactiveGenerator token.ThrowIfCancellationRequested(); + var alsoNotify = attributeData.GetConstructorArguments(); + using var alsoNotifyBuilder = ImmutableArrayBuilder.Rent(); + if (alsoNotify is not null) + { + foreach (var notify in alsoNotify) + { + if (!string.IsNullOrEmpty(notify) && !string.Equals(notify, propertyName, StringComparison.Ordinal)) + { + alsoNotifyBuilder.Add(notify!); + } + } + } + + token.ThrowIfCancellationRequested(); + // Get the containing type info var targetInfo = TargetInfo.From(fieldSymbol.ContainingType); @@ -293,7 +324,8 @@ public sealed partial class ReactiveGenerator inheritance, useRequired, false, - "public"), + "public", + alsoNotifyBuilder.ToImmutable()), builder.ToImmutable()); } @@ -396,6 +428,16 @@ private static string GetPropertySyntax(PropertyInfo propertyInfo) propertyAttributes = string.Join("\n ", AttributeDefinitions.ExcludeFromCodeCoverage.Concat(propertyInfo.ForwardedAttributes)); } + var alsoNotifyAttributes = string.Empty; + if (propertyInfo.AlsoNotify.IsEmpty) + { + alsoNotifyAttributes = string.Empty; + } + else + { + alsoNotifyAttributes = string.Concat(propertyInfo.AlsoNotify.Select(an => $"\n this.RaisePropertyChanged(nameof({an}));")); + } + var accessModifier = propertyInfo.PropertyAccessModifier; var setAccessModifier = propertyInfo.SetAccessModifier; @@ -411,7 +453,10 @@ private static string GetPropertySyntax(PropertyInfo propertyInfo) { get => {{getFieldName}}; [global::System.Diagnostics.CodeAnalysis.MemberNotNull("{{setFieldName}}")] - {{setAccessModifier}} => this.RaiseAndSetIfChanged(ref {{setFieldName}}, value); + {{setAccessModifier}} + { + this.RaiseAndSetIfChanged(ref {{setFieldName}}, value);{{alsoNotifyAttributes}} + } } """; } @@ -422,7 +467,14 @@ private static string GetPropertySyntax(PropertyInfo propertyInfo) /// [global::System.CodeDom.Compiler.GeneratedCode("{{GeneratorName}}", "{{GeneratorVersion}}")] {{propertyAttributes}} - {{accessModifier}}{{propertyInfo.Inheritance}} {{propertyInfo.UseRequired}}{{partialModifier}}{{propertyInfo.TypeNameWithNullabilityAnnotations}} {{propertyInfo.PropertyName}} { get => {{getFieldName}}; {{setAccessModifier}} => this.RaiseAndSetIfChanged(ref {{setFieldName}}, value); } + {{accessModifier}}{{propertyInfo.Inheritance}} {{propertyInfo.UseRequired}}{{partialModifier}}{{propertyInfo.TypeNameWithNullabilityAnnotations}} {{propertyInfo.PropertyName}} + { + get => {{getFieldName}}; + {{setAccessModifier}} + { + this.RaiseAndSetIfChanged(ref {{setFieldName}}, value);{{alsoNotifyAttributes}} + } + } """; } } From 0e28091b79b07ab42148e471c27a5977d123daac Mon Sep 17 00:00:00 2001 From: Chris Pulman Date: Wed, 24 Dec 2025 13:44:10 +0000 Subject: [PATCH 2/4] Trim whitespace and refactor AlsoNotify property handling Removed trailing whitespace in generated property setters across multiple test verification files. Refactored AlsoNotify property handling in the source generator to filter and add valid property names more efficiently, and simplified the code for generating AlsoNotify attributes. Updated a test summary for clarity. --- ...ify#TestNs.TestVM.Properties.g.verified.cs | 2 +- ...tes#TestNs.TestVM.Properties.g.verified.cs | 2 +- ...ies#TestNs.TestVM.Properties.g.verified.cs | 2 +- ...lue#TestNs.TestVM.Properties.g.verified.cs | 2 +- ...ess#TestNs.TestVM.Properties.g.verified.cs | 2 +- ...nce#TestNs.TestVM.Properties.g.verified.cs | 2 +- ...ss#TestNs1.TestVM.Properties.g.verified.cs | 2 +- ...ss#TestNs2.TestVM.Properties.g.verified.cs | 2 +- ...nit#TestNs.TestVM.Properties.g.verified.cs | 2 +- ...3+TestInnerClass1.Properties.g.verified.cs | 4 ++-- ...2+TestInnerClass3.Properties.g.verified.cs | 4 ++-- ...3+TestInnerClass2.Properties.g.verified.cs | 4 ++-- ...s1.TestViewModel3.Properties.g.verified.cs | 4 ++-- .../UnitTests/ReactiveGeneratorTests.cs | 2 +- .../Reactive/ReactiveGenerator.Execute.cs | 24 +++++++------------ 15 files changed, 26 insertions(+), 34 deletions(-) diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveFromPartialWithAlsoNotify#TestNs.TestVM.Properties.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveFromPartialWithAlsoNotify#TestNs.TestVM.Properties.g.verified.cs index 02e65d6..36376f8 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveFromPartialWithAlsoNotify#TestNs.TestVM.Properties.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveFromPartialWithAlsoNotify#TestNs.TestVM.Properties.g.verified.cs @@ -19,7 +19,7 @@ public int Test4 [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_test4")] set { - this.RaiseAndSetIfChanged(ref _test4, value); + this.RaiseAndSetIfChanged(ref _test4, value); } } } diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperiesWithAttributes#TestNs.TestVM.Properties.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperiesWithAttributes#TestNs.TestVM.Properties.g.verified.cs index 4fd7e72..8a2e02d 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperiesWithAttributes#TestNs.TestVM.Properties.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperiesWithAttributes#TestNs.TestVM.Properties.g.verified.cs @@ -21,7 +21,7 @@ public int Test3 [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_test3")] set { - this.RaiseAndSetIfChanged(ref _test3, value); + this.RaiseAndSetIfChanged(ref _test3, value); } } } diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperties#TestNs.TestVM.Properties.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperties#TestNs.TestVM.Properties.g.verified.cs index a4ace90..55a7f05 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperties#TestNs.TestVM.Properties.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperties#TestNs.TestVM.Properties.g.verified.cs @@ -19,7 +19,7 @@ public int Test1 [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_test1")] set { - this.RaiseAndSetIfChanged(ref _test1, value); + this.RaiseAndSetIfChanged(ref _test1, value); } } } diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesCalledValue#TestNs.TestVM.Properties.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesCalledValue#TestNs.TestVM.Properties.g.verified.cs index 7b92f8d..f0515a5 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesCalledValue#TestNs.TestVM.Properties.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesCalledValue#TestNs.TestVM.Properties.g.verified.cs @@ -19,7 +19,7 @@ public string Value [global::System.Diagnostics.CodeAnalysis.MemberNotNull("this.value")] set { - this.RaiseAndSetIfChanged(ref this.value, value); + this.RaiseAndSetIfChanged(ref this.value, value); } } } diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAccess#TestNs.TestVM.Properties.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAccess#TestNs.TestVM.Properties.g.verified.cs index cd60891..03151e4 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAccess#TestNs.TestVM.Properties.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAccess#TestNs.TestVM.Properties.g.verified.cs @@ -19,7 +19,7 @@ public int Test2 [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_test2")] set { - this.RaiseAndSetIfChanged(ref _test2, value); + this.RaiseAndSetIfChanged(ref _test2, value); } } } diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAttributesAccessAndInheritance#TestNs.TestVM.Properties.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAttributesAccessAndInheritance#TestNs.TestVM.Properties.g.verified.cs index 325f145..6eb33cf 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAttributesAccessAndInheritance#TestNs.TestVM.Properties.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAttributesAccessAndInheritance#TestNs.TestVM.Properties.g.verified.cs @@ -21,7 +21,7 @@ public string? Name [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_name")] set { - this.RaiseAndSetIfChanged(ref _name, value); + this.RaiseAndSetIfChanged(ref _name, value); } } } diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithIdenticalClass#TestNs1.TestVM.Properties.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithIdenticalClass#TestNs1.TestVM.Properties.g.verified.cs index 76ced8e..bb716ed 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithIdenticalClass#TestNs1.TestVM.Properties.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithIdenticalClass#TestNs1.TestVM.Properties.g.verified.cs @@ -21,7 +21,7 @@ public string? Name [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_name")] set { - this.RaiseAndSetIfChanged(ref _name, value); + this.RaiseAndSetIfChanged(ref _name, value); } } } diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithIdenticalClass#TestNs2.TestVM.Properties.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithIdenticalClass#TestNs2.TestVM.Properties.g.verified.cs index 172c7a9..75dc500 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithIdenticalClass#TestNs2.TestVM.Properties.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithIdenticalClass#TestNs2.TestVM.Properties.g.verified.cs @@ -21,7 +21,7 @@ public string? Name [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_name")] set { - this.RaiseAndSetIfChanged(ref _name, value); + this.RaiseAndSetIfChanged(ref _name, value); } } } diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithInit#TestNs.TestVM.Properties.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithInit#TestNs.TestVM.Properties.g.verified.cs index 7fae151..1514954 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithInit#TestNs.TestVM.Properties.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithInit#TestNs.TestVM.Properties.g.verified.cs @@ -19,7 +19,7 @@ public string MustBeSet [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_mustBeSet")] set { - this.RaiseAndSetIfChanged(ref _mustBeSet, value); + this.RaiseAndSetIfChanged(ref _mustBeSet, value); } } } diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3+TestInnerClass1.Properties.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3+TestInnerClass1.Properties.g.verified.cs index 8649744..68d382c 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3+TestInnerClass1.Properties.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3+TestInnerClass1.Properties.g.verified.cs @@ -21,7 +21,7 @@ public int TestInner1 [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_testInner1")] set { - this.RaiseAndSetIfChanged(ref _testInner1, value); + this.RaiseAndSetIfChanged(ref _testInner1, value); } } @@ -33,7 +33,7 @@ public int TestInner11 [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_testInner11")] set { - this.RaiseAndSetIfChanged(ref _testInner11, value); + this.RaiseAndSetIfChanged(ref _testInner11, value); } } } diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3+TestInnerClass2+TestInnerClass3.Properties.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3+TestInnerClass2+TestInnerClass3.Properties.g.verified.cs index 458d475..64e8205 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3+TestInnerClass2+TestInnerClass3.Properties.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3+TestInnerClass2+TestInnerClass3.Properties.g.verified.cs @@ -23,7 +23,7 @@ public int TestInner3 [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_testInner3")] set { - this.RaiseAndSetIfChanged(ref _testInner3, value); + this.RaiseAndSetIfChanged(ref _testInner3, value); } } @@ -35,7 +35,7 @@ public int TestInner33 [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_testInner33")] set { - this.RaiseAndSetIfChanged(ref _testInner33, value); + this.RaiseAndSetIfChanged(ref _testInner33, value); } } } diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3+TestInnerClass2.Properties.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3+TestInnerClass2.Properties.g.verified.cs index c40e062..82b2dcb 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3+TestInnerClass2.Properties.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3+TestInnerClass2.Properties.g.verified.cs @@ -21,7 +21,7 @@ public int TestInner2 [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_testInner2")] set { - this.RaiseAndSetIfChanged(ref _testInner2, value); + this.RaiseAndSetIfChanged(ref _testInner2, value); } } @@ -33,7 +33,7 @@ public int TestInner22 [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_testInner22")] set { - this.RaiseAndSetIfChanged(ref _testInner22, value); + this.RaiseAndSetIfChanged(ref _testInner22, value); } } } diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3.Properties.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3.Properties.g.verified.cs index da230e4..b8e54c8 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3.Properties.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#TestNs1.TestViewModel3.Properties.g.verified.cs @@ -19,7 +19,7 @@ public float TestVM3Property [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_testVM3Property")] set { - this.RaiseAndSetIfChanged(ref _testVM3Property, value); + this.RaiseAndSetIfChanged(ref _testVM3Property, value); } } @@ -31,7 +31,7 @@ public float TestVM3Property2 [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_testVM3Property2")] set { - this.RaiseAndSetIfChanged(ref _testVM3Property2, value); + this.RaiseAndSetIfChanged(ref _testVM3Property2, value); } } } diff --git a/src/ReactiveUI.SourceGenerator.Tests/UnitTests/ReactiveGeneratorTests.cs b/src/ReactiveUI.SourceGenerator.Tests/UnitTests/ReactiveGeneratorTests.cs index d1f30cb..f3ffa0b 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/UnitTests/ReactiveGeneratorTests.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/UnitTests/ReactiveGeneratorTests.cs @@ -327,7 +327,7 @@ public partial class TestVM : ReactiveObject } /// - /// Froms the reactive from partial with also notify. + /// Tests reactive property generation from a partial class with AlsoNotify support. /// /// A task to monitor the async. [Test] diff --git a/src/ReactiveUI.SourceGenerators.Roslyn/Reactive/ReactiveGenerator.Execute.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Reactive/ReactiveGenerator.Execute.cs index a6efcaa..568399e 100644 --- a/src/ReactiveUI.SourceGenerators.Roslyn/Reactive/ReactiveGenerator.Execute.cs +++ b/src/ReactiveUI.SourceGenerators.Roslyn/Reactive/ReactiveGenerator.Execute.cs @@ -149,16 +149,14 @@ public sealed partial class ReactiveGenerator token.ThrowIfCancellationRequested(); - var alsoNotify = attributeData.GetConstructorArguments(); + var alsoNotify = attributeData.GetConstructorArguments() + .Where(notify => !string.IsNullOrEmpty(notify) && !string.Equals(notify, propertyName, StringComparison.Ordinal)); using var alsoNotifyBuilder = ImmutableArrayBuilder.Rent(); if (alsoNotify is not null) { foreach (var notify in alsoNotify) { - if (!string.IsNullOrEmpty(notify) && !string.Equals(notify, propertyName, StringComparison.Ordinal)) - { - alsoNotifyBuilder.Add(notify!); - } + alsoNotifyBuilder.Add(notify!); } } @@ -291,16 +289,14 @@ public sealed partial class ReactiveGenerator token.ThrowIfCancellationRequested(); - var alsoNotify = attributeData.GetConstructorArguments(); + var alsoNotify = attributeData.GetConstructorArguments() + .Where(notify => !string.IsNullOrEmpty(notify) && !string.Equals(notify, propertyName, StringComparison.Ordinal)); using var alsoNotifyBuilder = ImmutableArrayBuilder.Rent(); if (alsoNotify is not null) { foreach (var notify in alsoNotify) { - if (!string.IsNullOrEmpty(notify) && !string.Equals(notify, propertyName, StringComparison.Ordinal)) - { - alsoNotifyBuilder.Add(notify!); - } + alsoNotifyBuilder.Add(notify!); } } @@ -429,11 +425,7 @@ private static string GetPropertySyntax(PropertyInfo propertyInfo) } var alsoNotifyAttributes = string.Empty; - if (propertyInfo.AlsoNotify.IsEmpty) - { - alsoNotifyAttributes = string.Empty; - } - else + if (!propertyInfo.AlsoNotify.IsEmpty) { alsoNotifyAttributes = string.Concat(propertyInfo.AlsoNotify.Select(an => $"\n this.RaisePropertyChanged(nameof({an}));")); } @@ -455,7 +447,7 @@ private static string GetPropertySyntax(PropertyInfo propertyInfo) [global::System.Diagnostics.CodeAnalysis.MemberNotNull("{{setFieldName}}")] {{setAccessModifier}} { - this.RaiseAndSetIfChanged(ref {{setFieldName}}, value);{{alsoNotifyAttributes}} + this.RaiseAndSetIfChanged(ref {{setFieldName}}, value);{{alsoNotifyAttributes}} } } """; From 1dd7bd544966bf76ec260b19fab0c485c5d7db87 Mon Sep 17 00:00:00 2001 From: Chris Pulman Date: Wed, 24 Dec 2025 13:55:43 +0000 Subject: [PATCH 3/4] Fix typos in README.md Corrected 'decalaration' to 'declaration' in two instances to improve documentation accuracy. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8ed5a8a..191920a 100644 --- a/README.md +++ b/README.md @@ -25,9 +25,9 @@ This documentation covers using ReactiveUI Source Generators to simplify and enh ReactiveUI Source Generators automatically generate ReactiveUI objects to streamline your code. These Source Generators are designed to work with ReactiveUI V19.5.31+ and support the following features: - `[Reactive]` With field and access modifiers, partial property support (C# 13 Visual Studio Version 17.12.0), partial properties with initializer support (C# preview only) -- `[Reactive(SetModifier = AccessModifier.Protected)]` With field and access modifiers, (Not Required for partial properties, configure set accessor with the property decalaration). +- `[Reactive(SetModifier = AccessModifier.Protected)]` With field and access modifiers, (Not Required for partial properties, configure set accessor with the property declaration). - `[Reactive(Inheritance = InheritanceModifier.Virtual)]` With field and access modifiers. This will generate a virtual property. -- `[Reactive(UseRequired = true)]` With field and access modifiers. This will generate a required property, (Not Required for partial properties, use required keyword for property decalaration). +- `[Reactive(UseRequired = true)]` With field and access modifiers. This will generate a required property, (Not Required for partial properties, use required keyword for property declaration). - `[Reactive(nameof(RaiseProperty1), nameof(RaiseProperty2))]` With field and property changed notification for additional properties. - `[ObservableAsProperty]` With field, method, Observable property and partial property support (C# 13 Visual Studio Version 17.12.0) - `[ObservableAsProperty(ReadOnly = false)]` Removes readonly keyword from the generated helper field From 37a422f557a874926127fe6baa86ce8dc85b7d7b Mon Sep 17 00:00:00 2001 From: Chris Pulman Date: Wed, 24 Dec 2025 14:00:02 +0000 Subject: [PATCH 4/4] Fix typo in XML doc comment for Inheritance property Corrected '' to '' in the XML documentation comments for the Inheritance property in ReactiveAttribute. Updated both the source and generated test files for consistency. --- ...eactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs | 4 ++-- ...eactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs | 4 ++-- ...eactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs | 4 ++-- ...eactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs | 4 ++-- ...eactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs | 4 ++-- ...eactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs | 4 ++-- ...eactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs | 4 ++-- ...eactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs | 4 ++-- ...eactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs | 4 ++-- ...eactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs | 4 ++-- .../AttributeDefinitions.cs | 4 ++-- 11 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveFromPartialWithAlsoNotify#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveFromPartialWithAlsoNotify#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs index dcdaf7c..0e92495 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveFromPartialWithAlsoNotify#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveFromPartialWithAlsoNotify#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs @@ -34,7 +34,7 @@ public ReactiveAttribute(params string[] alsoNotify) /// /// Gets the InheritanceModifier of the property. - /// + /// public InheritanceModifier Inheritance { get; init; } /// @@ -48,4 +48,4 @@ public ReactiveAttribute(params string[] alsoNotify) public string[]? AlsoNotify { get; } } #nullable restore -#pragma warning restore \ No newline at end of file +#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePartialProperties#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePartialProperties#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs index dcdaf7c..0e92495 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePartialProperties#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePartialProperties#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs @@ -34,7 +34,7 @@ public ReactiveAttribute(params string[] alsoNotify) /// /// Gets the InheritanceModifier of the property. - /// + /// public InheritanceModifier Inheritance { get; init; } /// @@ -48,4 +48,4 @@ public ReactiveAttribute(params string[] alsoNotify) public string[]? AlsoNotify { get; } } #nullable restore -#pragma warning restore \ No newline at end of file +#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperiesWithAttributes#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperiesWithAttributes#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs index dcdaf7c..0e92495 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperiesWithAttributes#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperiesWithAttributes#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs @@ -34,7 +34,7 @@ public ReactiveAttribute(params string[] alsoNotify) /// /// Gets the InheritanceModifier of the property. - /// + /// public InheritanceModifier Inheritance { get; init; } /// @@ -48,4 +48,4 @@ public ReactiveAttribute(params string[] alsoNotify) public string[]? AlsoNotify { get; } } #nullable restore -#pragma warning restore \ No newline at end of file +#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperties#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperties#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs index dcdaf7c..0e92495 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperties#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperties#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs @@ -34,7 +34,7 @@ public ReactiveAttribute(params string[] alsoNotify) /// /// Gets the InheritanceModifier of the property. - /// + /// public InheritanceModifier Inheritance { get; init; } /// @@ -48,4 +48,4 @@ public ReactiveAttribute(params string[] alsoNotify) public string[]? AlsoNotify { get; } } #nullable restore -#pragma warning restore \ No newline at end of file +#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesCalledValue#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesCalledValue#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs index dcdaf7c..0e92495 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesCalledValue#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesCalledValue#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs @@ -34,7 +34,7 @@ public ReactiveAttribute(params string[] alsoNotify) /// /// Gets the InheritanceModifier of the property. - /// + /// public InheritanceModifier Inheritance { get; init; } /// @@ -48,4 +48,4 @@ public ReactiveAttribute(params string[] alsoNotify) public string[]? AlsoNotify { get; } } #nullable restore -#pragma warning restore \ No newline at end of file +#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAccess#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAccess#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs index dcdaf7c..0e92495 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAccess#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAccess#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs @@ -34,7 +34,7 @@ public ReactiveAttribute(params string[] alsoNotify) /// /// Gets the InheritanceModifier of the property. - /// + /// public InheritanceModifier Inheritance { get; init; } /// @@ -48,4 +48,4 @@ public ReactiveAttribute(params string[] alsoNotify) public string[]? AlsoNotify { get; } } #nullable restore -#pragma warning restore \ No newline at end of file +#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAttributesAccessAndInheritance#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAttributesAccessAndInheritance#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs index dcdaf7c..0e92495 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAttributesAccessAndInheritance#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAttributesAccessAndInheritance#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs @@ -34,7 +34,7 @@ public ReactiveAttribute(params string[] alsoNotify) /// /// Gets the InheritanceModifier of the property. - /// + /// public InheritanceModifier Inheritance { get; init; } /// @@ -48,4 +48,4 @@ public ReactiveAttribute(params string[] alsoNotify) public string[]? AlsoNotify { get; } } #nullable restore -#pragma warning restore \ No newline at end of file +#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithIdenticalClass#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithIdenticalClass#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs index dcdaf7c..0e92495 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithIdenticalClass#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithIdenticalClass#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs @@ -34,7 +34,7 @@ public ReactiveAttribute(params string[] alsoNotify) /// /// Gets the InheritanceModifier of the property. - /// + /// public InheritanceModifier Inheritance { get; init; } /// @@ -48,4 +48,4 @@ public ReactiveAttribute(params string[] alsoNotify) public string[]? AlsoNotify { get; } } #nullable restore -#pragma warning restore \ No newline at end of file +#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithInit#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithInit#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs index dcdaf7c..0e92495 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithInit#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithInit#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs @@ -34,7 +34,7 @@ public ReactiveAttribute(params string[] alsoNotify) /// /// Gets the InheritanceModifier of the property. - /// + /// public InheritanceModifier Inheritance { get; init; } /// @@ -48,4 +48,4 @@ public ReactiveAttribute(params string[] alsoNotify) public string[]? AlsoNotify { get; } } #nullable restore -#pragma warning restore \ No newline at end of file +#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs index dcdaf7c..0e92495 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithNestedClass#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs @@ -34,7 +34,7 @@ public ReactiveAttribute(params string[] alsoNotify) /// /// Gets the InheritanceModifier of the property. - /// + /// public InheritanceModifier Inheritance { get; init; } /// @@ -48,4 +48,4 @@ public ReactiveAttribute(params string[] alsoNotify) public string[]? AlsoNotify { get; } } #nullable restore -#pragma warning restore \ No newline at end of file +#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerators.Roslyn/AttributeDefinitions.cs b/src/ReactiveUI.SourceGenerators.Roslyn/AttributeDefinitions.cs index 45e104b..55e3dbf 100644 --- a/src/ReactiveUI.SourceGenerators.Roslyn/AttributeDefinitions.cs +++ b/src/ReactiveUI.SourceGenerators.Roslyn/AttributeDefinitions.cs @@ -202,7 +202,7 @@ public ReactiveAttribute(params string[] alsoNotify) /// /// Gets the InheritanceModifier of the property. - /// + /// public InheritanceModifier Inheritance { get; init; } /// @@ -256,7 +256,7 @@ public ReactiveAttribute(params string[] alsoNotify) /// /// Gets the InheritanceModifier of the property. - /// + /// public InheritanceModifier Inheritance { get; init; } ///