Skip to content

Commit 7a8930d

Browse files
authored
Add Field Attribute For Partial Properties (#218)
* Add Field Attribute For Partial Properties * Rollback Copyright update
1 parent c677fef commit 7a8930d

File tree

5 files changed

+65
-2
lines changed

5 files changed

+65
-2
lines changed

src/ReactiveUI.SourceGenerators.Execute/TestViewModel.cs

+1
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ public TestViewModel()
236236
/// The partial property test.
237237
/// </value>
238238
[Reactive]
239+
[field: JsonInclude]
239240
public partial string? PartialPropertyTest { get; set; }
240241

241242
/// <summary>

src/ReactiveUI.SourceGenerators.Roslyn/Core/Extensions/ContextExtensions.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ internal static void GetForwardedAttributes(
6565
// Only look for attribute lists explicitly targeting the (generated) property. Roslyn will normally emit a
6666
// CS0657 warning (invalid target), but that is automatically suppressed by a dedicated diagnostic suppressor
6767
// that recognizes uses of this target specifically to support [ObservableAsProperty].
68-
if (attributeList.Target?.Identifier is not SyntaxToken(SyntaxKind.PropertyKeyword))
68+
if (attributeList.Target?.Identifier is not SyntaxToken(SyntaxKind.PropertyKeyword) && attributeList.Target?.Identifier is not SyntaxToken(SyntaxKind.FieldKeyword))
6969
{
7070
continue;
7171
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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.Immutable;
7+
using Microsoft.CodeAnalysis;
8+
using Microsoft.CodeAnalysis.CSharp;
9+
using Microsoft.CodeAnalysis.CSharp.Syntax;
10+
using Microsoft.CodeAnalysis.Diagnostics;
11+
using ReactiveUI.SourceGenerators.Extensions;
12+
using ReactiveUI.SourceGenerators.Helpers;
13+
using static ReactiveUI.SourceGenerators.Diagnostics.SuppressionDescriptors;
14+
15+
namespace ReactiveUI.SourceGenerators.Diagnostics.Suppressions
16+
{
17+
/// <summary>
18+
/// ReactiveCommand Attribute With Field Or Property Target Diagnostic Suppressor.
19+
/// </summary>
20+
/// <seealso cref="DiagnosticSuppressor" />
21+
[DiagnosticAnalyzer(LanguageNames.CSharp)]
22+
public sealed class ReactiveAttributeWithFieldTargetDiagnosticSuppressor : DiagnosticSuppressor
23+
{
24+
/// <inheritdoc/>
25+
public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions => ImmutableArray.Create(FieldOrPropertyAttributeListForReactiveProperty);
26+
27+
/// <inheritdoc/>
28+
public override void ReportSuppressions(SuppressionAnalysisContext context)
29+
{
30+
foreach (var diagnostic in context.ReportedDiagnostics)
31+
{
32+
var syntaxNode = diagnostic.Location.SourceTree?.GetRoot(context.CancellationToken).FindNode(diagnostic.Location.SourceSpan);
33+
34+
// Check that the target is effectively [field:] or [property:] over a method declaration, which is the case we're looking for
35+
if (syntaxNode is AttributeTargetSpecifierSyntax { Parent.Parent: PropertyDeclarationSyntax propertyDeclaration, Identifier: SyntaxToken(SyntaxKind.FieldKeyword) })
36+
{
37+
var semanticModel = context.GetSemanticModel(syntaxNode.SyntaxTree);
38+
39+
// Get the method symbol from the first variable declaration
40+
ISymbol? declaredSymbol = semanticModel.GetDeclaredSymbol(propertyDeclaration, context.CancellationToken);
41+
42+
// Check if the method is using [Reactive], in which case we should suppress the warning
43+
if (declaredSymbol is IPropertySymbol propertySymbol &&
44+
semanticModel.Compilation.GetTypeByMetadataName(AttributeDefinitions.ReactiveAttributeType) is INamedTypeSymbol reactiveSymbol &&
45+
propertySymbol.HasAttributeWithType(reactiveSymbol))
46+
{
47+
context.ReportSuppression(Suppression.Create(FieldOrPropertyAttributeListForReactiveProperty, diagnostic));
48+
}
49+
}
50+
}
51+
}
52+
}
53+
}

src/ReactiveUI.SourceGenerators.Roslyn/Reactive/ReactiveGenerator.Execute.cs

+9-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,15 @@ public sealed partial class ReactiveGenerator
8686
out var isReferenceTypeOrUnconstraindTypeParameter,
8787
out var includeMemberNotNullOnSetAccessor);
8888

89-
ImmutableArray<string> forwardedAttributesString = [];
89+
var propertyDeclaration = (PropertyDeclarationSyntax)context.TargetNode;
90+
91+
context.GetForwardedAttributes(
92+
builder,
93+
propertySymbol,
94+
propertyDeclaration.AttributeLists,
95+
token,
96+
out var forwardedAttributesString);
97+
9098
token.ThrowIfCancellationRequested();
9199

92100
// Get the containing type info

src/ReactiveUI.SourceGenerators.Roslyn/ReactiveUI.SourceGenerators.Roslyn.projitems

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Suppressions\OAPHMethodDoesNotNeedToBeStaticDiagnosticSuppressor.cs" />
4141
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Suppressions\ObservableAsPropertyAttributeWithFieldNeverReadDiagnosticSuppressor.cs" />
4242
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Suppressions\ReactiveAttributeWithPropertyTargetDiagnosticSuppressor.cs" />
43+
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Suppressions\ReactiveAttributeWithFieldTargetDiagnosticSuppressor.cs" />
4344
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Suppressions\ReactiveCommandAttributeWithFieldOrPropertyTargetDiagnosticSuppressor.cs" />
4445
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Suppressions\ReactiveCommandMethodDoesNotNeedToBeStaticDiagnosticSuppressor.cs" />
4546
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Suppressions\ReactiveFieldDoesNotNeedToBeReadOnlyDiagnosticSuppressor.cs" />

0 commit comments

Comments
 (0)