diff --git a/README.md b/README.md index 9997a4f..df69ef7 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ 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]` +- `[Reactive]` With field and access modifiers, partial property support (C# 13 Visual Studio Version 17.12.0) - `[ObservableAsProperty]` - `[ObservableAsProperty(PropertyName = "ReadOnlyPropertyName")]` - `[ReactiveCommand]` @@ -131,6 +131,23 @@ public partial class MyReactiveClass : ReactiveObject } ``` +### Usage Reactive property from partial property + +Partial properties are supported in C# 13 and Visual Studio 17.12.0 and later. +Both the getter and setter must be empty, and the `[Reactive]` attribute must be placed on the property. +Override and Virtual properties are supported. +Set Access Modifier is also supported on partial properties. + +```csharp +using ReactiveUI.SourceGenerators; + +public partial class MyReactiveClass : ReactiveObject +{ + [Reactive] + public partial string MyProperty { get; set; } +} +``` + ## Usage ObservableAsPropertyHelper `[ObservableAsProperty]` ObservableAsPropertyHelper is used to create a read-only property from an IObservable. The generated code will create a backing field and a property that returns the value of the backing field. The backing field is initialized with the value of the IObservable when the class is instantiated. diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index f2ac444..95c2740 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -35,7 +35,7 @@ - + @@ -45,4 +45,4 @@ - \ No newline at end of file + diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePartialProperties#ReactiveUI.SourceGenerators.AccessModifier.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePartialProperties#ReactiveUI.SourceGenerators.AccessModifier.g.verified.cs new file mode 100644 index 0000000..2f7525d --- /dev/null +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePartialProperties#ReactiveUI.SourceGenerators.AccessModifier.g.verified.cs @@ -0,0 +1,37 @@ +//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, +} + +/// +/// InheritanceModifier. +/// +internal enum InheritanceModifier +{ + None, + Virtual, + Override, + New, +} +#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 new file mode 100644 index 0000000..a13d64c --- /dev/null +++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePartialProperties#ReactiveUI.SourceGenerators.ReactiveAttribute.g.verified.cs @@ -0,0 +1,40 @@ +//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. + +using System; + +// +#pragma warning disable +#nullable enable +namespace ReactiveUI.SourceGenerators; + +/// +/// ReactiveAttribute. +/// +/// +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] +internal sealed class ReactiveAttribute : Attribute +{ + /// + /// 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; } +} +#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 d8b5f0d..a13d64c 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 @@ -15,7 +15,7 @@ namespace ReactiveUI.SourceGenerators; /// ReactiveAttribute. /// /// -[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)] +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] internal sealed class ReactiveAttribute : Attribute { /// 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 3946232..2b9aa7f 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 @@ -12,6 +12,7 @@ namespace TestNs /// public partial class TestVM { + /// [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.Runtime.Serialization.DataMemberAttribute()] 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 d8b5f0d..a13d64c 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 @@ -15,7 +15,7 @@ namespace ReactiveUI.SourceGenerators; /// ReactiveAttribute. /// /// -[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)] +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] internal sealed class ReactiveAttribute : Attribute { /// 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 3b48333..1c5ad56 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 @@ -12,6 +12,7 @@ namespace TestNs /// public partial class TestVM { + /// [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] public int Test1 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 d8b5f0d..a13d64c 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 @@ -15,7 +15,7 @@ namespace ReactiveUI.SourceGenerators; /// ReactiveAttribute. /// /// -[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)] +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] internal sealed class ReactiveAttribute : Attribute { /// 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 94971df..240162b 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 @@ -12,6 +12,7 @@ namespace TestNs /// public partial class TestVM { + /// [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] public int Test2 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 d8b5f0d..a13d64c 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 @@ -15,7 +15,7 @@ namespace ReactiveUI.SourceGenerators; /// ReactiveAttribute. /// /// -[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)] +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] internal sealed class ReactiveAttribute : Attribute { /// 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 00d6b21..3728a75 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 @@ -12,11 +12,17 @@ namespace TestNs /// public partial class TestVM { + /// [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.Runtime.Serialization.DataMemberAttribute()] [global::System.Text.Json.Serialization.JsonIncludeAttribute()] - public string? Name { get => _name; set => this.RaiseAndSetIfChanged(ref _name, value); } + public string? Name + { + get => _name; + [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_name")] + set => this.RaiseAndSetIfChanged(ref _name, value); + } } } #nullable 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 d8b5f0d..a13d64c 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 @@ -15,7 +15,7 @@ namespace ReactiveUI.SourceGenerators; /// ReactiveAttribute. /// /// -[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)] +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] internal sealed class ReactiveAttribute : Attribute { /// 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 02aa8f9..159c9f2 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 @@ -12,11 +12,17 @@ namespace TestNs1 /// public partial class TestVM { + /// [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.Runtime.Serialization.DataMemberAttribute()] [global::System.Text.Json.Serialization.JsonIncludeAttribute()] - public string? Name { get => _name; set => this.RaiseAndSetIfChanged(ref _name, value); } + public string? Name + { + get => _name; + [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_name")] + set => this.RaiseAndSetIfChanged(ref _name, value); + } } } #nullable restore 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 eea6ffc..0a2dd96 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 @@ -12,11 +12,17 @@ namespace TestNs2 /// public partial class TestVM { + /// [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.Runtime.Serialization.DataMemberAttribute()] [global::System.Text.Json.Serialization.JsonIncludeAttribute()] - public string? Name { get => _name; set => this.RaiseAndSetIfChanged(ref _name, value); } + public string? Name + { + get => _name; + [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_name")] + set => this.RaiseAndSetIfChanged(ref _name, value); + } } } #nullable 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 d8b5f0d..a13d64c 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 @@ -15,7 +15,7 @@ namespace ReactiveUI.SourceGenerators; /// ReactiveAttribute. /// /// -[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)] +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] internal sealed class ReactiveAttribute : Attribute { /// 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 85a5659..fe7117d 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 @@ -12,6 +12,7 @@ namespace TestNs /// public partial class TestVM { + /// [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] public string MustBeSet 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 d8b5f0d..a13d64c 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 @@ -15,7 +15,7 @@ namespace ReactiveUI.SourceGenerators; /// ReactiveAttribute. /// /// -[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)] +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] internal sealed class ReactiveAttribute : Attribute { /// 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 c6a03c8..0fffb75 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 @@ -14,6 +14,7 @@ public partial class TestViewModel3 /// public partial class TestInnerClass1 { + /// [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] public int TestInner1 @@ -22,6 +23,7 @@ public int TestInner1 [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_testInner1")] set => this.RaiseAndSetIfChanged(ref _testInner1, value); } + /// [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] public int TestInner11 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 d0bdca0..1a8a826 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 @@ -16,6 +16,7 @@ public partial class TestInnerClass2 /// public partial class TestInnerClass3 { + /// [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] public int TestInner3 @@ -24,6 +25,7 @@ public int TestInner3 [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_testInner3")] set => this.RaiseAndSetIfChanged(ref _testInner3, value); } + /// [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] public int TestInner33 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 6e4d188..37cf5b9 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 @@ -14,6 +14,7 @@ public partial class TestViewModel3 /// public partial class TestInnerClass2 { + /// [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] public int TestInner2 @@ -22,6 +23,7 @@ public int TestInner2 [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_testInner2")] set => this.RaiseAndSetIfChanged(ref _testInner2, value); } + /// [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] public int TestInner22 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 3c4b0ee..14731ce 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 @@ -12,6 +12,7 @@ namespace TestNs1 /// public partial class TestViewModel3 { + /// [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] public float TestVM3Property @@ -20,6 +21,7 @@ public float TestVM3Property [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_testVM3Property")] set => this.RaiseAndSetIfChanged(ref _testVM3Property, value); } + /// [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] public float TestVM3Property2 diff --git a/src/ReactiveUI.SourceGenerator.Tests/ReactiveUI.SourceGenerators.Tests.csproj b/src/ReactiveUI.SourceGenerator.Tests/ReactiveUI.SourceGenerators.Tests.csproj index 36e09a3..1d7a0a5 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/ReactiveUI.SourceGenerators.Tests.csproj +++ b/src/ReactiveUI.SourceGenerator.Tests/ReactiveUI.SourceGenerators.Tests.csproj @@ -4,7 +4,7 @@ net8.0 enable enable - 12.0 + 13.0 false true $(NoWarn);CA1812;CA1001 @@ -42,7 +42,7 @@ - + diff --git a/src/ReactiveUI.SourceGenerator.Tests/TestHelper.cs b/src/ReactiveUI.SourceGenerator.Tests/TestHelper.cs index 4a0de0f..c004667 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/TestHelper.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/TestHelper.cs @@ -104,7 +104,7 @@ public async Task InitializeAsync() // Download necessary NuGet package files. var inputGroup = await NuGetPackageHelper.DownloadPackageFilesAndFolder( - new[] { SplatLibrary, ReactiveuiLibrary }, + [SplatLibrary, ReactiveuiLibrary], targetFrameworks, packageOutputDirectory: null).ConfigureAwait(false); @@ -127,7 +127,11 @@ public void TestFail( var utility = new SourceGeneratorUtility(x => testOutput.WriteLine(x)); +#pragma warning disable IDE0053 // Use expression body for lambda expression +#pragma warning disable RCS1021 // Convert lambda expression body to expression body Assert.Throws(() => { RunGeneratorAndCheck(source); }); +#pragma warning restore RCS1021 // Convert lambda expression body to expression body +#pragma warning restore IDE0053 // Use expression body for lambda expression } /// @@ -183,7 +187,7 @@ public SettingsTask RunGeneratorAndCheck( .Concat(_eventCompiler.ReferencedModules.Select(x => MetadataReference.CreateFromFile(x.PEFile!.FileName))) .Concat(_eventCompiler.NeededModules.Select(x => MetadataReference.CreateFromFile(x.PEFile!.FileName)))); - var syntaxTree = CSharpSyntaxTree.ParseText(code, CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp12)); + var syntaxTree = CSharpSyntaxTree.ParseText(code, CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp13)); // Create a compilation with the provided source code. var compilation = CSharpCompilation.Create( diff --git a/src/ReactiveUI.SourceGenerator.Tests/UnitTests/ReactiveGeneratorTests.cs b/src/ReactiveUI.SourceGenerator.Tests/UnitTests/ReactiveGeneratorTests.cs index aafe582..c87c702 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/UnitTests/ReactiveGeneratorTests.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/UnitTests/ReactiveGeneratorTests.cs @@ -272,4 +272,31 @@ public partial class TestVM : ReactiveObject // Act: Initialize the helper and run the generator. Assert: Verify the generated code. return TestHelper.TestPass(sourceCode); } + + /// + /// Tests that the source generator correctly generates reactive properties. + /// + /// A task to monitor the async. + [Fact] + public Task FromReactivePartialProperties() + { + // 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] + public int Test1 { 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.Maui/ReactiveUI.SourceGenerators.Execute.Maui.csproj b/src/ReactiveUI.SourceGenerators.Execute.Maui/ReactiveUI.SourceGenerators.Execute.Maui.csproj index 82e6642..9e8cfd1 100644 --- a/src/ReactiveUI.SourceGenerators.Execute.Maui/ReactiveUI.SourceGenerators.Execute.Maui.csproj +++ b/src/ReactiveUI.SourceGenerators.Execute.Maui/ReactiveUI.SourceGenerators.Execute.Maui.csproj @@ -24,7 +24,7 @@ - + diff --git a/src/ReactiveUI.SourceGenerators.Execute.Nested1/ReactiveUI.SourceGenerators.Execute.Nested1.csproj b/src/ReactiveUI.SourceGenerators.Execute.Nested1/ReactiveUI.SourceGenerators.Execute.Nested1.csproj index 081b2ab..398d0c0 100644 --- a/src/ReactiveUI.SourceGenerators.Execute.Nested1/ReactiveUI.SourceGenerators.Execute.Nested1.csproj +++ b/src/ReactiveUI.SourceGenerators.Execute.Nested1/ReactiveUI.SourceGenerators.Execute.Nested1.csproj @@ -4,10 +4,12 @@ net8.0 enable enable + false + 13.0 - + diff --git a/src/ReactiveUI.SourceGenerators.Execute.Nested2/ReactiveUI.SourceGenerators.Execute.Nested2.csproj b/src/ReactiveUI.SourceGenerators.Execute.Nested2/ReactiveUI.SourceGenerators.Execute.Nested2.csproj index e4ab9f7..b72210e 100644 --- a/src/ReactiveUI.SourceGenerators.Execute.Nested2/ReactiveUI.SourceGenerators.Execute.Nested2.csproj +++ b/src/ReactiveUI.SourceGenerators.Execute.Nested2/ReactiveUI.SourceGenerators.Execute.Nested2.csproj @@ -4,10 +4,12 @@ net8.0 enable enable + false + 13.0 - + diff --git a/src/ReactiveUI.SourceGenerators.Execute.Nested3/ReactiveUI.SourceGenerators.Execute.Nested3.csproj b/src/ReactiveUI.SourceGenerators.Execute.Nested3/ReactiveUI.SourceGenerators.Execute.Nested3.csproj index 2cbe8c2..d1e5031 100644 --- a/src/ReactiveUI.SourceGenerators.Execute.Nested3/ReactiveUI.SourceGenerators.Execute.Nested3.csproj +++ b/src/ReactiveUI.SourceGenerators.Execute.Nested3/ReactiveUI.SourceGenerators.Execute.Nested3.csproj @@ -5,11 +5,11 @@ enable enable false - 12.0 + 13.0 - + diff --git a/src/ReactiveUI.SourceGenerators.Execute/ReactiveUI.SourceGenerators.Execute.csproj b/src/ReactiveUI.SourceGenerators.Execute/ReactiveUI.SourceGenerators.Execute.csproj index f1ac395..a03439f 100644 --- a/src/ReactiveUI.SourceGenerators.Execute/ReactiveUI.SourceGenerators.Execute.csproj +++ b/src/ReactiveUI.SourceGenerators.Execute/ReactiveUI.SourceGenerators.Execute.csproj @@ -8,7 +8,7 @@ true enable false - 12.0 + 13.0 A MVVM framework that integrates with the Reactive Extensions for .NET to create elegant, testable User Interfaces that run on any mobile or desktop platform. This is the Source Generators package for ReactiveUI @@ -17,7 +17,7 @@ - - + + diff --git a/src/ReactiveUI.SourceGenerators.Execute/TestViewModel.cs b/src/ReactiveUI.SourceGenerators.Execute/TestViewModel.cs index d893d97..2ed0103 100644 --- a/src/ReactiveUI.SourceGenerators.Execute/TestViewModel.cs +++ b/src/ReactiveUI.SourceGenerators.Execute/TestViewModel.cs @@ -28,7 +28,6 @@ public partial class TestViewModel : ReactiveObject, IActivatableViewModel, IDis private readonly IObservable _observable = Observable.Return(true); private readonly Subject _testSubject = new(); private readonly Subject _testNonNullSubject = new(); - private IScheduler _scheduler = RxApp.MainThreadScheduler; [property: JsonInclude] @@ -193,6 +192,15 @@ public TestViewModel() /// public static TestViewModel Instance { get; } = new(); + /// + /// Gets or sets the partial property test. + /// + /// + /// The partial property test. + /// + [Reactive] + public partial string? PartialPropertyTest { get; set; } + /// /// Gets the internal test property. Should not prompt to replace with INPC Reactive Property. /// diff --git a/src/ReactiveUI.SourceGenerators/AnalyzerReleases.Shipped.md b/src/ReactiveUI.SourceGenerators.Roslyn/AnalyzerReleases.Shipped.md similarity index 100% rename from src/ReactiveUI.SourceGenerators/AnalyzerReleases.Shipped.md rename to src/ReactiveUI.SourceGenerators.Roslyn/AnalyzerReleases.Shipped.md diff --git a/src/ReactiveUI.SourceGenerators/AnalyzerReleases.Unshipped.md b/src/ReactiveUI.SourceGenerators.Roslyn/AnalyzerReleases.Unshipped.md similarity index 100% rename from src/ReactiveUI.SourceGenerators/AnalyzerReleases.Unshipped.md rename to src/ReactiveUI.SourceGenerators.Roslyn/AnalyzerReleases.Unshipped.md diff --git a/src/ReactiveUI.SourceGenerators/AttributeDefinitions.cs b/src/ReactiveUI.SourceGenerators.Roslyn/AttributeDefinitions.cs similarity index 89% rename from src/ReactiveUI.SourceGenerators/AttributeDefinitions.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/AttributeDefinitions.cs index 11d65c6..bbea4ac 100644 --- a/src/ReactiveUI.SourceGenerators/AttributeDefinitions.cs +++ b/src/ReactiveUI.SourceGenerators.Roslyn/AttributeDefinitions.cs @@ -137,6 +137,50 @@ internal sealed class ReactiveCommandAttribute : Attribute /// /// The reactive attribute. /// +#if ROSYLN_412 + public static string ReactiveAttribute => $$""" +// Copyright (c) {{DateTime.Now.Year}} .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. + +using System; + +// +#pragma warning disable +#nullable enable +namespace ReactiveUI.SourceGenerators; + +/// +/// ReactiveAttribute. +/// +/// +[global::System.CodeDom.Compiler.GeneratedCode("{{ReactiveGenerator.GeneratorName}}", "{{ReactiveGenerator.GeneratorVersion}}")] +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] +internal sealed class ReactiveAttribute : Attribute +{ + /// + /// 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; } +} +#nullable restore +#pragma warning restore +"""; +#else public static string ReactiveAttribute => $$""" // Copyright (c) {{DateTime.Now.Year}} .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. @@ -179,6 +223,7 @@ internal sealed class ReactiveAttribute : Attribute #nullable restore #pragma warning restore """; +#endif public const string ObservableAsPropertyAttributeType = "ReactiveUI.SourceGenerators.ObservableAsPropertyAttribute"; diff --git a/src/ReactiveUI.SourceGenerators/Core/Extensions/AttributeDataExtensions.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/AttributeDataExtensions.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Core/Extensions/AttributeDataExtensions.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/AttributeDataExtensions.cs diff --git a/src/ReactiveUI.SourceGenerators/Core/Extensions/CompilationExtensions.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/CompilationExtensions.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Core/Extensions/CompilationExtensions.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/CompilationExtensions.cs diff --git a/src/ReactiveUI.SourceGenerators/Core/Extensions/DiagnosticsExtensions.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/DiagnosticsExtensions.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Core/Extensions/DiagnosticsExtensions.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/DiagnosticsExtensions.cs diff --git a/src/ReactiveUI.SourceGenerators/Core/Extensions/FieldSyntaxExtensions.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/FieldSyntaxExtensions.cs similarity index 63% rename from src/ReactiveUI.SourceGenerators/Core/Extensions/FieldSyntaxExtensions.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/FieldSyntaxExtensions.cs index cead10e..65eb662 100644 --- a/src/ReactiveUI.SourceGenerators/Core/Extensions/FieldSyntaxExtensions.cs +++ b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/FieldSyntaxExtensions.cs @@ -31,6 +31,13 @@ internal static string GetGeneratedPropertyName(this IFieldSymbol fieldSymbol) return $"{char.ToUpper(propertyName[0], CultureInfo.InvariantCulture)}{propertyName.Substring(1)}"; } + internal static string GetGeneratedFieldName(this IPropertySymbol propertySymbol) + { + var propertyName = propertySymbol.Name; + + return $"_{char.ToLower(propertyName[0], CultureInfo.InvariantCulture)}{propertyName.Substring(1)}"; + } + /// /// Gets the nullability info on the generated property. /// @@ -72,4 +79,39 @@ internal static void GetNullabilityInfo( fieldSymbol.Type.NullableAnnotation != NullableAnnotation.Annotated && semanticModel.Compilation.HasAccessibleTypeWithMetadataName("System.Diagnostics.CodeAnalysis.MemberNotNullAttribute"); } + + internal static void GetNullabilityInfo( + this IPropertySymbol propertySymbol, + SemanticModel semanticModel, + out bool isReferenceTypeOrUnconstraindTypeParameter, + out bool includeMemberNotNullOnSetAccessor) + { + // We're using IsValueType here and not IsReferenceType to also cover unconstrained type parameter cases. + // This will cover both reference types as well T when the constraints are not struct or unmanaged. + // If this is true, it means the field storage can potentially be in a null state (even if not annotated). + isReferenceTypeOrUnconstraindTypeParameter = !propertySymbol.Type.IsValueType; + + // This is used to avoid nullability warnings when setting the property from a constructor, in case the field + // was marked as not nullable. Nullability annotations are assumed to always be enabled to make the logic simpler. + // Consider this example: + // + // partial class MyViewModel : ReactiveObject + // { + // public MyViewModel() + // { + // Name = "Bob"; + // } + // + // [Reactive] + // private string _name; + // } + // + // The [MemberNotNull] attribute is needed on the setter for the generated Name property so that when Name + // is set, the compiler can determine that the name backing field is also being set (to a non null value). + // Of course, this can only be the case if the field type is also of a type that could be in a null state. + includeMemberNotNullOnSetAccessor = + isReferenceTypeOrUnconstraindTypeParameter && + propertySymbol.Type.NullableAnnotation != NullableAnnotation.Annotated && + semanticModel.Compilation.HasAccessibleTypeWithMetadataName("System.Diagnostics.CodeAnalysis.MemberNotNullAttribute"); + } } diff --git a/src/ReactiveUI.SourceGenerators/Core/Extensions/INamedTypeSymbolExtensions.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/INamedTypeSymbolExtensions.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Core/Extensions/INamedTypeSymbolExtensions.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/INamedTypeSymbolExtensions.cs diff --git a/src/ReactiveUI.SourceGenerators/Core/Extensions/ISymbolExtensions.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/ISymbolExtensions.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Core/Extensions/ISymbolExtensions.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/ISymbolExtensions.cs diff --git a/src/ReactiveUI.SourceGenerators/Core/Extensions/ITypeSymbolExtensions.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/ITypeSymbolExtensions.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Core/Extensions/ITypeSymbolExtensions.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/ITypeSymbolExtensions.cs diff --git a/src/ReactiveUI.SourceGenerators/Core/Extensions/SymbolInfoExtensions.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/SymbolInfoExtensions.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Core/Extensions/SymbolInfoExtensions.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/SymbolInfoExtensions.cs diff --git a/src/ReactiveUI.SourceGenerators/Core/Extensions/SyntaxTokenExtensions.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/SyntaxTokenExtensions.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Core/Extensions/SyntaxTokenExtensions.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/SyntaxTokenExtensions.cs diff --git a/src/ReactiveUI.SourceGenerators/Core/Extensions/SyntaxValueProviderExtensions.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/SyntaxValueProviderExtensions.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Core/Extensions/SyntaxValueProviderExtensions.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/SyntaxValueProviderExtensions.cs diff --git a/src/ReactiveUI.SourceGenerators/Core/Helpers/AttributeInfo.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Helpers/AttributeInfo.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Core/Helpers/AttributeInfo.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Core/Helpers/AttributeInfo.cs diff --git a/src/ReactiveUI.SourceGenerators/Core/Helpers/EquatableArray{T}.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Helpers/EquatableArray{T}.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Core/Helpers/EquatableArray{T}.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Core/Helpers/EquatableArray{T}.cs diff --git a/src/ReactiveUI.SourceGenerators/Core/Helpers/HashCode.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Helpers/HashCode.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Core/Helpers/HashCode.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Core/Helpers/HashCode.cs diff --git a/src/ReactiveUI.SourceGenerators/Core/Helpers/ImmutableArrayBuilder{T}.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Helpers/ImmutableArrayBuilder{T}.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Core/Helpers/ImmutableArrayBuilder{T}.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Core/Helpers/ImmutableArrayBuilder{T}.cs diff --git a/src/ReactiveUI.SourceGenerators/Core/Helpers/SymbolHelpers.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Helpers/SymbolHelpers.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Core/Helpers/SymbolHelpers.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Core/Helpers/SymbolHelpers.cs diff --git a/src/ReactiveUI.SourceGenerators/Core/Helpers/TypedConstantInfo.Factory.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Helpers/TypedConstantInfo.Factory.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Core/Helpers/TypedConstantInfo.Factory.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Core/Helpers/TypedConstantInfo.Factory.cs diff --git a/src/ReactiveUI.SourceGenerators/Core/Helpers/TypedConstantInfo.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Helpers/TypedConstantInfo.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Core/Helpers/TypedConstantInfo.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Core/Helpers/TypedConstantInfo.cs diff --git a/src/ReactiveUI.SourceGenerators/Core/Models/DiagnosticInfo.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Models/DiagnosticInfo.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Core/Models/DiagnosticInfo.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Core/Models/DiagnosticInfo.cs diff --git a/src/ReactiveUI.SourceGenerators/Core/Models/GenericGeneratorAttributeSyntaxContext.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Models/GenericGeneratorAttributeSyntaxContext.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Core/Models/GenericGeneratorAttributeSyntaxContext.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Core/Models/GenericGeneratorAttributeSyntaxContext.cs diff --git a/src/ReactiveUI.SourceGenerators/Core/Models/PropertyAttributeData.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Models/PropertyAttributeData.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Core/Models/PropertyAttributeData.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Core/Models/PropertyAttributeData.cs diff --git a/src/ReactiveUI.SourceGenerators/Core/Models/Result.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Models/Result.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Core/Models/Result.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Core/Models/Result.cs diff --git a/src/ReactiveUI.SourceGenerators/Core/Models/TargetInfo.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Core/Models/TargetInfo.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Core/Models/TargetInfo.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Core/Models/TargetInfo.cs diff --git a/src/ReactiveUI.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Diagnostics/DiagnosticDescriptors.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Diagnostics/DiagnosticDescriptors.cs diff --git a/src/ReactiveUI.SourceGenerators/Diagnostics/SuppressionDescriptors.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Diagnostics/SuppressionDescriptors.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Diagnostics/SuppressionDescriptors.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Diagnostics/SuppressionDescriptors.cs diff --git a/src/ReactiveUI.SourceGenerators/Diagnostics/Suppressions/OAPHMethodDoesNotNeedToBeStaticDiagnosticSuppressor.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Diagnostics/Suppressions/OAPHMethodDoesNotNeedToBeStaticDiagnosticSuppressor.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Diagnostics/Suppressions/OAPHMethodDoesNotNeedToBeStaticDiagnosticSuppressor.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Diagnostics/Suppressions/OAPHMethodDoesNotNeedToBeStaticDiagnosticSuppressor.cs diff --git a/src/ReactiveUI.SourceGenerators/Diagnostics/Suppressions/ObservableAsPropertyAttributeWithFieldNeverReadDiagnosticSuppressor.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Diagnostics/Suppressions/ObservableAsPropertyAttributeWithFieldNeverReadDiagnosticSuppressor.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Diagnostics/Suppressions/ObservableAsPropertyAttributeWithFieldNeverReadDiagnosticSuppressor.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Diagnostics/Suppressions/ObservableAsPropertyAttributeWithFieldNeverReadDiagnosticSuppressor.cs diff --git a/src/ReactiveUI.SourceGenerators/Diagnostics/Suppressions/ReactiveAttributeWithPropertyTargetDiagnosticSuppressor.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Diagnostics/Suppressions/ReactiveAttributeWithPropertyTargetDiagnosticSuppressor.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Diagnostics/Suppressions/ReactiveAttributeWithPropertyTargetDiagnosticSuppressor.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Diagnostics/Suppressions/ReactiveAttributeWithPropertyTargetDiagnosticSuppressor.cs diff --git a/src/ReactiveUI.SourceGenerators/Diagnostics/Suppressions/ReactiveCommandAttributeWithFieldOrPropertyTargetDiagnosticSuppressor.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Diagnostics/Suppressions/ReactiveCommandAttributeWithFieldOrPropertyTargetDiagnosticSuppressor.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Diagnostics/Suppressions/ReactiveCommandAttributeWithFieldOrPropertyTargetDiagnosticSuppressor.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Diagnostics/Suppressions/ReactiveCommandAttributeWithFieldOrPropertyTargetDiagnosticSuppressor.cs diff --git a/src/ReactiveUI.SourceGenerators/Diagnostics/Suppressions/ReactiveCommandMethodDoesNotNeedToBeStaticDiagnosticSuppressor.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Diagnostics/Suppressions/ReactiveCommandMethodDoesNotNeedToBeStaticDiagnosticSuppressor.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Diagnostics/Suppressions/ReactiveCommandMethodDoesNotNeedToBeStaticDiagnosticSuppressor.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Diagnostics/Suppressions/ReactiveCommandMethodDoesNotNeedToBeStaticDiagnosticSuppressor.cs diff --git a/src/ReactiveUI.SourceGenerators/Diagnostics/Suppressions/ReactiveFieldDoesNotNeedToBeReadOnlyDiagnosticSuppressor.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Diagnostics/Suppressions/ReactiveFieldDoesNotNeedToBeReadOnlyDiagnosticSuppressor.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Diagnostics/Suppressions/ReactiveFieldDoesNotNeedToBeReadOnlyDiagnosticSuppressor.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Diagnostics/Suppressions/ReactiveFieldDoesNotNeedToBeReadOnlyDiagnosticSuppressor.cs diff --git a/src/ReactiveUI.SourceGenerators/Generated/PolySharp.SourceGenerators/PolySharp.SourceGenerators.PolyfillsGenerator/System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute.g.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Generated/PolySharp.SourceGenerators/PolySharp.SourceGenerators.PolyfillsGenerator/System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute.g.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Generated/PolySharp.SourceGenerators/PolySharp.SourceGenerators.PolyfillsGenerator/System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute.g.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Generated/PolySharp.SourceGenerators/PolySharp.SourceGenerators.PolyfillsGenerator/System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute.g.cs diff --git a/src/ReactiveUI.SourceGenerators/Generated/PolySharp.SourceGenerators/PolySharp.SourceGenerators.PolyfillsGenerator/System.Diagnostics.CodeAnalysis.NotNullWhenAttribute.g.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Generated/PolySharp.SourceGenerators/PolySharp.SourceGenerators.PolyfillsGenerator/System.Diagnostics.CodeAnalysis.NotNullWhenAttribute.g.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Generated/PolySharp.SourceGenerators/PolySharp.SourceGenerators.PolyfillsGenerator/System.Diagnostics.CodeAnalysis.NotNullWhenAttribute.g.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Generated/PolySharp.SourceGenerators/PolySharp.SourceGenerators.PolyfillsGenerator/System.Diagnostics.CodeAnalysis.NotNullWhenAttribute.g.cs diff --git a/src/ReactiveUI.SourceGenerators/Generated/PolySharp.SourceGenerators/PolySharp.SourceGenerators.PolyfillsGenerator/System.Diagnostics.CodeAnalysis.UnscopedRefAttribute.g.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Generated/PolySharp.SourceGenerators/PolySharp.SourceGenerators.PolyfillsGenerator/System.Diagnostics.CodeAnalysis.UnscopedRefAttribute.g.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Generated/PolySharp.SourceGenerators/PolySharp.SourceGenerators.PolyfillsGenerator/System.Diagnostics.CodeAnalysis.UnscopedRefAttribute.g.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Generated/PolySharp.SourceGenerators/PolySharp.SourceGenerators.PolyfillsGenerator/System.Diagnostics.CodeAnalysis.UnscopedRefAttribute.g.cs diff --git a/src/ReactiveUI.SourceGenerators/Generated/PolySharp.SourceGenerators/PolySharp.SourceGenerators.PolyfillsGenerator/System.Index.g.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Generated/PolySharp.SourceGenerators/PolySharp.SourceGenerators.PolyfillsGenerator/System.Index.g.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Generated/PolySharp.SourceGenerators/PolySharp.SourceGenerators.PolyfillsGenerator/System.Index.g.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Generated/PolySharp.SourceGenerators/PolySharp.SourceGenerators.PolyfillsGenerator/System.Index.g.cs diff --git a/src/ReactiveUI.SourceGenerators/Generated/PolySharp.SourceGenerators/PolySharp.SourceGenerators.PolyfillsGenerator/System.Range.g.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Generated/PolySharp.SourceGenerators/PolySharp.SourceGenerators.PolyfillsGenerator/System.Range.g.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Generated/PolySharp.SourceGenerators/PolySharp.SourceGenerators.PolyfillsGenerator/System.Range.g.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Generated/PolySharp.SourceGenerators/PolySharp.SourceGenerators.PolyfillsGenerator/System.Range.g.cs diff --git a/src/ReactiveUI.SourceGenerators/Generated/PolySharp.SourceGenerators/PolySharp.SourceGenerators.PolyfillsGenerator/System.Runtime.CompilerServices.IsExternalInit.g.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Generated/PolySharp.SourceGenerators/PolySharp.SourceGenerators.PolyfillsGenerator/System.Runtime.CompilerServices.IsExternalInit.g.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/Generated/PolySharp.SourceGenerators/PolySharp.SourceGenerators.PolyfillsGenerator/System.Runtime.CompilerServices.IsExternalInit.g.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Generated/PolySharp.SourceGenerators/PolySharp.SourceGenerators.PolyfillsGenerator/System.Runtime.CompilerServices.IsExternalInit.g.cs diff --git a/src/ReactiveUI.SourceGenerators/IViewFor/IViewForGenerator.Execute.cs b/src/ReactiveUI.SourceGenerators.Roslyn/IViewFor/IViewForGenerator.Execute.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/IViewFor/IViewForGenerator.Execute.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/IViewFor/IViewForGenerator.Execute.cs diff --git a/src/ReactiveUI.SourceGenerators/IViewFor/IViewForGenerator.cs b/src/ReactiveUI.SourceGenerators.Roslyn/IViewFor/IViewForGenerator.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/IViewFor/IViewForGenerator.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/IViewFor/IViewForGenerator.cs diff --git a/src/ReactiveUI.SourceGenerators/IViewFor/Models/IViewForBaseType.cs b/src/ReactiveUI.SourceGenerators.Roslyn/IViewFor/Models/IViewForBaseType.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/IViewFor/Models/IViewForBaseType.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/IViewFor/Models/IViewForBaseType.cs diff --git a/src/ReactiveUI.SourceGenerators/IViewFor/Models/IViewForInfo.cs b/src/ReactiveUI.SourceGenerators.Roslyn/IViewFor/Models/IViewForInfo.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/IViewFor/Models/IViewForInfo.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/IViewFor/Models/IViewForInfo.cs diff --git a/src/ReactiveUI.SourceGenerators/ObservableAsProperty/Models/ObservableFieldInfo.cs b/src/ReactiveUI.SourceGenerators.Roslyn/ObservableAsProperty/Models/ObservableFieldInfo.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/ObservableAsProperty/Models/ObservableFieldInfo.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/ObservableAsProperty/Models/ObservableFieldInfo.cs diff --git a/src/ReactiveUI.SourceGenerators/ObservableAsProperty/Models/ObservableMethodInfo.cs b/src/ReactiveUI.SourceGenerators.Roslyn/ObservableAsProperty/Models/ObservableMethodInfo.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/ObservableAsProperty/Models/ObservableMethodInfo.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/ObservableAsProperty/Models/ObservableMethodInfo.cs diff --git a/src/ReactiveUI.SourceGenerators/ObservableAsProperty/ObservableAsPropertyGenerator.cs b/src/ReactiveUI.SourceGenerators.Roslyn/ObservableAsProperty/ObservableAsPropertyGenerator.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/ObservableAsProperty/ObservableAsPropertyGenerator.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/ObservableAsProperty/ObservableAsPropertyGenerator.cs diff --git a/src/ReactiveUI.SourceGenerators/ObservableAsProperty/ObservableAsPropertyGenerator{FromField}.Execute.cs b/src/ReactiveUI.SourceGenerators.Roslyn/ObservableAsProperty/ObservableAsPropertyGenerator{FromField}.Execute.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/ObservableAsProperty/ObservableAsPropertyGenerator{FromField}.Execute.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/ObservableAsProperty/ObservableAsPropertyGenerator{FromField}.Execute.cs diff --git a/src/ReactiveUI.SourceGenerators/ObservableAsProperty/ObservableAsPropertyGenerator{FromField}.cs b/src/ReactiveUI.SourceGenerators.Roslyn/ObservableAsProperty/ObservableAsPropertyGenerator{FromField}.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/ObservableAsProperty/ObservableAsPropertyGenerator{FromField}.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/ObservableAsProperty/ObservableAsPropertyGenerator{FromField}.cs diff --git a/src/ReactiveUI.SourceGenerators/ObservableAsProperty/ObservableAsPropertyGenerator{FromObservable}.Execute.cs b/src/ReactiveUI.SourceGenerators.Roslyn/ObservableAsProperty/ObservableAsPropertyGenerator{FromObservable}.Execute.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/ObservableAsProperty/ObservableAsPropertyGenerator{FromObservable}.Execute.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/ObservableAsProperty/ObservableAsPropertyGenerator{FromObservable}.Execute.cs diff --git a/src/ReactiveUI.SourceGenerators/ObservableAsProperty/ObservableAsPropertyGenerator{FromObservable}.cs b/src/ReactiveUI.SourceGenerators.Roslyn/ObservableAsProperty/ObservableAsPropertyGenerator{FromObservable}.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/ObservableAsProperty/ObservableAsPropertyGenerator{FromObservable}.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/ObservableAsProperty/ObservableAsPropertyGenerator{FromObservable}.cs diff --git a/src/ReactiveUI.SourceGenerators/Reactive/Models/PropertyInfo.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Reactive/Models/PropertyInfo.cs similarity index 94% rename from src/ReactiveUI.SourceGenerators/Reactive/Models/PropertyInfo.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Reactive/Models/PropertyInfo.cs index 59ca939..2c9f085 100644 --- a/src/ReactiveUI.SourceGenerators/Reactive/Models/PropertyInfo.cs +++ b/src/ReactiveUI.SourceGenerators.Roslyn/Reactive/Models/PropertyInfo.cs @@ -21,4 +21,5 @@ internal sealed record PropertyInfo( EquatableArray ForwardedAttributes, string AccessModifier, string Inheritance, - string UseRequired); + string UseRequired, + bool IsProperty); diff --git a/src/ReactiveUI.SourceGenerators/Reactive/ReactiveGenerator.Execute.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Reactive/ReactiveGenerator.Execute.cs similarity index 78% rename from src/ReactiveUI.SourceGenerators/Reactive/ReactiveGenerator.Execute.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Reactive/ReactiveGenerator.Execute.cs index ab07e43..d64e46a 100644 --- a/src/ReactiveUI.SourceGenerators/Reactive/ReactiveGenerator.Execute.cs +++ b/src/ReactiveUI.SourceGenerators.Roslyn/Reactive/ReactiveGenerator.Execute.cs @@ -4,7 +4,6 @@ // See the LICENSE file in the project root for full license information. using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; @@ -28,6 +27,88 @@ public sealed partial class ReactiveGenerator internal static readonly string GeneratorName = typeof(ReactiveGenerator).FullName!; internal static readonly string GeneratorVersion = typeof(ReactiveGenerator).Assembly.GetName().Version.ToString(); +#if ROSYLN_412 + private static Result? GetPropertyInfo(in GeneratorAttributeSyntaxContext context, CancellationToken token) + { + using var builder = ImmutableArrayBuilder.Rent(); + var symbol = context.TargetSymbol; + + if (!symbol.TryGetAttributeWithFullyQualifiedMetadataName(AttributeDefinitions.ReactiveAttributeType, out var attributeData)) + { + return default; + } + + if (symbol is not IPropertySymbol propertySymbol) + { + return default; + } + + if (!propertySymbol.IsPartialDefinition || propertySymbol.IsStatic) + { + return default; + } + + if (!IsTargetTypeValid(propertySymbol)) + { + builder.Add( + InvalidReactiveError, + propertySymbol, + propertySymbol.ContainingType, + propertySymbol.Name); + return new(default, builder.ToImmutable()); + } + + token.ThrowIfCancellationRequested(); + + var accessModifier = $"{propertySymbol.SetMethod?.DeclaredAccessibility} set".ToLower(); + if (accessModifier.StartsWith("public", StringComparison.Ordinal)) + { + accessModifier = "set"; + } + else if (accessModifier.Contains("and")) + { + accessModifier = accessModifier.Replace("and", " "); + } + + token.ThrowIfCancellationRequested(); + + var inheritance = propertySymbol.IsVirtual ? " virtual" : propertySymbol.IsOverride ? " override" : string.Empty; + var useRequired = string.Empty; + var typeNameWithNullabilityAnnotations = propertySymbol.Type.GetFullyQualifiedNameWithNullabilityAnnotations(); + var fieldName = propertySymbol.GetGeneratedFieldName(); + var propertyName = propertySymbol.Name; + + // Get the nullability info for the property + propertySymbol.GetNullabilityInfo( + context.SemanticModel, + out var isReferenceTypeOrUnconstraindTypeParameter, + out var includeMemberNotNullOnSetAccessor); + + ImmutableArray forwardedAttributesString = []; + token.ThrowIfCancellationRequested(); + + // Get the containing type info + var targetInfo = TargetInfo.From(propertySymbol.ContainingType); + + token.ThrowIfCancellationRequested(); + + return new( + new( + targetInfo, + typeNameWithNullabilityAnnotations, + fieldName, + propertyName, + isReferenceTypeOrUnconstraindTypeParameter, + includeMemberNotNullOnSetAccessor, + forwardedAttributesString, + accessModifier, + inheritance, + useRequired, + true), + builder.ToImmutable()); + } +#endif + /// /// Gets the observable method information. /// @@ -229,7 +310,8 @@ public sealed partial class ReactiveGenerator forwardedAttributesString, accessModifier, inheritance, - useRequired), + useRequired, + false), builder.ToImmutable()); } @@ -305,15 +387,23 @@ private static string GetPropertySyntax(PropertyInfo propertyInfo) return string.Empty; } + var fieldSyntax = string.Empty; + var partialModifier = propertyInfo.IsProperty ? "partial " : string.Empty; + if (propertyInfo.IsProperty) + { + fieldSyntax = $"private {propertyInfo.TypeNameWithNullabilityAnnotations} {propertyInfo.FieldName};"; + } + var propertyAttributes = string.Join("\n ", AttributeDefinitions.ExcludeFromCodeCoverage.Concat(propertyInfo.ForwardedAttributes)); - if (propertyInfo.IncludeMemberNotNullOnSetAccessor) + if (propertyInfo.IncludeMemberNotNullOnSetAccessor || propertyInfo.IsReferenceTypeOrUnconstrainedTypeParameter) { return $$""" + {{fieldSyntax}} /// {{propertyAttributes}} - {{propertyInfo.TargetInfo.TargetVisibility}}{{propertyInfo.Inheritance}} {{propertyInfo.UseRequired}}{{propertyInfo.TypeNameWithNullabilityAnnotations}} {{propertyInfo.PropertyName}} + {{propertyInfo.TargetInfo.TargetVisibility}}{{propertyInfo.Inheritance}} {{partialModifier}}{{propertyInfo.UseRequired}}{{propertyInfo.TypeNameWithNullabilityAnnotations}} {{propertyInfo.PropertyName}} { get => {{propertyInfo.FieldName}}; [global::System.Diagnostics.CodeAnalysis.MemberNotNull("{{propertyInfo.FieldName}}")] @@ -324,9 +414,10 @@ private static string GetPropertySyntax(PropertyInfo propertyInfo) return $$""" + {{fieldSyntax}} /// {{propertyAttributes}} - {{propertyInfo.TargetInfo.TargetVisibility}}{{propertyInfo.Inheritance}} {{propertyInfo.UseRequired}}{{propertyInfo.TypeNameWithNullabilityAnnotations}} {{propertyInfo.PropertyName}} { get => {{propertyInfo.FieldName}}; {{propertyInfo.AccessModifier}} => this.RaiseAndSetIfChanged(ref {{propertyInfo.FieldName}}, value); } + {{propertyInfo.TargetInfo.TargetVisibility}}{{propertyInfo.Inheritance}} {{partialModifier}}{{propertyInfo.UseRequired}}{{propertyInfo.TypeNameWithNullabilityAnnotations}} {{propertyInfo.PropertyName}} { get => {{propertyInfo.FieldName}}; {{propertyInfo.AccessModifier}} => this.RaiseAndSetIfChanged(ref {{propertyInfo.FieldName}}, value); } """; } @@ -343,4 +434,13 @@ private static bool IsTargetTypeValid(IFieldSymbol fieldSymbol) return isIObservableObject || isObservableObject || hasObservableObjectAttribute; } + + private static bool IsTargetTypeValid(IPropertySymbol propertySymbol) + { + var isObservableObject = propertySymbol.ContainingType.InheritsFromFullyQualifiedMetadataName("ReactiveUI.ReactiveObject"); + var isIObservableObject = propertySymbol.ContainingType.InheritsFromFullyQualifiedMetadataName("ReactiveUI.IReactiveObject"); + var hasObservableObjectAttribute = propertySymbol.ContainingType.HasOrInheritsAttributeWithFullyQualifiedMetadataName("ReactiveUI.SourceGenerators.ReactiveObjectAttribute"); + + return isIObservableObject || isObservableObject || hasObservableObjectAttribute; + } } diff --git a/src/ReactiveUI.SourceGenerators/Reactive/ReactiveGenerator.cs b/src/ReactiveUI.SourceGenerators.Roslyn/Reactive/ReactiveGenerator.cs similarity index 59% rename from src/ReactiveUI.SourceGenerators/Reactive/ReactiveGenerator.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/Reactive/ReactiveGenerator.cs index 313cc19..baeca84 100644 --- a/src/ReactiveUI.SourceGenerators/Reactive/ReactiveGenerator.cs +++ b/src/ReactiveUI.SourceGenerators.Roslyn/Reactive/ReactiveGenerator.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; -using ReactiveUI.SourceGenerators.Extensions; using ReactiveUI.SourceGenerators.Helpers; namespace ReactiveUI.SourceGenerators; @@ -32,6 +31,14 @@ public void Initialize(IncrementalGeneratorInitializationContext context) ctx.AddSource($"{AttributeDefinitions.ReactiveAttributeType}.g.cs", SourceText.From(AttributeDefinitions.ReactiveAttribute, Encoding.UTF8)); }); + RunReactiveFromField(context); +#if ROSYLN_412 + RunReactiveFromProperty(context); +#endif + } + + private static void RunReactiveFromField(IncrementalGeneratorInitializationContext context) + { // Gather info for all annotated variable with at least one attribute. var propertyInfo = context.SyntaxProvider @@ -79,4 +86,56 @@ public void Initialize(IncrementalGeneratorInitializationContext context) } }); } + +#if ROSYLN_412 + private static void RunReactiveFromProperty(IncrementalGeneratorInitializationContext context) + { + // Gather info for all annotated variable with at least one attribute. + var propertyInfo = + context.SyntaxProvider + .ForAttributeWithMetadataName( + AttributeDefinitions.ReactiveAttributeType, + static (node, _) => node is PropertyDeclarationSyntax { AccessorList.Accessors: { Count: 2 } accessors, AttributeLists.Count: > 0 }, + static (context, token) => GetPropertyInfo(context, token)) + .Where(x => x != null) + .Select((x, _) => x!) + .Collect(); + + // Generate the requested properties + context.RegisterSourceOutput(propertyInfo, static (context, input) => + { + foreach (var diagnostic in input.SelectMany(static x => x.Errors)) + { + // Output the diagnostics + context.ReportDiagnostic(diagnostic.ToDiagnostic()); + } + + // Gather all the properties that are valid and group them by the target information. + var groupedPropertyInfo = input + .Where(static x => x.Value != null) + .Select(static x => x.Value!).GroupBy( + static info => (info.TargetInfo.FileHintName, info.TargetInfo.TargetName, info.TargetInfo.TargetNamespace, info.TargetInfo.TargetVisibility, info.TargetInfo.TargetType), + static info => info) + .ToImmutableArray(); + + if (groupedPropertyInfo.Length == 0) + { + return; + } + + foreach (var grouping in groupedPropertyInfo) + { + var items = grouping.ToImmutableArray(); + + if (items.Length == 0) + { + continue; + } + + var source = GenerateSource(grouping.Key.TargetName, grouping.Key.TargetNamespace, grouping.Key.TargetVisibility, grouping.Key.TargetType, [.. grouping]); + context.AddSource($"{grouping.Key.FileHintName}.PartialProperties.g.cs", source); + } + }); + } +#endif } diff --git a/src/ReactiveUI.SourceGenerators/ReactiveCommand/Models/CanExecuteTypeInfo.cs b/src/ReactiveUI.SourceGenerators.Roslyn/ReactiveCommand/Models/CanExecuteTypeInfo.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/ReactiveCommand/Models/CanExecuteTypeInfo.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/ReactiveCommand/Models/CanExecuteTypeInfo.cs diff --git a/src/ReactiveUI.SourceGenerators/ReactiveCommand/Models/CommandInfo.cs b/src/ReactiveUI.SourceGenerators.Roslyn/ReactiveCommand/Models/CommandInfo.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/ReactiveCommand/Models/CommandInfo.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/ReactiveCommand/Models/CommandInfo.cs diff --git a/src/ReactiveUI.SourceGenerators/ReactiveCommand/ReactiveCommandGenerator.Execute.cs b/src/ReactiveUI.SourceGenerators.Roslyn/ReactiveCommand/ReactiveCommandGenerator.Execute.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/ReactiveCommand/ReactiveCommandGenerator.Execute.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/ReactiveCommand/ReactiveCommandGenerator.Execute.cs diff --git a/src/ReactiveUI.SourceGenerators/ReactiveCommand/ReactiveCommandGenerator.cs b/src/ReactiveUI.SourceGenerators.Roslyn/ReactiveCommand/ReactiveCommandGenerator.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/ReactiveCommand/ReactiveCommandGenerator.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/ReactiveCommand/ReactiveCommandGenerator.cs diff --git a/src/ReactiveUI.SourceGenerators.Roslyn/ReactiveUI.SourceGenerators.Roslyn.projitems b/src/ReactiveUI.SourceGenerators.Roslyn/ReactiveUI.SourceGenerators.Roslyn.projitems new file mode 100644 index 0000000..b4fc3dc --- /dev/null +++ b/src/ReactiveUI.SourceGenerators.Roslyn/ReactiveUI.SourceGenerators.Roslyn.projitems @@ -0,0 +1,78 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + bc460daf-92ed-41d9-9eb4-2c92121d62a8 + + + ReactiveUI.SourceGenerators.Roslyn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerators.Roslyn/ReactiveUI.SourceGenerators.Roslyn.shproj b/src/ReactiveUI.SourceGenerators.Roslyn/ReactiveUI.SourceGenerators.Roslyn.shproj new file mode 100644 index 0000000..6870b61 --- /dev/null +++ b/src/ReactiveUI.SourceGenerators.Roslyn/ReactiveUI.SourceGenerators.Roslyn.shproj @@ -0,0 +1,13 @@ + + + + bc460daf-92ed-41d9-9eb4-2c92121d62a8 + 14.0 + + + + + + + + diff --git a/src/ReactiveUI.SourceGenerators/RoutedControlHost/Models/RoutedControlHostInfo.cs b/src/ReactiveUI.SourceGenerators.Roslyn/RoutedControlHost/Models/RoutedControlHostInfo.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/RoutedControlHost/Models/RoutedControlHostInfo.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/RoutedControlHost/Models/RoutedControlHostInfo.cs diff --git a/src/ReactiveUI.SourceGenerators/RoutedControlHost/RoutedControlHostGenerator.Execute.cs b/src/ReactiveUI.SourceGenerators.Roslyn/RoutedControlHost/RoutedControlHostGenerator.Execute.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/RoutedControlHost/RoutedControlHostGenerator.Execute.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/RoutedControlHost/RoutedControlHostGenerator.Execute.cs diff --git a/src/ReactiveUI.SourceGenerators/RoutedControlHost/RoutedControlHostGenerator.cs b/src/ReactiveUI.SourceGenerators.Roslyn/RoutedControlHost/RoutedControlHostGenerator.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/RoutedControlHost/RoutedControlHostGenerator.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/RoutedControlHost/RoutedControlHostGenerator.cs diff --git a/src/ReactiveUI.SourceGenerators/ViewModelControlHost/Models/ViewModelControlHostInfo.cs b/src/ReactiveUI.SourceGenerators.Roslyn/ViewModelControlHost/Models/ViewModelControlHostInfo.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/ViewModelControlHost/Models/ViewModelControlHostInfo.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/ViewModelControlHost/Models/ViewModelControlHostInfo.cs diff --git a/src/ReactiveUI.SourceGenerators/ViewModelControlHost/ViewModelControlHostGenerator.Execute.cs b/src/ReactiveUI.SourceGenerators.Roslyn/ViewModelControlHost/ViewModelControlHostGenerator.Execute.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/ViewModelControlHost/ViewModelControlHostGenerator.Execute.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/ViewModelControlHost/ViewModelControlHostGenerator.Execute.cs diff --git a/src/ReactiveUI.SourceGenerators/ViewModelControlHost/ViewModelControlHostGenerator.cs b/src/ReactiveUI.SourceGenerators.Roslyn/ViewModelControlHost/ViewModelControlHostGenerator.cs similarity index 100% rename from src/ReactiveUI.SourceGenerators/ViewModelControlHost/ViewModelControlHostGenerator.cs rename to src/ReactiveUI.SourceGenerators.Roslyn/ViewModelControlHost/ViewModelControlHostGenerator.cs diff --git a/src/ReactiveUI.SourceGenerators.Roslyn4120/ReactiveUI.SourceGenerators.Roslyn4120.csproj b/src/ReactiveUI.SourceGenerators.Roslyn4120/ReactiveUI.SourceGenerators.Roslyn4120.csproj new file mode 100644 index 0000000..5ecb3c6 --- /dev/null +++ b/src/ReactiveUI.SourceGenerators.Roslyn4120/ReactiveUI.SourceGenerators.Roslyn4120.csproj @@ -0,0 +1,34 @@ + + + + netstandard2.0 + enable + latest + true + true + true + true + false + true + false + + A MVVM framework that integrates with the Reactive Extensions for .NET to create elegant, testable User Interfaces that run on any mobile or desktop platform. This is the Source Generators package for ReactiveUI + ReactiveUI.SourceGenerators + $(DefineConstants);ROSYLN_412 + + + + + + + + + + + + + + + + + diff --git a/src/ReactiveUI.SourceGenerators.Roslyn480/ReactiveUI.SourceGenerators.Roslyn480.csproj b/src/ReactiveUI.SourceGenerators.Roslyn480/ReactiveUI.SourceGenerators.Roslyn480.csproj new file mode 100644 index 0000000..0e6e508 --- /dev/null +++ b/src/ReactiveUI.SourceGenerators.Roslyn480/ReactiveUI.SourceGenerators.Roslyn480.csproj @@ -0,0 +1,29 @@ + + + + netstandard2.0 + enable + latest + true + true + true + true + false + true + false + + A MVVM framework that integrates with the Reactive Extensions for .NET to create elegant, testable User Interfaces that run on any mobile or desktop platform. This is the Source Generators package for ReactiveUI + ReactiveUI.SourceGenerators + + + + + + + + + + + + + diff --git a/src/ReactiveUI.SourceGenerators.sln b/src/ReactiveUI.SourceGenerators.sln index 762f6a7..7d6f42d 100644 --- a/src/ReactiveUI.SourceGenerators.sln +++ b/src/ReactiveUI.SourceGenerators.sln @@ -1,5 +1,5 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# 17 +# Visual Studio Version 17 VisualStudioVersion = 17.10.35027.167 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionConfig", "SolutionConfig", "{F29AF2F3-DEC8-58BC-043A-1447862C832D}" @@ -34,6 +34,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReactiveUI.SourceGenerators EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NestedTest", "NestedTest", "{CAFBD27B-5078-4A0C-A4E9-19DCF2A7DF16}" EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "ReactiveUI.SourceGenerators.Roslyn", "ReactiveUI.SourceGenerators.Roslyn\ReactiveUI.SourceGenerators.Roslyn.shproj", "{BC460DAF-92ED-41D9-9EB4-2C92121D62A8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReactiveUI.SourceGenerators.Roslyn480", "ReactiveUI.SourceGenerators.Roslyn480\ReactiveUI.SourceGenerators.Roslyn480.csproj", "{7C308972-5697-464F-BEAE-02FE9E03BECE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReactiveUI.SourceGenerators.Roslyn4120", "ReactiveUI.SourceGenerators.Roslyn4120\ReactiveUI.SourceGenerators.Roslyn4120.csproj", "{BF121262-7F30-4EC0-9F03-324AC3B834B8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -72,6 +78,14 @@ Global {B42F683D-91D8-4378-9DFE-EC55DB0FE43A}.Debug|Any CPU.Build.0 = Debug|Any CPU {B42F683D-91D8-4378-9DFE-EC55DB0FE43A}.Release|Any CPU.ActiveCfg = Release|Any CPU {B42F683D-91D8-4378-9DFE-EC55DB0FE43A}.Release|Any CPU.Build.0 = Release|Any CPU + {7C308972-5697-464F-BEAE-02FE9E03BECE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7C308972-5697-464F-BEAE-02FE9E03BECE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7C308972-5697-464F-BEAE-02FE9E03BECE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7C308972-5697-464F-BEAE-02FE9E03BECE}.Release|Any CPU.Build.0 = Release|Any CPU + {BF121262-7F30-4EC0-9F03-324AC3B834B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BF121262-7F30-4EC0-9F03-324AC3B834B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BF121262-7F30-4EC0-9F03-324AC3B834B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BF121262-7F30-4EC0-9F03-324AC3B834B8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -87,4 +101,9 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {173F891B-86A2-4226-B563-A7318CE0E2EC} EndGlobalSection + GlobalSection(SharedMSBuildProjectFiles) = preSolution + ReactiveUI.SourceGenerators.Roslyn\ReactiveUI.SourceGenerators.Roslyn.projitems*{7c308972-5697-464f-beae-02fe9e03bece}*SharedItemsImports = 5 + ReactiveUI.SourceGenerators.Roslyn\ReactiveUI.SourceGenerators.Roslyn.projitems*{bc460daf-92ed-41d9-9eb4-2c92121d62a8}*SharedItemsImports = 13 + ReactiveUI.SourceGenerators.Roslyn\ReactiveUI.SourceGenerators.Roslyn.projitems*{bf121262-7f30-4ec0-9f03-324ac3b834b8}*SharedItemsImports = 5 + EndGlobalSection EndGlobal diff --git a/src/ReactiveUI.SourceGenerators/ReactiveUI.SourceGenerators.csproj b/src/ReactiveUI.SourceGenerators/ReactiveUI.SourceGenerators.csproj index 282a61d..a18702e 100644 --- a/src/ReactiveUI.SourceGenerators/ReactiveUI.SourceGenerators.csproj +++ b/src/ReactiveUI.SourceGenerators/ReactiveUI.SourceGenerators.csproj @@ -12,9 +12,10 @@ true A MVVM framework that integrates with the Reactive Extensions for .NET to create elegant, testable User Interfaces that run on any mobile or desktop platform. This is the Source Generators package for ReactiveUI + - Generated - + - System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute; + + + + + True - - + @@ -47,15 +51,18 @@ + + - + + - +