1
0
mirror of https://github.com/chylex/.NET-Community-Toolkit.git synced 2025-05-15 15:34:03 +02:00

Add warning for unnecessary [NotifyRecipients] attribute

This commit is contained in:
Sergio Pedri 2022-06-01 13:03:36 +02:00
parent 4a3f919f71
commit 0594506c2a
4 changed files with 76 additions and 1 deletions
CommunityToolkit.Mvvm.SourceGenerators
tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests

View File

@ -35,3 +35,4 @@ ### New Rules
MVVMTK0026 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Error | See https://aka.ms/mvvmtoolkit/error
MVVMTK0027 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Error | See https://aka.ms/mvvmtoolkit/error
MVVMTK0028 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Error | See https://aka.ms/mvvmtoolkit/error
MVVMTK0029 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Error | See https://aka.ms/mvvmtoolkit/error

View File

@ -91,6 +91,7 @@ internal static class Execute
ImmutableArray<AttributeInfo>.Builder forwardedAttributes = ImmutableArray.CreateBuilder<AttributeInfo>();
bool notifyRecipients = false;
bool notifyDataErrorInfo = false;
bool hasOrInheritsClassLevelNotifyRecipients = false;
bool hasAnyValidationAttributes = false;
// Track the property changing event for the property, if the type supports it
@ -106,6 +107,7 @@ internal static class Execute
if (TryGetIsNotifyingRecipients(fieldSymbol, out bool isBroadcastTargetValid))
{
notifyRecipients = isBroadcastTargetValid;
hasOrInheritsClassLevelNotifyRecipients = true;
}
// Get the class-level [NotifyDataErrorInfo] setting, if any
@ -125,7 +127,7 @@ internal static class Execute
}
// Check whether the property should also notify recipients
if (TryGetIsNotifyingRecipients(fieldSymbol, attributeData, builder, out isBroadcastTargetValid))
if (TryGetIsNotifyingRecipients(fieldSymbol, attributeData, builder, hasOrInheritsClassLevelNotifyRecipients, out isBroadcastTargetValid))
{
notifyRecipients = isBroadcastTargetValid;
@ -452,16 +454,28 @@ private static bool TryGetIsNotifyingRecipients(IFieldSymbol fieldSymbol, out bo
/// <param name="fieldSymbol">The input <see cref="IFieldSymbol"/> instance to process.</param>
/// <param name="attributeData">The <see cref="AttributeData"/> instance for <paramref name="fieldSymbol"/>.</param>
/// <param name="diagnostics">The current collection of gathered diagnostics.</param>
/// <param name="hasOrInheritsClassLevelNotifyRecipients">Indicates wether the containing type of <paramref name="fieldSymbol"/> has or inherits <c>[NotifyRecipients]</c>.</param>
/// <param name="isBroadcastTargetValid">Whether or not the the property is in a valid target that can notify recipients.</param>
/// <returns>Whether or not the generated property for <paramref name="fieldSymbol"/> used <c>[NotifyRecipients]</c>.</returns>
private static bool TryGetIsNotifyingRecipients(
IFieldSymbol fieldSymbol,
AttributeData attributeData,
ImmutableArray<Diagnostic>.Builder diagnostics,
bool hasOrInheritsClassLevelNotifyRecipients,
out bool isBroadcastTargetValid)
{
if (attributeData.AttributeClass?.HasFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.NotifyRecipientsAttribute") == true)
{
// Emit a diagnostic if the attribute is unnecessary
if (hasOrInheritsClassLevelNotifyRecipients)
{
diagnostics.Add(
UnnecessaryNotifyRecipientsAttributeOnFieldWarning,
fieldSymbol,
fieldSymbol.ContainingType,
fieldSymbol.Name);
}
// If the containing type is valid, track it
if (fieldSymbol.ContainingType.InheritsFromFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableRecipient") ||
fieldSymbol.ContainingType.HasOrInheritsAttributeWithFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableRecipientAttribute"))

View File

@ -459,4 +459,20 @@ internal static class DiagnosticDescriptors
isEnabledByDefault: true,
description: "Types annotated with [NotifyDataErrorInfo] must inherit from ObservableRecipient.",
helpLinkUri: "https://aka.ms/mvvmtoolkit");
/// <summary>
/// Gets a <see cref="DiagnosticDescriptor"/> indicating when <c>[NotifyRecipients]</c> is applied to a field in a class with <c>[NotifyRecipients]</c> used at the class-level.
/// <para>
/// Format: <c>"The field {0}.{1} is annotated with [NotifyRecipients], but that is not needed since its containing type already uses or inherits [NotifyRecipients] at the class-level"</c>.
/// </para>
/// </summary>
public static readonly DiagnosticDescriptor UnnecessaryNotifyRecipientsAttributeOnFieldWarning = new DiagnosticDescriptor(
id: "MVVMTK0029",
title: "Unnecessary [NotifyRecipients] field annotation",
messageFormat: "The field {0}.{1} is annotated with [NotifyRecipients], but that is not needed since its containing type already uses or inherits [NotifyRecipients] at the class-level",
category: typeof(ObservablePropertyGenerator).FullName,
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: "Annotating a field with [NotifyRecipients] is not necessary if the containing type has or inherits [NotifyRecipients] at the class-level.",
helpLinkUri: "https://aka.ms/mvvmtoolkit");
}

View File

@ -1298,6 +1298,50 @@ public partial class SampleViewModel : ObservableObject
VerifyGeneratedDiagnostics<ObservablePropertyGenerator>(source, "MVVMTK0006", "MVVMTK0028");
}
[TestMethod]
public void UnnecessaryNotifyRecipientsWarning_SameType()
{
string source = @"
using CommunityToolkit.Mvvm.ComponentModel;
namespace MyApp
{
[NotifyRecipients]
public partial class MyViewModel : ObservableRecipient
{
[ObservableProperty]
[NotifyRecipients]
public int number;
}
}";
VerifyGeneratedDiagnostics<ObservablePropertyGenerator>(source, "MVVMTK0029");
}
[TestMethod]
public void UnnecessaryNotifyRecipientsWarning_BaseType()
{
string source = @"
using CommunityToolkit.Mvvm.ComponentModel;
namespace MyApp
{
[NotifyRecipients]
public class MyBaseViewModel : ObservableRecipient
{
}
public partial class MyViewModel : MyBaseViewModel
{
[ObservableProperty]
[NotifyRecipients]
public int number;
}
}";
VerifyGeneratedDiagnostics<ObservablePropertyGenerator>(source, "MVVMTK0029");
}
/// <summary>
/// Verifies the output of a source generator.
/// </summary>