From 0da9565d2d0d1a3186b2758fb0315e843ff37c23 Mon Sep 17 00:00:00 2001 From: Chris Pulman Date: Sat, 4 Apr 2026 12:28:17 +0100 Subject: [PATCH 1/2] Support AccessModifier for BindableDerivedList Add an AccessModifier property to the BindableDerivedListAttribute and propagate it through the generator so generated properties use the requested access level. The generator now reads the AccessModifier named argument, maps enum values to C# access keywords, and includes the access modifier in BindableDerivedListInfo and the generated property template. Tests were added/updated to cover internal-class behavior and all supported access modifiers, and corresponding generated test files were added/updated. --- ...BindableDerivedListAttribute.g.verified.cs | 11 +++- ...BindableDerivedListAttribute.g.verified.cs | 11 +++- ...BindableDerivedListAttribute.g.verified.cs | 11 +++- ...BindableDerivedListAttribute.g.verified.cs | 13 +++- ...BindableDerivedListAttribute.g.verified.cs | 11 +++- ...BindableDerivedListAttribute.g.verified.cs | 13 +++- ...BindableDerivedListAttribute.g.verified.cs | 11 +++- ...BindableDerivedListAttribute.g.verified.cs | 13 +++- ...BindableDerivedListAttribute.g.verified.cs | 28 +++++++++ ...iewModel.BindableDerivedList.g.verified.cs | 21 +++++++ ...BindableDerivedListAttribute.g.verified.cs | 11 +++- ...BindableDerivedListAttribute.g.verified.cs | 11 +++- ...BindableDerivedListAttribute.g.verified.cs | 11 +++- ...BindableDerivedListAttribute.g.verified.cs | 11 +++- ...BindableDerivedListAttribute.g.verified.cs | 13 +++- ...BindableDerivedListAttribute.g.verified.cs | 11 +++- ...BindableDerivedListAttribute.g.verified.cs | 11 +++- .../UnitTests/DerivedListExtTests.cs | 62 +++++++++++++++++++ .../AttributeDefinitions.cs | 11 +++- .../BindableDerivedListGenerator.Execute.cs | 18 +++++- .../Models/BindableDerivedListInfo.cs | 3 +- 21 files changed, 293 insertions(+), 23 deletions(-) create mode 100644 src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.InternalClassGeneratesPublicProperty#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs create mode 100644 src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.InternalClassGeneratesPublicProperty#TestNs.ViewModel.BindableDerivedList.g.verified.cs diff --git a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/BindableDerivedListGeneratorTests.FromReactiveProperties#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/BindableDerivedListGeneratorTests.FromReactiveProperties#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs index 105caa6..f529f0c 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/BindableDerivedListGeneratorTests.FromReactiveProperties#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/BindableDerivedListGeneratorTests.FromReactiveProperties#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs @@ -14,6 +14,15 @@ namespace ReactiveUI.SourceGenerators; /// /// [global::System.AttributeUsage(global::System.AttributeTargets.Field, AllowMultiple = false, Inherited = false)] -internal sealed class BindableDerivedListAttribute : global::System.Attribute; +internal sealed class BindableDerivedListAttribute : global::System.Attribute +{ + /// + /// Gets the AccessModifier of the BindableDerivedList property. + /// + /// + /// The AccessModifier of the property. + /// + public PropertyAccessModifier AccessModifier { get; init; } +} #nullable restore #pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.ComplexGeneric#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.ComplexGeneric#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs index 105caa6..f529f0c 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.ComplexGeneric#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.ComplexGeneric#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs @@ -14,6 +14,15 @@ namespace ReactiveUI.SourceGenerators; /// /// [global::System.AttributeUsage(global::System.AttributeTargets.Field, AllowMultiple = false, Inherited = false)] -internal sealed class BindableDerivedListAttribute : global::System.Attribute; +internal sealed class BindableDerivedListAttribute : global::System.Attribute +{ + /// + /// Gets the AccessModifier of the BindableDerivedList property. + /// + /// + /// The AccessModifier of the property. + /// + public PropertyAccessModifier AccessModifier { get; init; } +} #nullable restore #pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.DateTimeType#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.DateTimeType#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs index 105caa6..f529f0c 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.DateTimeType#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.DateTimeType#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs @@ -14,6 +14,15 @@ namespace ReactiveUI.SourceGenerators; /// /// [global::System.AttributeUsage(global::System.AttributeTargets.Field, AllowMultiple = false, Inherited = false)] -internal sealed class BindableDerivedListAttribute : global::System.Attribute; +internal sealed class BindableDerivedListAttribute : global::System.Attribute +{ + /// + /// Gets the AccessModifier of the BindableDerivedList property. + /// + /// + /// The AccessModifier of the property. + /// + public PropertyAccessModifier AccessModifier { get; init; } +} #nullable restore #pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.DiffNamespaces#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.DiffNamespaces#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs index 105caa6..07ca7a2 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.DiffNamespaces#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.DiffNamespaces#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs @@ -14,6 +14,15 @@ namespace ReactiveUI.SourceGenerators; /// /// [global::System.AttributeUsage(global::System.AttributeTargets.Field, AllowMultiple = false, Inherited = false)] -internal sealed class BindableDerivedListAttribute : global::System.Attribute; +internal sealed class BindableDerivedListAttribute : global::System.Attribute +{ + /// + /// Gets the AccessModifier of the BindableDerivedList property. + /// + /// + /// The AccessModifier of the property. + /// + public PropertyAccessModifier AccessModifier { get; init; } +} #nullable restore -#pragma warning restore \ No newline at end of file +#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.EnumType#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.EnumType#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs index 105caa6..f529f0c 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.EnumType#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.EnumType#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs @@ -14,6 +14,15 @@ namespace ReactiveUI.SourceGenerators; /// /// [global::System.AttributeUsage(global::System.AttributeTargets.Field, AllowMultiple = false, Inherited = false)] -internal sealed class BindableDerivedListAttribute : global::System.Attribute; +internal sealed class BindableDerivedListAttribute : global::System.Attribute +{ + /// + /// Gets the AccessModifier of the BindableDerivedList property. + /// + /// + /// The AccessModifier of the property. + /// + public PropertyAccessModifier AccessModifier { get; init; } +} #nullable restore #pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.GenericClass#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.GenericClass#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs index 105caa6..07ca7a2 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.GenericClass#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.GenericClass#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs @@ -14,6 +14,15 @@ namespace ReactiveUI.SourceGenerators; /// /// [global::System.AttributeUsage(global::System.AttributeTargets.Field, AllowMultiple = false, Inherited = false)] -internal sealed class BindableDerivedListAttribute : global::System.Attribute; +internal sealed class BindableDerivedListAttribute : global::System.Attribute +{ + /// + /// Gets the AccessModifier of the BindableDerivedList property. + /// + /// + /// The AccessModifier of the property. + /// + public PropertyAccessModifier AccessModifier { get; init; } +} #nullable restore -#pragma warning restore \ No newline at end of file +#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.GuidType#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.GuidType#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs index 105caa6..f529f0c 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.GuidType#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.GuidType#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs @@ -14,6 +14,15 @@ namespace ReactiveUI.SourceGenerators; /// /// [global::System.AttributeUsage(global::System.AttributeTargets.Field, AllowMultiple = false, Inherited = false)] -internal sealed class BindableDerivedListAttribute : global::System.Attribute; +internal sealed class BindableDerivedListAttribute : global::System.Attribute +{ + /// + /// Gets the AccessModifier of the BindableDerivedList property. + /// + /// + /// The AccessModifier of the property. + /// + public PropertyAccessModifier AccessModifier { get; init; } +} #nullable restore #pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.InterfaceType#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.InterfaceType#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs index 105caa6..07ca7a2 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.InterfaceType#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.InterfaceType#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs @@ -14,6 +14,15 @@ namespace ReactiveUI.SourceGenerators; /// /// [global::System.AttributeUsage(global::System.AttributeTargets.Field, AllowMultiple = false, Inherited = false)] -internal sealed class BindableDerivedListAttribute : global::System.Attribute; +internal sealed class BindableDerivedListAttribute : global::System.Attribute +{ + /// + /// Gets the AccessModifier of the BindableDerivedList property. + /// + /// + /// The AccessModifier of the property. + /// + public PropertyAccessModifier AccessModifier { get; init; } +} #nullable restore -#pragma warning restore \ No newline at end of file +#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.InternalClassGeneratesPublicProperty#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.InternalClassGeneratesPublicProperty#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs new file mode 100644 index 0000000..f529f0c --- /dev/null +++ b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.InternalClassGeneratesPublicProperty#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs @@ -0,0 +1,28 @@ +//HintName: ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.cs +// Copyright (c) 2026 .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, AllowMultiple = false, Inherited = false)] +internal sealed class BindableDerivedListAttribute : global::System.Attribute +{ + /// + /// Gets the AccessModifier of the BindableDerivedList property. + /// + /// + /// The AccessModifier of the property. + /// + public PropertyAccessModifier AccessModifier { get; init; } +} +#nullable restore +#pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.InternalClassGeneratesPublicProperty#TestNs.ViewModel.BindableDerivedList.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.InternalClassGeneratesPublicProperty#TestNs.ViewModel.BindableDerivedList.g.verified.cs new file mode 100644 index 0000000..666ec0d --- /dev/null +++ b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.InternalClassGeneratesPublicProperty#TestNs.ViewModel.BindableDerivedList.g.verified.cs @@ -0,0 +1,21 @@ +//HintName: TestNs.ViewModel.BindableDerivedList.g.cs +// +using System.Collections.ObjectModel; +using DynamicData; +using ReactiveUI; + +#pragma warning disable +#nullable enable + +namespace TestNs +{ + + internal partial class ViewModel + { + /// + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + public global::System.Collections.ObjectModel.ReadOnlyObservableCollection? TheList => _theList; + } +} +#nullable restore +#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.MultipleLists#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.MultipleLists#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs index 105caa6..f529f0c 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.MultipleLists#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.MultipleLists#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs @@ -14,6 +14,15 @@ namespace ReactiveUI.SourceGenerators; /// /// [global::System.AttributeUsage(global::System.AttributeTargets.Field, AllowMultiple = false, Inherited = false)] -internal sealed class BindableDerivedListAttribute : global::System.Attribute; +internal sealed class BindableDerivedListAttribute : global::System.Attribute +{ + /// + /// Gets the AccessModifier of the BindableDerivedList property. + /// + /// + /// The AccessModifier of the property. + /// + public PropertyAccessModifier AccessModifier { get; init; } +} #nullable restore #pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.NestedClass#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.NestedClass#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs index 105caa6..f529f0c 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.NestedClass#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.NestedClass#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs @@ -14,6 +14,15 @@ namespace ReactiveUI.SourceGenerators; /// /// [global::System.AttributeUsage(global::System.AttributeTargets.Field, AllowMultiple = false, Inherited = false)] -internal sealed class BindableDerivedListAttribute : global::System.Attribute; +internal sealed class BindableDerivedListAttribute : global::System.Attribute +{ + /// + /// Gets the AccessModifier of the BindableDerivedList property. + /// + /// + /// The AccessModifier of the property. + /// + public PropertyAccessModifier AccessModifier { get; init; } +} #nullable restore #pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.NullableElements#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.NullableElements#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs index 105caa6..f529f0c 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.NullableElements#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.NullableElements#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs @@ -14,6 +14,15 @@ namespace ReactiveUI.SourceGenerators; /// /// [global::System.AttributeUsage(global::System.AttributeTargets.Field, AllowMultiple = false, Inherited = false)] -internal sealed class BindableDerivedListAttribute : global::System.Attribute; +internal sealed class BindableDerivedListAttribute : global::System.Attribute +{ + /// + /// Gets the AccessModifier of the BindableDerivedList property. + /// + /// + /// The AccessModifier of the property. + /// + public PropertyAccessModifier AccessModifier { get; init; } +} #nullable restore #pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.RecordClass#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.RecordClass#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs index 105caa6..f529f0c 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.RecordClass#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.RecordClass#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs @@ -14,6 +14,15 @@ namespace ReactiveUI.SourceGenerators; /// /// [global::System.AttributeUsage(global::System.AttributeTargets.Field, AllowMultiple = false, Inherited = false)] -internal sealed class BindableDerivedListAttribute : global::System.Attribute; +internal sealed class BindableDerivedListAttribute : global::System.Attribute +{ + /// + /// Gets the AccessModifier of the BindableDerivedList property. + /// + /// + /// The AccessModifier of the property. + /// + public PropertyAccessModifier AccessModifier { get; init; } +} #nullable restore #pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.StructType#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.StructType#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs index 105caa6..07ca7a2 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.StructType#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.StructType#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs @@ -14,6 +14,15 @@ namespace ReactiveUI.SourceGenerators; /// /// [global::System.AttributeUsage(global::System.AttributeTargets.Field, AllowMultiple = false, Inherited = false)] -internal sealed class BindableDerivedListAttribute : global::System.Attribute; +internal sealed class BindableDerivedListAttribute : global::System.Attribute +{ + /// + /// Gets the AccessModifier of the BindableDerivedList property. + /// + /// + /// The AccessModifier of the property. + /// + public PropertyAccessModifier AccessModifier { get; init; } +} #nullable restore -#pragma warning restore \ No newline at end of file +#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.TupleType#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.TupleType#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs index 105caa6..f529f0c 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.TupleType#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.TupleType#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs @@ -14,6 +14,15 @@ namespace ReactiveUI.SourceGenerators; /// /// [global::System.AttributeUsage(global::System.AttributeTargets.Field, AllowMultiple = false, Inherited = false)] -internal sealed class BindableDerivedListAttribute : global::System.Attribute; +internal sealed class BindableDerivedListAttribute : global::System.Attribute +{ + /// + /// Gets the AccessModifier of the BindableDerivedList property. + /// + /// + /// The AccessModifier of the property. + /// + public PropertyAccessModifier AccessModifier { get; init; } +} #nullable restore #pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.WithReactive#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.WithReactive#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs index 105caa6..f529f0c 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.WithReactive#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.WithReactive#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs @@ -14,6 +14,15 @@ namespace ReactiveUI.SourceGenerators; /// /// [global::System.AttributeUsage(global::System.AttributeTargets.Field, AllowMultiple = false, Inherited = false)] -internal sealed class BindableDerivedListAttribute : global::System.Attribute; +internal sealed class BindableDerivedListAttribute : global::System.Attribute +{ + /// + /// Gets the AccessModifier of the BindableDerivedList property. + /// + /// + /// The AccessModifier of the property. + /// + public PropertyAccessModifier AccessModifier { get; init; } +} #nullable restore #pragma warning restore \ No newline at end of file diff --git a/src/ReactiveUI.SourceGenerator.Tests/UnitTests/DerivedListExtTests.cs b/src/ReactiveUI.SourceGenerator.Tests/UnitTests/DerivedListExtTests.cs index 1f5939d..370704d 100644 --- a/src/ReactiveUI.SourceGenerator.Tests/UnitTests/DerivedListExtTests.cs +++ b/src/ReactiveUI.SourceGenerator.Tests/UnitTests/DerivedListExtTests.cs @@ -402,4 +402,66 @@ public partial class TestVM : ReactiveObject return TestHelper.TestPass(sourceCode); } + + /// + /// Tests BindableDerivedList always generates public properties even for internal classes. + /// + /// A task to monitor the async. + [Test] + public Task InternalClassGeneratesPublicProperty() + { + const string sourceCode = """ + using System.Collections.ObjectModel; + using DynamicData; + using ReactiveUI; + + namespace TestNs; + + internal partial class ViewModel : ReactiveObject + { + [BindableDerivedList] + private readonly ReadOnlyObservableCollection? _theList; + } + """; + + return TestHelper.TestPass(sourceCode); + } + + /// + /// Tests BindableDerivedList with all supported property access modifiers. + /// + /// A task to monitor the async. + [Test] + public Task AccessModifiers() + { + const string sourceCode = """ + using System.Collections.ObjectModel; + using DynamicData; + + namespace TestNs; + + public partial class TestVM + { + [BindableDerivedList(AccessModifier = PropertyAccessModifier.Public)] + private ReadOnlyObservableCollection? _publicItems; + + [BindableDerivedList(AccessModifier = PropertyAccessModifier.Protected)] + private ReadOnlyObservableCollection? _protectedItems; + + [BindableDerivedList(AccessModifier = PropertyAccessModifier.Internal)] + private ReadOnlyObservableCollection? _internalItems; + + [BindableDerivedList(AccessModifier = PropertyAccessModifier.Private)] + private ReadOnlyObservableCollection? _privateItems; + + [BindableDerivedList(AccessModifier = PropertyAccessModifier.InternalProtected)] + private ReadOnlyObservableCollection? _internalProtectedItems; + + [BindableDerivedList(AccessModifier = PropertyAccessModifier.PrivateProtected)] + private ReadOnlyObservableCollection? _privateProtectedItems; + } + """; + + return TestHelper.TestPass(sourceCode); + } } diff --git a/src/ReactiveUI.SourceGenerators.Roslyn/AttributeDefinitions.cs b/src/ReactiveUI.SourceGenerators.Roslyn/AttributeDefinitions.cs index 9c38ce3..1ee66da 100644 --- a/src/ReactiveUI.SourceGenerators.Roslyn/AttributeDefinitions.cs +++ b/src/ReactiveUI.SourceGenerators.Roslyn/AttributeDefinitions.cs @@ -519,7 +519,16 @@ namespace ReactiveUI.SourceGenerators; /// [global::System.CodeDom.Compiler.GeneratedCode("{{ReactiveGenerator.GeneratorName}}", "{{ReactiveGenerator.GeneratorVersion}}")] [global::System.AttributeUsage(global::System.AttributeTargets.Field, AllowMultiple = false, Inherited = false)] -internal sealed class BindableDerivedListAttribute : global::System.Attribute; +internal sealed class BindableDerivedListAttribute : global::System.Attribute +{ + /// + /// Gets the AccessModifier of the BindableDerivedList property. + /// + /// + /// The AccessModifier of the property. + /// + public PropertyAccessModifier AccessModifier { get; init; } +} #nullable restore #pragma warning restore """; diff --git a/src/ReactiveUI.SourceGenerators.Roslyn/BindableDerivedList/BindableDerivedListGenerator.Execute.cs b/src/ReactiveUI.SourceGenerators.Roslyn/BindableDerivedList/BindableDerivedListGenerator.Execute.cs index 6de2884..a7b037e 100644 --- a/src/ReactiveUI.SourceGenerators.Roslyn/BindableDerivedList/BindableDerivedListGenerator.Execute.cs +++ b/src/ReactiveUI.SourceGenerators.Roslyn/BindableDerivedList/BindableDerivedListGenerator.Execute.cs @@ -73,6 +73,19 @@ public sealed partial class BindableDerivedListGenerator token.ThrowIfCancellationRequested(); + // Get AccessModifier enum value from the attribute + var accessModifier = attributeData.GetNamedArgument("AccessModifier") switch + { + 1 => "protected", + 2 => "internal", + 3 => "private", + 4 => "protected internal", + 5 => "private protected", + _ => "public", + }; + + token.ThrowIfCancellationRequested(); + // Get the nullability info for the property fieldSymbol.GetNullabilityInfo( context.SemanticModel, @@ -104,7 +117,8 @@ public sealed partial class BindableDerivedListGenerator propertyName, isReferenceTypeOrUnconstraindTypeParameter, includeMemberNotNullOnSetAccessor, - forwardedAttributesString), + forwardedAttributesString, + accessModifier), builder.ToImmutable()); } @@ -177,7 +191,7 @@ private static string GetPropertySyntax(BindableDerivedListInfo propertyInfo) $$""" /// {{propertyAttributes}} - {{propertyInfo.TargetInfo.TargetVisibility}} {{propertyInfo.TypeNameWithNullabilityAnnotations}} {{propertyInfo.PropertyName}} => {{propertyInfo.FieldName}}; + {{propertyInfo.AccessModifier}} {{propertyInfo.TypeNameWithNullabilityAnnotations}} {{propertyInfo.PropertyName}} => {{propertyInfo.FieldName}}; """; } } diff --git a/src/ReactiveUI.SourceGenerators.Roslyn/BindableDerivedList/Models/BindableDerivedListInfo.cs b/src/ReactiveUI.SourceGenerators.Roslyn/BindableDerivedList/Models/BindableDerivedListInfo.cs index 921f032..74a4374 100644 --- a/src/ReactiveUI.SourceGenerators.Roslyn/BindableDerivedList/Models/BindableDerivedListInfo.cs +++ b/src/ReactiveUI.SourceGenerators.Roslyn/BindableDerivedList/Models/BindableDerivedListInfo.cs @@ -18,4 +18,5 @@ internal sealed record BindableDerivedListInfo( string PropertyName, bool IsReferenceTypeOrUnconstrainedTypeParameter, bool IncludeMemberNotNullOnSetAccessor, - EquatableArray ForwardedAttributes); + EquatableArray ForwardedAttributes, + string AccessModifier); From 65e4b0ac544386554281698fea72fb6e66871ecc Mon Sep 17 00:00:00 2001 From: Chris Pulman Date: Sat, 4 Apr 2026 12:37:57 +0100 Subject: [PATCH 2/2] Add generated BindableDerivedList test files Add two auto-generated verification files for DerivedList tests: a BindableDerivedListAttribute definition and a partial TestVM with ReadOnlyObservableCollection properties. The attribute (internal) exposes a PropertyAccessModifier init-only property. The TestVM partial exposes read-only properties for fields with various access modifiers (public, protected, internal, private, protected internal, private protected) and marks them as excluded from code coverage. --- ...BindableDerivedListAttribute.g.verified.cs | 28 +++++++++++++++ ...s.TestVM.BindableDerivedList.g.verified.cs | 36 +++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.AccessModifiers#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs create mode 100644 src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.AccessModifiers#TestNs.TestVM.BindableDerivedList.g.verified.cs diff --git a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.AccessModifiers#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.AccessModifiers#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs new file mode 100644 index 0000000..07ca7a2 --- /dev/null +++ b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.AccessModifiers#ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.verified.cs @@ -0,0 +1,28 @@ +//HintName: ReactiveUI.SourceGenerators.BindableDerivedListAttribute.g.cs +// Copyright (c) 2026 .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, AllowMultiple = false, Inherited = false)] +internal sealed class BindableDerivedListAttribute : global::System.Attribute +{ + /// + /// Gets the AccessModifier of the BindableDerivedList property. + /// + /// + /// The AccessModifier of the property. + /// + public PropertyAccessModifier AccessModifier { get; init; } +} +#nullable restore +#pragma warning restore diff --git a/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.AccessModifiers#TestNs.TestVM.BindableDerivedList.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.AccessModifiers#TestNs.TestVM.BindableDerivedList.g.verified.cs new file mode 100644 index 0000000..51b6f37 --- /dev/null +++ b/src/ReactiveUI.SourceGenerator.Tests/DERIVEDLIST/DerivedListExtTests.AccessModifiers#TestNs.TestVM.BindableDerivedList.g.verified.cs @@ -0,0 +1,36 @@ +//HintName: TestNs.TestVM.BindableDerivedList.g.cs +// +using System.Collections.ObjectModel; +using DynamicData; +using ReactiveUI; + +#pragma warning disable +#nullable enable + +namespace TestNs +{ + + public partial class TestVM + { + /// + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + public global::System.Collections.ObjectModel.ReadOnlyObservableCollection? PublicItems => _publicItems; + /// + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + protected global::System.Collections.ObjectModel.ReadOnlyObservableCollection? ProtectedItems => _protectedItems; + /// + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal global::System.Collections.ObjectModel.ReadOnlyObservableCollection? InternalItems => _internalItems; + /// + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + private global::System.Collections.ObjectModel.ReadOnlyObservableCollection? PrivateItems => _privateItems; + /// + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + protected internal global::System.Collections.ObjectModel.ReadOnlyObservableCollection? InternalProtectedItems => _internalProtectedItems; + /// + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + private protected global::System.Collections.ObjectModel.ReadOnlyObservableCollection? PrivateProtectedItems => _privateProtectedItems; + } +} +#nullable restore +#pragma warning restore