Skip to content

Commit 3467bf4

Browse files
authored
Update Add separate CodeFixes Analyzer (#101)
* Update Add CodeFixes Analyser Separate CodeFixes from SourceGenerators Fix for #99 * Remove Microsoft.CodeAnalysis.CSharp.Workspaces * Update Codefix name * Fix ambiguous name issue * Update Routed Control Host * Update ControlHost Generator * Reorganise again due to analyser error Recommendation not to use Compile Analysers with Workspace Analysers - moved Generator analytics back to SourceGenerator Project * Update Attribute collection methods Ensure that we pass on Attributes placed on Fields, handle Property Declaration Attributes on Fields
1 parent 1507810 commit 3467bf4

File tree

43 files changed

+1302
-1344
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1302
-1344
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ These Source Generators were designed to work in full with ReactiveUI V19.5.31 a
99
- [ReactiveCommand(CanExecute = nameof(IObservableBoolName))] with CanExecute
1010
- [ReactiveCommand][property: AttribueToAddToCommand] with Attribute passthrough
1111
- [IViewFor(nameof(ViewModelName))]
12+
- [RoutedControlHost("YourNameSpace.CustomControl")]
13+
- [ViewModelControlHost("YourNameSpace.CustomControl")]
1214

1315
Versions older than V19.5.31 to this:
1416
- [ReactiveCommand] all options supported except Cancellation Token asnyc methods.
@@ -84,6 +86,18 @@ public partial class MyReactiveClass : ReactiveObject
8486
}
8587
```
8688

89+
### Usage Reactive property with property Attribute pass through
90+
```csharp
91+
using ReactiveUI.SourceGenerators;
92+
93+
public partial class MyReactiveClass : ReactiveObject
94+
{
95+
[Reactive]
96+
[property: JsonIgnore]
97+
private string _myProperty;
98+
}
99+
```
100+
87101
## Usage ObservableAsPropertyHelper `[ObservableAsProperty]`
88102

89103
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.

src/Directory.Packages.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<PackageVersion Include="ReactiveUI" Version="20.1.63" />
1313
<PackageVersion Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
1414
<PackageVersion Include="Microsoft.Maui.Controls.Compatibility" Version="$(MauiVersion)" />
15-
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
15+
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" />
1616
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="$(CodeAnalysisVersion)" />
1717
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="$(CodeAnalysisVersion)" />
1818
<PackageVersion Include="PolySharp" Version="1.14.1" />

src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperiesWithAttributes#TestVM.Properties.g.verified.cs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,23 @@
77

88
namespace TestNs
99
{
10-
/// <summary>
11-
/// Partial class for the TestVM which contains ReactiveUI Reactive property initialization.
12-
/// </summary>
13-
public partial class TestVM
14-
{
15-
[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.ReactiveGenerator", "1.1.0.0")]
16-
/// <inheritdoc cref="_test3"/>
17-
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
18-
[System.Text.Json.Serialization.JsonInclude]
19-
[System.Runtime.Serialization.DataMember]
20-
public int Test3
21-
{
22-
get => _test3;
23-
[global::System.Diagnostics.CodeAnalysis.MemberNotNull("_test3")]
24-
set => this.RaiseAndSetIfChanged(ref _test3, value);
25-
}
26-
}
10+
/// <summary>
11+
/// Partial class for the TestVM which contains ReactiveUI Reactive property initialization.
12+
/// </summary>
13+
public partial class TestVM
14+
{
15+
[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.ReactiveGenerator", "1.1.0.0")]
16+
/// <inheritdoc cref="_test3"/>
17+
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
18+
[global::System.Runtime.Serialization.DataMemberAttribute()]
19+
[global::System.Text.Json.Serialization.JsonIncludeAttribute()]
20+
public int Test3
21+
{
22+
get => _test3;
23+
[global::System.Diagnostics.CodeAnalysis.MemberNotNull("_test3")]
24+
set => this.RaiseAndSetIfChanged(ref _test3, value);
25+
}
26+
}
2727
}
2828
#nullable restore
2929
#pragma warning restore

src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperties#TestVM.Properties.g.verified.cs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,21 @@
77

88
namespace TestNs
99
{
10-
/// <summary>
11-
/// Partial class for the TestVM which contains ReactiveUI Reactive property initialization.
12-
/// </summary>
13-
public partial class TestVM
14-
{
15-
[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.ReactiveGenerator", "1.1.0.0")]
16-
/// <inheritdoc cref="_test1"/>
17-
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
18-
public int Test1
19-
{
20-
get => _test1;
21-
[global::System.Diagnostics.CodeAnalysis.MemberNotNull("_test1")]
22-
set => this.RaiseAndSetIfChanged(ref _test1, value);
23-
}
24-
}
10+
/// <summary>
11+
/// Partial class for the TestVM which contains ReactiveUI Reactive property initialization.
12+
/// </summary>
13+
public partial class TestVM
14+
{
15+
[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.ReactiveGenerator", "1.1.0.0")]
16+
/// <inheritdoc cref="_test1"/>
17+
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
18+
public int Test1
19+
{
20+
get => _test1;
21+
[global::System.Diagnostics.CodeAnalysis.MemberNotNull("_test1")]
22+
set => this.RaiseAndSetIfChanged(ref _test1, value);
23+
}
24+
}
2525
}
2626
#nullable restore
2727
#pragma warning restore

src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAccess#TestVM.Properties.g.verified.cs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,21 @@
77

88
namespace TestNs
99
{
10-
/// <summary>
11-
/// Partial class for the TestVM which contains ReactiveUI Reactive property initialization.
12-
/// </summary>
13-
public partial class TestVM
14-
{
15-
[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.ReactiveGenerator", "1.1.0.0")]
16-
/// <inheritdoc cref="_test2"/>
17-
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
18-
public int Test2
19-
{
20-
get => _test2;
21-
[global::System.Diagnostics.CodeAnalysis.MemberNotNull("_test2")]
22-
set => this.RaiseAndSetIfChanged(ref _test2, value);
23-
}
24-
}
10+
/// <summary>
11+
/// Partial class for the TestVM which contains ReactiveUI Reactive property initialization.
12+
/// </summary>
13+
public partial class TestVM
14+
{
15+
[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.ReactiveGenerator", "1.1.0.0")]
16+
/// <inheritdoc cref="_test2"/>
17+
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
18+
public int Test2
19+
{
20+
get => _test2;
21+
[global::System.Diagnostics.CodeAnalysis.MemberNotNull("_test2")]
22+
set => this.RaiseAndSetIfChanged(ref _test2, value);
23+
}
24+
}
2525
}
2626
#nullable restore
2727
#pragma warning restore

src/ReactiveUI.SourceGenerator.Tests/ReactiveUI.SourceGenerators.Tests.csproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,6 @@
2525
<PackageReference Include="FluentAssertions" />
2626
</ItemGroup>
2727

28-
<ItemGroup>
29-
<ProjectReference Include="..\ReactiveUI.SourceGenerators\ReactiveUI.SourceGenerators.csproj" />
30-
</ItemGroup>
31-
3228
<ItemGroup>
3329
<Using Include="Xunit" />
3430
</ItemGroup>
@@ -40,4 +36,8 @@
4036
<Folder Include="REACTIVE\" />
4137
</ItemGroup>
4238

39+
<ItemGroup>
40+
<ProjectReference Include="..\ReactiveUI.SourceGenerators\ReactiveUI.SourceGenerators.csproj" />
41+
</ItemGroup>
42+
4343
</Project>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ namespace TestNs;
9494
9595
public partial class TestVM : ReactiveObject
9696
{
97-
[JsonInclude]
97+
[property: JsonInclude]
9898
[DataMember]
9999
[Reactive]
100100
private int _test3 = 10;
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
; Shipped analyzer releases
2+
; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
3+
4+
## Release 1.0
5+
6+
### New Rules
7+
8+
Rule ID | Category | Severity | Notes
9+
--------|----------|----------|-------
10+
RXUISG0016 | ReactiveUI.SourceGenerators.PropertyToReactiveFieldCodeFixProvider | Info | See https://www.reactiveui.net/docs/handbook/view-models/boilerplate-code.html
11+
12+
13+
## Rules
14+
Shipped in ReactiveUI.SourceGenerators
15+
16+
- RXUISG0001 - Unsupported C# Language Version
17+
This rule checks if the project is using an unsupported C# language version. The supported versions are C# 12.0 and above. If the project is using an unsupported version, the rule will raise an error.
18+
19+
- RXUISG0002 - ReactiveCommandGenerator
20+
This rule checks if the `ReactiveCommand` has a Invalid ReactiveCommand method signature.
21+
22+
- RXUISG0003 - ReactiveCommandGenerator
23+
This rule checks if the `ReactiveCommand` has a Invalid ReactiveCommand.CanExecute member name.
24+
25+
- RXUISG0004 - ReactiveCommandGenerator
26+
This rule checks if the `ReactiveCommand` has Multiple ReactiveCommand.CanExecute member name matches.
27+
28+
- RXUISG0005 - ReactiveCommandGenerator
29+
This rule checks if the `ReactiveCommand` has No valid ReactiveCommand.CanExecute member match.
30+
31+
- RXUISG0006 - ReactiveCommandGenerator
32+
This rule checks if the `ReactiveCommand` has Invalid field or property targeted attribute type.
33+
34+
- RXUISG0007 - ReactiveCommandGenerator
35+
This rule checks if the `ReactiveCommand` has Invalid field or property targeted attribute expression.
36+
37+
- RXUISG0008 - AsyncVoidReturningReactiveCommandMethodAnalyzer
38+
This rule checks if the `ReactiveCommand` has Async void returning method annotated with ReactiveCommand.
39+
40+
- RXUISG0009 - ReactiveGenerator
41+
This rule checks if the `Reactive` has Name collision for generated property.
42+
43+
- RXUISG0010 - ReactiveGenerator
44+
This rule checks if the `Reactive` has Invalid property targeted attribute type.
45+
46+
- RXUISG0011 - ReactiveGenerator
47+
This rule checks if the `Reactive` has Invalid property targeted attribute expression.
48+
49+
- RXUISG0012 - ObservableAsPropertyGenerator
50+
This rule checks if the `ObservableAsProperty` has Invalid property targeted attribute type.
51+
52+
- RXUISG0013 - ObservableAsPropertyGenerator
53+
This rule checks if the `ObservableAsProperty` has Invalid property targeted attribute expression.
54+
55+
- RXUISG0014 - ObservableAsPropertyGenerator
56+
This rule checks if the `ObservableAsProperty` has Invalid generated property declaration.
57+
58+
- RXUISG0015 - ReactiveGenerator
59+
This rule checks if the `Reactive` attribute is being used correctly. If the `Reactive` has Invalid generated property declaration.
60+
61+
- RXUISG0016 - PropertyToReactiveFieldCodeFixProvider
62+
This rule checks if there are any Properties to change to Reactive Field, change to [Reactive] private type _fieldName;.
63+
64+
- RXUISG0017 - ObservableAsPropertyFromObservableGenerator
65+
This rule checks if the `ObservableAsProperty` has Invalid generated property declaration.
66+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
; Unshipped analyzer releases
2+
; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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 System.Collections.Generic;
7+
using System.Collections.Immutable;
8+
using System.Linq;
9+
using Microsoft.CodeAnalysis;
10+
using Microsoft.CodeAnalysis.CSharp;
11+
using Microsoft.CodeAnalysis.CSharp.Syntax;
12+
using Microsoft.CodeAnalysis.Diagnostics;
13+
using static ReactiveUI.SourceGenerators.CodeFixers.Diagnostics.DiagnosticDescriptors;
14+
15+
namespace ReactiveUI.SourceGenerators.CodeFixers;
16+
17+
/// <summary>
18+
/// PropertyToFieldAnalyzer.
19+
/// </summary>
20+
[DiagnosticAnalyzer(LanguageNames.CSharp)]
21+
public class PropertyToReactiveFieldAnalyzer : DiagnosticAnalyzer
22+
{
23+
/// <summary>
24+
/// Gets the supported diagnostics.
25+
/// </summary>
26+
/// <value>
27+
/// The supported diagnostics.
28+
/// </value>
29+
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
30+
ImmutableArray.Create(PropertyToReactiveFieldRule);
31+
32+
/// <summary>
33+
/// Initializes the specified context.
34+
/// </summary>
35+
/// <param name="context">The context.</param>
36+
public override void Initialize(AnalysisContext context)
37+
{
38+
if (context is null)
39+
{
40+
throw new System.ArgumentNullException(nameof(context));
41+
}
42+
43+
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
44+
context.EnableConcurrentExecution();
45+
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.PropertyDeclaration);
46+
}
47+
48+
private void AnalyzeNode(SyntaxNodeAnalysisContext context)
49+
{
50+
if (context.Node is not PropertyDeclarationSyntax propertyDeclaration)
51+
{
52+
return;
53+
}
54+
55+
var isAutoProperty = propertyDeclaration.ExpressionBody == null && (propertyDeclaration.AccessorList?.Accessors.All(a => a.Body == null && a.ExpressionBody == null) != false);
56+
var hasCorrectModifiers = propertyDeclaration.Modifiers.Any(SyntaxKind.PublicKeyword) && !propertyDeclaration.Modifiers.Any(SyntaxKind.StaticKeyword);
57+
var doesNotHavePrivateSetOrInternalSet = propertyDeclaration.AccessorList?.Accessors.Any(a => a.Modifiers.Any(SyntaxKind.PrivateKeyword) || a.Modifiers.Any(SyntaxKind.InternalKeyword)) == false;
58+
var namesToIgnore = new List<string> { "ReactiveCommand", "ReactiveProperty", "ViewModelActivator" };
59+
var isNotIgnored = !namesToIgnore.Any(n => propertyDeclaration.Type.ToString().Contains(n));
60+
61+
if (isAutoProperty && hasCorrectModifiers && doesNotHavePrivateSetOrInternalSet && isNotIgnored)
62+
{
63+
var diagnostic = Diagnostic.Create(PropertyToReactiveFieldRule, propertyDeclaration.GetLocation());
64+
context.ReportDiagnostic(diagnostic);
65+
}
66+
}
67+
}

0 commit comments

Comments
 (0)