Skip to content

Commit 0f0d7bb

Browse files
committed
Enable class-level [NotifyRecipients] usage
1 parent 52e5ebf commit 0f0d7bb

File tree

2 files changed

+51
-9
lines changed

2 files changed

+51
-9
lines changed

CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.Execute.cs

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ internal static class Execute
8989
ImmutableArray<string>.Builder propertyChangingNames = ImmutableArray.CreateBuilder<string>();
9090
ImmutableArray<string>.Builder notifiedCommandNames = ImmutableArray.CreateBuilder<string>();
9191
ImmutableArray<AttributeInfo>.Builder forwardedAttributes = ImmutableArray.CreateBuilder<AttributeInfo>();
92-
bool alsoBroadcastChange = false;
92+
bool notifyRecipients = false;
9393
bool alsoValidateProperty = false;
9494
bool hasAnyValidationAttributes = false;
9595

@@ -102,6 +102,12 @@ internal static class Execute
102102
// The current property is always notified
103103
propertyChangedNames.Add(propertyName);
104104

105+
// Get the class-level [NotifyRecipients] setting, if any
106+
if (TryGetIsNotifyingRecipients(fieldSymbol, out bool isBroadcastTargetValid))
107+
{
108+
notifyRecipients = isBroadcastTargetValid;
109+
}
110+
105111
// Gather attributes info
106112
foreach (AttributeData attributeData in fieldSymbol.GetAttributes())
107113
{
@@ -112,10 +118,10 @@ internal static class Execute
112118
continue;
113119
}
114120

115-
// Check whether the property should also broadcast changes
116-
if (TryGetIsBroadcastingChanges(fieldSymbol, attributeData, builder, out bool isBroadcastTargetValid))
121+
// Check whether the property should also notify recipients
122+
if (TryGetIsNotifyingRecipients(fieldSymbol, attributeData, builder, out isBroadcastTargetValid))
117123
{
118-
alsoBroadcastChange = isBroadcastTargetValid;
124+
notifyRecipients = isBroadcastTargetValid;
119125

120126
continue;
121127
}
@@ -183,7 +189,7 @@ internal static class Execute
183189
propertyChangingNames.ToImmutable(),
184190
propertyChangedNames.ToImmutable(),
185191
notifiedCommandNames.ToImmutable(),
186-
alsoBroadcastChange,
192+
notifyRecipients,
187193
alsoValidateProperty,
188194
forwardedAttributes.ToImmutable());
189195
}
@@ -404,14 +410,45 @@ bool IsCommandNameValidWithGeneratedMembers(string commandName)
404410
}
405411

406412
/// <summary>
407-
/// Checks whether a given generated property should also broadcast changes.
413+
/// Checks whether a given generated property should also notify recipients.
414+
/// </summary>
415+
/// <param name="fieldSymbol">The input <see cref="IFieldSymbol"/> instance to process.</param>
416+
/// <param name="isBroadcastTargetValid">Whether or not the the property is in a valid target that can notify recipients.</param>
417+
/// <returns>Whether or not the generated property for <paramref name="fieldSymbol"/> is in a type annotated with <c>[NotifyRecipients]</c>.</returns>
418+
private static bool TryGetIsNotifyingRecipients(IFieldSymbol fieldSymbol, out bool isBroadcastTargetValid)
419+
{
420+
if (fieldSymbol.ContainingType?.HasOrInheritsAttributeWithFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.NotifyRecipientsAttribute") == true)
421+
{
422+
// If the containing type is valid, track it
423+
if (fieldSymbol.ContainingType.InheritsFromFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableRecipient") ||
424+
fieldSymbol.ContainingType.HasOrInheritsAttributeWithFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableRecipientAttribute"))
425+
{
426+
isBroadcastTargetValid = true;
427+
428+
return true;
429+
}
430+
431+
// Otherwise, ignore the attribute but don't emit a diagnostic.
432+
// The diagnostic for class-level attributes is handled separately.
433+
isBroadcastTargetValid = false;
434+
435+
return true;
436+
}
437+
438+
isBroadcastTargetValid = false;
439+
440+
return false;
441+
}
442+
443+
/// <summary>
444+
/// Checks whether a given generated property should also notify recipients.
408445
/// </summary>
409446
/// <param name="fieldSymbol">The input <see cref="IFieldSymbol"/> instance to process.</param>
410447
/// <param name="attributeData">The <see cref="AttributeData"/> instance for <paramref name="fieldSymbol"/>.</param>
411448
/// <param name="diagnostics">The current collection of gathered diagnostics.</param>
412-
/// <param name="isBroadcastTargetValid">Whether or not the the property is in a valid target that can broadcast changes.</param>
449+
/// <param name="isBroadcastTargetValid">Whether or not the the property is in a valid target that can notify recipients.</param>
413450
/// <returns>Whether or not the generated property for <paramref name="fieldSymbol"/> used <c>[NotifyRecipients]</c>.</returns>
414-
private static bool TryGetIsBroadcastingChanges(
451+
private static bool TryGetIsNotifyingRecipients(
415452
IFieldSymbol fieldSymbol,
416453
AttributeData attributeData,
417454
ImmutableArray<Diagnostic>.Builder diagnostics,

CommunityToolkit.Mvvm/ComponentModel/Attributes/NotifyRecipientsAttribute.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ namespace CommunityToolkit.Mvvm.ComponentModel;
2222
/// }
2323
/// </code>
2424
/// </para>
25+
/// <para>
2526
/// And with this, code analogous to this will be generated:
2627
/// <code>
2728
/// partial class MyViewModel
@@ -33,8 +34,12 @@ namespace CommunityToolkit.Mvvm.ComponentModel;
3334
/// }
3435
/// }
3536
/// </code>
37+
/// </para>
38+
/// <para>
39+
/// This attribute can also be added to a class, and if so it will affect all generated properties in that type and inherited types.
40+
/// </para>
3641
/// </summary>
37-
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
42+
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
3843
public sealed class NotifyRecipientsAttribute : Attribute
3944
{
4045
}

0 commit comments

Comments
 (0)