Skip to content

Commit

Permalink
Fix for OAPH nullability for reference types (#126)
Browse files Browse the repository at this point in the history
* Fix for OAPH nullability for reference types

Added Nested Project test to prove inheritance can operate

* Add IsNullable, Fix tests
  • Loading branch information
ChrisPulman authored Nov 9, 2024
1 parent 9c1feac commit 311fde1
Show file tree
Hide file tree
Showing 16 changed files with 372 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//HintName: ReactiveUI.SourceGenerators.ObservableAsPropertyAttribute.g.cs
// Copyright (c) 2024 .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>
/// ObservableAsPropertyAttribute.
/// </summary>
/// <seealso cref="Attribute" />
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
internal sealed class ObservableAsPropertyAttribute : Attribute
{
/// <summary>
/// Gets the name of the property.
/// </summary>
/// <value>
/// The name of the property.
/// </value>
public string? PropertyName { get; init; }

/// <summary>
/// Gets the Readonly state of the OAPH property.
/// </summary>
/// <value>
/// The is read only of the OAPH property.
/// </value>
public bool ReadOnly { get; init; } = true;
}
#nullable restore
#pragma warning restore
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//HintName: TestVM.ObservableAsPropertyFromObservable.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="Test7Property"/>
private object? _test7Property;

/// <inheritdoc cref="_test7PropertyHelper"/>
private ReactiveUI.ObservableAsPropertyHelper<object?>? _test7PropertyHelper;

/// <inheritdoc cref="_test7Property"/>
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
[global::System.Text.Json.Serialization.JsonIncludeAttribute()]
public object? Test7Property { get => _test7Property = (_test7PropertyHelper == null ? _test7Property : _test7PropertyHelper.Value); }

[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
protected void InitializeOAPH()
{
_test7PropertyHelper = Test7!.ToProperty(this, nameof(Test7Property));
}
}
}
#nullable restore
#pragma warning restore
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//HintName: ReactiveUI.SourceGenerators.ObservableAsPropertyAttribute.g.cs
// Copyright (c) 2024 .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>
/// ObservableAsPropertyAttribute.
/// </summary>
/// <seealso cref="Attribute" />
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
internal sealed class ObservableAsPropertyAttribute : Attribute
{
/// <summary>
/// Gets the name of the property.
/// </summary>
/// <value>
/// The name of the property.
/// </value>
public string? PropertyName { get; init; }

/// <summary>
/// Gets the Readonly state of the OAPH property.
/// </summary>
/// <value>
/// The is read only of the OAPH property.
/// </value>
public bool ReadOnly { get; init; } = true;
}
#nullable restore
#pragma warning restore
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//HintName: TestVM.ObservableAsPropertyFromObservable.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="Test6Property"/>
private object _test6Property;

/// <inheritdoc cref="_test6PropertyHelper"/>
private ReactiveUI.ObservableAsPropertyHelper<object>? _test6PropertyHelper;

/// <inheritdoc cref="_test6Property"/>
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
[global::System.Text.Json.Serialization.JsonIncludeAttribute()]
public object Test6Property { get => _test6Property = _test6PropertyHelper?.Value ?? _test6Property; }

[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
protected void InitializeOAPH()
{
_test6PropertyHelper = Test6!.ToProperty(this, nameof(Test6Property));
}
}
}
#nullable restore
#pragma warning restore
Original file line number Diff line number Diff line change
Expand Up @@ -169,5 +169,73 @@ public partial class TestVM : ReactiveObject
return VerifyGenerator(driver);
}

/// <summary>
/// Tests that the source generator correctly generates observable properties.
/// </summary>
/// <returns>A task to monitor the async.</returns>
[Fact]
public Task FromObservablePropertiesWithAttributeRef()
{
// Arrange: Setup the source code that matches the generator input expectations.
const string sourceCode = """
using System;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
using ReactiveUI;
using ReactiveUI.SourceGenerators;
using System.Reactive.Linq;

namespace TestNs;

public partial class TestVM : ReactiveObject
{
[ObservableAsProperty(PropertyName = "MyNamedProperty")]
[property: JsonInclude]
[DataMember]
public IObservable<object> Test6 => Observable.Return(new object());
}
""";

// Act: Initialize the helper and run the generator.
var driver = TestHelper.TestPass(sourceCode);

// Assert: Verify the generated code.
return VerifyGenerator(driver);
}

/// <summary>
/// Tests that the source generator correctly generates observable properties.
/// </summary>
/// <returns>A task to monitor the async.</returns>
[Fact]
public Task FromObservablePropertiesWithAttributeNullableRef()
{
// Arrange: Setup the source code that matches the generator input expectations.
const string sourceCode = """
using System;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
using ReactiveUI;
using ReactiveUI.SourceGenerators;
using System.Reactive.Linq;

namespace TestNs;

public partial class TestVM : ReactiveObject
{
[ObservableAsProperty(PropertyName = "MyNamedProperty")]
[property: JsonInclude]
[DataMember]
public IObservable<object?> Test7 => Observable.Return(new object());
}
""";

// Act: Initialize the helper and run the generator.
var driver = TestHelper.TestPass(sourceCode);

// Assert: Verify the generated code.
return VerifyGenerator(driver);
}

private SettingsTask VerifyGenerator(GeneratorDriver driver) => Verify(driver).UseDirectory(TestHelper.VerifiedFilePath()).ScrubLinesContaining("[global::System.CodeDom.Compiler.GeneratedCode(\"");
}
18 changes: 18 additions & 0 deletions src/ReactiveUI.SourceGenerators.Execute.Nested1/Class1.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) 2024 .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 ReactiveUI;
using ReactiveUI.SourceGenerators;

namespace SGReactiveUI.SourceGenerators.Execute.Nested1;

/// <summary>
/// Class1.
/// </summary>
public partial class Class1 : ReactiveObject
{
[Reactive]
private string? _property1;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\ReactiveUI.SourceGenerators\ReactiveUI.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" PrivateAssets="all" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="ReactiveUI" />
</ItemGroup>

</Project>
18 changes: 18 additions & 0 deletions src/ReactiveUI.SourceGenerators.Execute.Nested2/Class1.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) 2024 .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 ReactiveUI;
using ReactiveUI.SourceGenerators;

namespace SGReactiveUI.SourceGenerators.Execute.Nested2;

/// <summary>
/// Class1.
/// </summary>
public partial class Class1 : ReactiveObject
{
[Reactive]
private string? _property1;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\ReactiveUI.SourceGenerators\ReactiveUI.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" PrivateAssets="all" />
<ProjectReference Include="..\ReactiveUI.SourceGenerators.Execute.Nested1\ReactiveUI.SourceGenerators.Execute.Nested1.csproj" />
</ItemGroup>

</Project>
18 changes: 18 additions & 0 deletions src/ReactiveUI.SourceGenerators.Execute.Nested3/Class1.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) 2024 .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 ReactiveUI;
using ReactiveUI.SourceGenerators;

namespace SGReactiveUI.SourceGenerators.Execute.Nested3;

/// <summary>
/// Class1.
/// </summary>
public partial class Class1 : ReactiveObject
{
[Reactive]
private string? _property1;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<LangVersion>12.0</LangVersion>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\ReactiveUI.SourceGenerators\ReactiveUI.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" PrivateAssets="all" />
<ProjectReference Include="..\ReactiveUI.SourceGenerators.Execute.Nested2\ReactiveUI.SourceGenerators.Execute.Nested2.csproj" />
</ItemGroup>

</Project>
11 changes: 11 additions & 0 deletions src/ReactiveUI.SourceGenerators.Execute/TestViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ public TestViewModel()
_observableAsPropertyTest2Property = 11223344;
Console.Out.WriteLine(ObservableAsPropertyTest2Property);
Console.Out.WriteLine(_observableAsPropertyTest2Property);

_referenceTypeObservableProperty = default!;
ReferenceTypeObservable = Observable.Return(new object());
NullableReferenceTypeObservable = Observable.Return(new object());

InitializeOAPH();

Console.Out.WriteLine(Test1Command);
Expand Down Expand Up @@ -239,6 +244,12 @@ public TestViewModel()
/// </summary>
public ViewModelActivator Activator { get; } = new();

[ObservableAsProperty]
private IObservable<object> ReferenceTypeObservable { get; }

[ObservableAsProperty]
private IObservable<object?> NullableReferenceTypeObservable { get; }

/// <summary>
/// Gets observables as property test.
/// </summary>
Expand Down
Loading

0 comments on commit 311fde1

Please sign in to comment.