Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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.

// <auto-generated/>
#pragma warning disable
#nullable enable
namespace ReactiveUI.SourceGenerators;

/// <summary>
/// AccessModifier.
/// </summary>
internal enum AccessModifier
{
Public,
Protected,
Internal,
Private,
InternalProtected,
PrivateProtected,
Init,
}

/// <summary>
/// InheritanceModifier.
/// </summary>
internal enum InheritanceModifier
{
None,
Virtual,
Override,
New,
}
#nullable restore
#pragma warning restore
Original file line number Diff line number Diff line change
@@ -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;

// <auto-generated/>
#pragma warning disable
#nullable enable
namespace ReactiveUI.SourceGenerators;

/// <summary>
/// ReactiveAttribute.
/// </summary>
/// <seealso cref="Attribute" />
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
internal sealed class ReactiveAttribute : Attribute
{
/// <summary>
/// Gets the AccessModifier of the set property.
/// </summary>
/// <value>
/// The AccessModifier of the set property.
/// </value>
public AccessModifier SetModifier { get; init; }

/// <summary>
/// Gets the InheritanceModifier of the property.
/// </sumary>
public InheritanceModifier Inheritance { get; init; }

/// <summary>
/// Use Required attribute to indicate that the property is required.
/// </summary>
public bool UseRequired { get; init; }
}
#nullable restore
#pragma warning restore
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//HintName: TestNs.TestVM.Properties.g.cs
// <auto-generated/>
using ReactiveUI;

#pragma warning disable
#nullable enable

namespace TestNs
{
/// <summary>
/// Partial class for the TestVM which contains ReactiveUI Reactive property initialization.
/// </summary>
public partial class TestVM
{

/// <inheritdoc cref="this.value"/>
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public string Value
{
get => value;
[global::System.Diagnostics.CodeAnalysis.MemberNotNull("this.value")]
set => this.RaiseAndSetIfChanged(ref this.value, value);
}
}
}
#nullable restore
#pragma warning restore
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,33 @@ public partial class TestVM : ReactiveObject
return TestHelper.TestPass(sourceCode);
}

/// <summary>
/// Tests that the source generator correctly generates reactive properties.
/// </summary>
/// <returns>A task to monitor the async.</returns>
[Fact]
public Task FromReactivePropertiesCalledValue()
{
// 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]
private string value = string.Empty;
}
""";

// Act: Initialize the helper and run the generator. Assert: Verify the generated code.
return TestHelper.TestPass(sourceCode);
}

/// <summary>
/// Tests that the source generator correctly generates reactive properties.
/// </summary>
Expand Down
5 changes: 5 additions & 0 deletions src/ReactiveUI.SourceGenerators.Execute/TestClassOAPH_VM.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ public partial class TestClassOAPH_VM : ReactiveObject
[Reactive]
private bool _reactiveTestField;

[Reactive]
#pragma warning disable SX1309 // Field names should begin with underscore
private string value = string.Empty;
#pragma warning restore SX1309 // Field names should begin with underscore

/// <summary>
/// Initializes a new instance of the <see cref="TestClassOAPH_VM"/> class.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,12 @@ private static string GetPropertySyntax(PropertyInfo propertyInfo)
return string.Empty;
}

var fieldName = propertyInfo.FieldName;
if (propertyInfo.FieldName == "value")
{
fieldName = "this.value";
}

var fieldSyntax = string.Empty;
var partialModifier = propertyInfo.IsProperty ? "partial " : string.Empty;
if (propertyInfo.IsProperty)
Expand All @@ -318,23 +324,23 @@ private static string GetPropertySyntax(PropertyInfo propertyInfo)
return
$$"""
{{fieldSyntax}}
/// <inheritdoc cref="{{propertyInfo.FieldName}}"/>
/// <inheritdoc cref="{{fieldName}}"/>
{{propertyAttributes}}
{{propertyInfo.TargetInfo.TargetVisibility}}{{propertyInfo.Inheritance}} {{partialModifier}}{{propertyInfo.UseRequired}}{{propertyInfo.TypeNameWithNullabilityAnnotations}} {{propertyInfo.PropertyName}}
{
get => {{propertyInfo.FieldName}};
[global::System.Diagnostics.CodeAnalysis.MemberNotNull("{{propertyInfo.FieldName}}")]
{{propertyInfo.AccessModifier}} => this.RaiseAndSetIfChanged(ref {{propertyInfo.FieldName}}, value);
[global::System.Diagnostics.CodeAnalysis.MemberNotNull("{{fieldName}}")]
{{propertyInfo.AccessModifier}} => this.RaiseAndSetIfChanged(ref {{fieldName}}, value);
}
""";
}

return
$$"""
{{fieldSyntax}}
/// <inheritdoc cref="{{propertyInfo.FieldName}}"/>
/// <inheritdoc cref="{{fieldName}}"/>
{{propertyAttributes}}
{{propertyInfo.TargetInfo.TargetVisibility}}{{propertyInfo.Inheritance}} {{partialModifier}}{{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 {{fieldName}}, value); }
""";
}
}
Loading