Skip to content

Commit d85bbf4

Browse files
committed
Add ObservablePropertyAttributeWithPropertyTargetDiagnosticSuppressor
1 parent f818268 commit d85bbf4

File tree

2 files changed

+71
-0
lines changed

2 files changed

+71
-0
lines changed

CommunityToolkit.Mvvm.SourceGenerators/CommunityToolkit.Mvvm.SourceGenerators.projitems

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
<Compile Include="$(MSBuildThisFileDirectory)ComponentModel\TransitiveMembersGenerator.Execute.cs" />
5353
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Analyzers\FieldWithOrphanedDependentObservablePropertyAttributesAnalyzer.cs" />
5454
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Analyzers\UnsupportedCSharpLanguageVersionAnalyzer.cs" />
55+
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Suppressors\ObservablePropertyAttributeWithPropertyTargetDiagnosticSuppressor.cs" />
5556
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\DiagnosticDescriptors.cs" />
5657
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\DiagnosticExtensions.cs" />
5758
<Compile Include="$(MSBuildThisFileDirectory)Extensions\AttributeDataExtensions.cs" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Collections.Immutable;
6+
using System.Linq;
7+
using Microsoft.CodeAnalysis;
8+
using Microsoft.CodeAnalysis.CSharp;
9+
using Microsoft.CodeAnalysis.CSharp.Syntax;
10+
using Microsoft.CodeAnalysis.Diagnostics;
11+
12+
namespace CommunityToolkit.Mvvm.SourceGenerators;
13+
14+
/// <summary>
15+
/// <para>
16+
/// A diagnostic suppressor to suppress CS0657 warnings for fields with [ObservableProperty] using a [property:] attribute list.
17+
/// </para>
18+
/// <para>
19+
/// That is, this diagnostic suppressor will suppress the following diagnostic:
20+
/// <code>
21+
/// public class MyViewModel : ObservableObject
22+
/// {
23+
/// [ObservableProperty]
24+
/// [property: JsonPropertyName("Name")]
25+
/// private string? name;
26+
/// }
27+
/// </code>
28+
/// </para>
29+
/// </summary>
30+
[DiagnosticAnalyzer(LanguageNames.CSharp)]
31+
public sealed class ObservablePropertyAttributeWithPropertyTargetDiagnosticSuppressor : DiagnosticSuppressor
32+
{
33+
/// <summary>
34+
/// Gets a <see cref="SuppressionDescriptor"/> for a field using [ObservableProperty] with on attribute list targeting a property.
35+
/// </summary>
36+
private static readonly SuppressionDescriptor PropertyAttributeListForObservablePropertyField = new(
37+
id: "MVVMTKSPR0001",
38+
suppressedDiagnosticId: "CS0657",
39+
justification: "Fields using [ObservableProperty] can use [property:] attribute lists to forward attributes to the generated properties");
40+
41+
/// <inheritdoc/>
42+
public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions => ImmutableArray.Create(PropertyAttributeListForObservablePropertyField);
43+
44+
/// <inheritdoc/>
45+
public override void ReportSuppressions(SuppressionAnalysisContext context)
46+
{
47+
foreach (Diagnostic diagnostic in context.ReportedDiagnostics)
48+
{
49+
SyntaxNode? syntaxNode = diagnostic.Location.SourceTree?.GetRoot(context.CancellationToken).FindNode(diagnostic.Location.SourceSpan);
50+
51+
// Check that the target is effectively [property:] over a field declaration with at least one variable, which is the only case we are interested in
52+
if (syntaxNode is AttributeTargetSpecifierSyntax { Parent.Parent: FieldDeclarationSyntax { Declaration.Variables.Count: > 0 } fieldDeclaration } attributeTarget &&
53+
attributeTarget.Identifier.IsKind(SyntaxKind.PropertyKeyword))
54+
{
55+
SemanticModel semanticModel = context.GetSemanticModel(syntaxNode.SyntaxTree);
56+
57+
// Get the field symbol from the first variable declaration
58+
ISymbol? declaredSymbol = semanticModel.GetDeclaredSymbol(fieldDeclaration.Declaration.Variables[0], context.CancellationToken);
59+
60+
// Check if the field is using [ObservableProperty], in which case we should suppress the warning
61+
if (declaredSymbol is IFieldSymbol fieldSymbol &&
62+
semanticModel.Compilation.GetTypeByMetadataName("CommunityToolkit.Mvvm.ComponentModel.ObservablePropertyAttribute") is INamedTypeSymbol observablePropertySymbol &&
63+
fieldSymbol.GetAttributes().Select(attribute => attribute.AttributeClass).Contains(observablePropertySymbol, SymbolEqualityComparer.Default))
64+
{
65+
context.ReportSuppression(Suppression.Create(PropertyAttributeListForObservablePropertyField, diagnostic));
66+
}
67+
}
68+
}
69+
}
70+
}

0 commit comments

Comments
 (0)