Skip to content

Commit 476a163

Browse files
committed
Add RelayCommandAttributeWithFieldOrPropertyTargetDiagnosticSuppressor
1 parent 41100e2 commit 476a163

File tree

4 files changed

+78
-4
lines changed

4 files changed

+78
-4
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Analyzers\FieldWithOrphanedDependentObservablePropertyAttributesAnalyzer.cs" />
4646
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Analyzers\FieldReferenceForObservablePropertyFieldAnalyzer.cs" />
4747
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Analyzers\UnsupportedCSharpLanguageVersionAnalyzer.cs" />
48+
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Suppressors\RelayCommandAttributeWithFieldOrPropertyTargetDiagnosticSuppressor.cs" />
4849
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Suppressors\ObservablePropertyAttributeWithPropertyTargetDiagnosticSuppressor.cs" />
4950
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\DiagnosticDescriptors.cs" />
5051
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\SuppressionDescriptors.cs" />

src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/SuppressionDescriptors.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,18 @@ namespace CommunityToolkit.Mvvm.SourceGenerators.Diagnostics;
1212
internal static class SuppressionDescriptors
1313
{
1414
/// <summary>
15-
/// Gets a <see cref="SuppressionDescriptor"/> for a field using [ObservableProperty] with on attribute list targeting a property.
15+
/// Gets a <see cref="SuppressionDescriptor"/> for a field using [ObservableProperty] with an attribute list targeting a property.
1616
/// </summary>
1717
public static readonly SuppressionDescriptor PropertyAttributeListForObservablePropertyField = new(
1818
id: "MVVMTKSPR0001",
1919
suppressedDiagnosticId: "CS0657",
2020
justification: "Fields using [ObservableProperty] can use [property:] attribute lists to forward attributes to the generated properties");
21+
22+
/// <summary>
23+
/// Gets a <see cref="SuppressionDescriptor"/> for a method using [RelayCommand] with an attribute list targeting a field or property.
24+
/// </summary>
25+
public static readonly SuppressionDescriptor FieldOrPropertyAttributeListForRelayCommandMethod = new(
26+
id: "MVVMTKSPR0002",
27+
suppressedDiagnosticId: "CS0657",
28+
justification: "Methods using [RelayCommand] can use [field:] and [property:] attribute lists to forward attributes to the generated fields and properties");
2129
}

src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Suppressors/ObservablePropertyAttributeWithPropertyTargetDiagnosticSuppressor.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System.Collections.Immutable;
6-
using System.Linq;
6+
using CommunityToolkit.Mvvm.SourceGenerators.Extensions;
77
using Microsoft.CodeAnalysis;
88
using Microsoft.CodeAnalysis.CSharp;
99
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -19,7 +19,7 @@ namespace CommunityToolkit.Mvvm.SourceGenerators;
1919
/// <para>
2020
/// That is, this diagnostic suppressor will suppress the following diagnostic:
2121
/// <code>
22-
/// public class MyViewModel : ObservableObject
22+
/// public partial class MyViewModel : ObservableObject
2323
/// {
2424
/// [ObservableProperty]
2525
/// [property: JsonPropertyName("Name")]
@@ -53,7 +53,7 @@ public override void ReportSuppressions(SuppressionAnalysisContext context)
5353
// Check if the field is using [ObservableProperty], in which case we should suppress the warning
5454
if (declaredSymbol is IFieldSymbol fieldSymbol &&
5555
semanticModel.Compilation.GetTypeByMetadataName("CommunityToolkit.Mvvm.ComponentModel.ObservablePropertyAttribute") is INamedTypeSymbol observablePropertySymbol &&
56-
fieldSymbol.GetAttributes().Select(attribute => attribute.AttributeClass).Contains(observablePropertySymbol, SymbolEqualityComparer.Default))
56+
fieldSymbol.HasAttributeWithType(observablePropertySymbol))
5757
{
5858
context.ReportSuppression(Suppression.Create(PropertyAttributeListForObservablePropertyField, diagnostic));
5959
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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 CommunityToolkit.Mvvm.SourceGenerators.Extensions;
7+
using Microsoft.CodeAnalysis;
8+
using Microsoft.CodeAnalysis.CSharp;
9+
using Microsoft.CodeAnalysis.CSharp.Syntax;
10+
using Microsoft.CodeAnalysis.Diagnostics;
11+
using static CommunityToolkit.Mvvm.SourceGenerators.Diagnostics.SuppressionDescriptors;
12+
13+
namespace CommunityToolkit.Mvvm.SourceGenerators;
14+
15+
/// <summary>
16+
/// <para>
17+
/// A diagnostic suppressor to suppress CS0657 warnings for methods with [RelayCommand] using a [field:] or [property:] attribute list.
18+
/// </para>
19+
/// <para>
20+
/// That is, this diagnostic suppressor will suppress the following diagnostic:
21+
/// <code>
22+
/// public partial class MyViewModel : ObservableObject
23+
/// {
24+
/// [RelayCommand]
25+
/// [field: JsonIgnore]
26+
/// [property: SomeOtherAttribute]
27+
/// private void DoSomething()
28+
/// {
29+
/// }
30+
/// }
31+
/// </code>
32+
/// </para>
33+
/// </summary>
34+
[DiagnosticAnalyzer(LanguageNames.CSharp)]
35+
public sealed class RelayCommandAttributeWithFieldOrPropertyTargetDiagnosticSuppressor : DiagnosticSuppressor
36+
{
37+
/// <inheritdoc/>
38+
public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions => ImmutableArray.Create(FieldOrPropertyAttributeListForRelayCommandMethod);
39+
40+
/// <inheritdoc/>
41+
public override void ReportSuppressions(SuppressionAnalysisContext context)
42+
{
43+
foreach (Diagnostic diagnostic in context.ReportedDiagnostics)
44+
{
45+
SyntaxNode? syntaxNode = diagnostic.Location.SourceTree?.GetRoot(context.CancellationToken).FindNode(diagnostic.Location.SourceSpan);
46+
47+
// Check that the target is effectively [field:] or [property:] over a method declaration, which is the case we're looking for
48+
if (syntaxNode is AttributeTargetSpecifierSyntax { Parent.Parent: MethodDeclarationSyntax methodDeclaration, Identifier: SyntaxToken(SyntaxKind.FieldKeyword or SyntaxKind.PropertyKeyword) })
49+
{
50+
SemanticModel semanticModel = context.GetSemanticModel(syntaxNode.SyntaxTree);
51+
52+
// Get the method symbol from the first variable declaration
53+
ISymbol? declaredSymbol = semanticModel.GetDeclaredSymbol(methodDeclaration, context.CancellationToken);
54+
55+
// Check if the method is using [RelayCommand], in which case we should suppress the warning
56+
if (declaredSymbol is IMethodSymbol methodSymbol &&
57+
semanticModel.Compilation.GetTypeByMetadataName("CommunityToolkit.Mvvm.Input.RelayCommandAttribute") is INamedTypeSymbol relayCommandSymbol &&
58+
methodSymbol.HasAttributeWithType(relayCommandSymbol))
59+
{
60+
context.ReportSuppression(Suppression.Create(FieldOrPropertyAttributeListForRelayCommandMethod, diagnostic));
61+
}
62+
}
63+
}
64+
}
65+
}

0 commit comments

Comments
 (0)