Skip to content

Commit 311fde1

Browse files
authored
Fix for OAPH nullability for reference types (#126)
* Fix for OAPH nullability for reference types Added Nested Project test to prove inheritance can operate * Add IsNullable, Fix tests
1 parent 9c1feac commit 311fde1

16 files changed

+372
-14
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//HintName: ReactiveUI.SourceGenerators.ObservableAsPropertyAttribute.g.cs
2+
// Copyright (c) 2024 .NET Foundation and Contributors. All rights reserved.
3+
// Licensed to the .NET Foundation under one or more agreements.
4+
// The .NET Foundation licenses this file to you under the MIT license.
5+
// See the LICENSE file in the project root for full license information.
6+
7+
using System;
8+
9+
// <auto-generated/>
10+
#pragma warning disable
11+
#nullable enable
12+
namespace ReactiveUI.SourceGenerators;
13+
14+
/// <summary>
15+
/// ObservableAsPropertyAttribute.
16+
/// </summary>
17+
/// <seealso cref="Attribute" />
18+
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
19+
internal sealed class ObservableAsPropertyAttribute : Attribute
20+
{
21+
/// <summary>
22+
/// Gets the name of the property.
23+
/// </summary>
24+
/// <value>
25+
/// The name of the property.
26+
/// </value>
27+
public string? PropertyName { get; init; }
28+
29+
/// <summary>
30+
/// Gets the Readonly state of the OAPH property.
31+
/// </summary>
32+
/// <value>
33+
/// The is read only of the OAPH property.
34+
/// </value>
35+
public bool ReadOnly { get; init; } = true;
36+
}
37+
#nullable restore
38+
#pragma warning restore
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//HintName: TestVM.ObservableAsPropertyFromObservable.g.cs
2+
// <auto-generated/>
3+
using ReactiveUI;
4+
5+
#pragma warning disable
6+
#nullable enable
7+
8+
namespace TestNs
9+
{
10+
/// <summary>
11+
/// Partial class for the TestVM which contains ReactiveUI Reactive property initialization.
12+
/// </summary>
13+
public partial class TestVM
14+
{
15+
/// <inheritdoc cref="Test7Property"/>
16+
private object? _test7Property;
17+
18+
/// <inheritdoc cref="_test7PropertyHelper"/>
19+
private ReactiveUI.ObservableAsPropertyHelper<object?>? _test7PropertyHelper;
20+
21+
/// <inheritdoc cref="_test7Property"/>
22+
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
23+
[global::System.Text.Json.Serialization.JsonIncludeAttribute()]
24+
public object? Test7Property { get => _test7Property = (_test7PropertyHelper == null ? _test7Property : _test7PropertyHelper.Value); }
25+
26+
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
27+
protected void InitializeOAPH()
28+
{
29+
_test7PropertyHelper = Test7!.ToProperty(this, nameof(Test7Property));
30+
}
31+
}
32+
}
33+
#nullable restore
34+
#pragma warning restore
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//HintName: ReactiveUI.SourceGenerators.ObservableAsPropertyAttribute.g.cs
2+
// Copyright (c) 2024 .NET Foundation and Contributors. All rights reserved.
3+
// Licensed to the .NET Foundation under one or more agreements.
4+
// The .NET Foundation licenses this file to you under the MIT license.
5+
// See the LICENSE file in the project root for full license information.
6+
7+
using System;
8+
9+
// <auto-generated/>
10+
#pragma warning disable
11+
#nullable enable
12+
namespace ReactiveUI.SourceGenerators;
13+
14+
/// <summary>
15+
/// ObservableAsPropertyAttribute.
16+
/// </summary>
17+
/// <seealso cref="Attribute" />
18+
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
19+
internal sealed class ObservableAsPropertyAttribute : Attribute
20+
{
21+
/// <summary>
22+
/// Gets the name of the property.
23+
/// </summary>
24+
/// <value>
25+
/// The name of the property.
26+
/// </value>
27+
public string? PropertyName { get; init; }
28+
29+
/// <summary>
30+
/// Gets the Readonly state of the OAPH property.
31+
/// </summary>
32+
/// <value>
33+
/// The is read only of the OAPH property.
34+
/// </value>
35+
public bool ReadOnly { get; init; } = true;
36+
}
37+
#nullable restore
38+
#pragma warning restore
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//HintName: TestVM.ObservableAsPropertyFromObservable.g.cs
2+
// <auto-generated/>
3+
using ReactiveUI;
4+
5+
#pragma warning disable
6+
#nullable enable
7+
8+
namespace TestNs
9+
{
10+
/// <summary>
11+
/// Partial class for the TestVM which contains ReactiveUI Reactive property initialization.
12+
/// </summary>
13+
public partial class TestVM
14+
{
15+
/// <inheritdoc cref="Test6Property"/>
16+
private object _test6Property;
17+
18+
/// <inheritdoc cref="_test6PropertyHelper"/>
19+
private ReactiveUI.ObservableAsPropertyHelper<object>? _test6PropertyHelper;
20+
21+
/// <inheritdoc cref="_test6Property"/>
22+
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
23+
[global::System.Text.Json.Serialization.JsonIncludeAttribute()]
24+
public object Test6Property { get => _test6Property = _test6PropertyHelper?.Value ?? _test6Property; }
25+
26+
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
27+
protected void InitializeOAPH()
28+
{
29+
_test6PropertyHelper = Test6!.ToProperty(this, nameof(Test6Property));
30+
}
31+
}
32+
}
33+
#nullable restore
34+
#pragma warning restore

src/ReactiveUI.SourceGenerator.Tests/UnitTests/OAPFromObservableGeneratorTests.cs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,5 +169,73 @@ public partial class TestVM : ReactiveObject
169169
return VerifyGenerator(driver);
170170
}
171171

172+
/// <summary>
173+
/// Tests that the source generator correctly generates observable properties.
174+
/// </summary>
175+
/// <returns>A task to monitor the async.</returns>
176+
[Fact]
177+
public Task FromObservablePropertiesWithAttributeRef()
178+
{
179+
// Arrange: Setup the source code that matches the generator input expectations.
180+
const string sourceCode = """
181+
using System;
182+
using System.Runtime.Serialization;
183+
using System.Text.Json.Serialization;
184+
using ReactiveUI;
185+
using ReactiveUI.SourceGenerators;
186+
using System.Reactive.Linq;
187+
188+
namespace TestNs;
189+
190+
public partial class TestVM : ReactiveObject
191+
{
192+
[ObservableAsProperty(PropertyName = "MyNamedProperty")]
193+
[property: JsonInclude]
194+
[DataMember]
195+
public IObservable<object> Test6 => Observable.Return(new object());
196+
}
197+
""";
198+
199+
// Act: Initialize the helper and run the generator.
200+
var driver = TestHelper.TestPass(sourceCode);
201+
202+
// Assert: Verify the generated code.
203+
return VerifyGenerator(driver);
204+
}
205+
206+
/// <summary>
207+
/// Tests that the source generator correctly generates observable properties.
208+
/// </summary>
209+
/// <returns>A task to monitor the async.</returns>
210+
[Fact]
211+
public Task FromObservablePropertiesWithAttributeNullableRef()
212+
{
213+
// Arrange: Setup the source code that matches the generator input expectations.
214+
const string sourceCode = """
215+
using System;
216+
using System.Runtime.Serialization;
217+
using System.Text.Json.Serialization;
218+
using ReactiveUI;
219+
using ReactiveUI.SourceGenerators;
220+
using System.Reactive.Linq;
221+
222+
namespace TestNs;
223+
224+
public partial class TestVM : ReactiveObject
225+
{
226+
[ObservableAsProperty(PropertyName = "MyNamedProperty")]
227+
[property: JsonInclude]
228+
[DataMember]
229+
public IObservable<object?> Test7 => Observable.Return(new object());
230+
}
231+
""";
232+
233+
// Act: Initialize the helper and run the generator.
234+
var driver = TestHelper.TestPass(sourceCode);
235+
236+
// Assert: Verify the generated code.
237+
return VerifyGenerator(driver);
238+
}
239+
172240
private SettingsTask VerifyGenerator(GeneratorDriver driver) => Verify(driver).UseDirectory(TestHelper.VerifiedFilePath()).ScrubLinesContaining("[global::System.CodeDom.Compiler.GeneratedCode(\"");
173241
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) 2024 .NET Foundation and Contributors. All rights reserved.
2+
// Licensed to the .NET Foundation under one or more agreements.
3+
// The .NET Foundation licenses this file to you under the MIT license.
4+
// See the LICENSE file in the project root for full license information.
5+
6+
using ReactiveUI;
7+
using ReactiveUI.SourceGenerators;
8+
9+
namespace SGReactiveUI.SourceGenerators.Execute.Nested1;
10+
11+
/// <summary>
12+
/// Class1.
13+
/// </summary>
14+
public partial class Class1 : ReactiveObject
15+
{
16+
[Reactive]
17+
private string? _property1;
18+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<ProjectReference Include="..\ReactiveUI.SourceGenerators\ReactiveUI.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" PrivateAssets="all" />
11+
</ItemGroup>
12+
13+
<ItemGroup>
14+
<PackageReference Include="ReactiveUI" />
15+
</ItemGroup>
16+
17+
</Project>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) 2024 .NET Foundation and Contributors. All rights reserved.
2+
// Licensed to the .NET Foundation under one or more agreements.
3+
// The .NET Foundation licenses this file to you under the MIT license.
4+
// See the LICENSE file in the project root for full license information.
5+
6+
using ReactiveUI;
7+
using ReactiveUI.SourceGenerators;
8+
9+
namespace SGReactiveUI.SourceGenerators.Execute.Nested2;
10+
11+
/// <summary>
12+
/// Class1.
13+
/// </summary>
14+
public partial class Class1 : ReactiveObject
15+
{
16+
[Reactive]
17+
private string? _property1;
18+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<ProjectReference Include="..\ReactiveUI.SourceGenerators\ReactiveUI.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" PrivateAssets="all" />
11+
<ProjectReference Include="..\ReactiveUI.SourceGenerators.Execute.Nested1\ReactiveUI.SourceGenerators.Execute.Nested1.csproj" />
12+
</ItemGroup>
13+
14+
</Project>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) 2024 .NET Foundation and Contributors. All rights reserved.
2+
// Licensed to the .NET Foundation under one or more agreements.
3+
// The .NET Foundation licenses this file to you under the MIT license.
4+
// See the LICENSE file in the project root for full license information.
5+
6+
using ReactiveUI;
7+
using ReactiveUI.SourceGenerators;
8+
9+
namespace SGReactiveUI.SourceGenerators.Execute.Nested3;
10+
11+
/// <summary>
12+
/// Class1.
13+
/// </summary>
14+
public partial class Class1 : ReactiveObject
15+
{
16+
[Reactive]
17+
private string? _property1;
18+
}

0 commit comments

Comments
 (0)