@@ -73,20 +73,16 @@ public static PropertyInfo GetInfo(IFieldSymbol fieldSymbol, out ImmutableArray<
73
73
// Gather attributes info
74
74
foreach ( AttributeData attributeData in fieldSymbol . GetAttributes ( ) )
75
75
{
76
- if ( TryGatherDependentPropertyChangedNames ( fieldSymbol , attributeData , propertyChangedNames , builder ) )
76
+ // Gather dependent property and command names
77
+ if ( TryGatherDependentPropertyChangedNames ( fieldSymbol , attributeData , propertyChangedNames , builder ) ||
78
+ TryGatherDependentCommandNames ( fieldSymbol , attributeData , notifiedCommandNames , builder ) )
77
79
{
80
+ continue ;
78
81
}
79
- else if ( attributeData . AttributeClass ? . HasFullyQualifiedName ( "global::CommunityToolkit.Mvvm.ComponentModel.AlsoNotifyCanExecuteForAttribute" ) == true )
80
- {
81
- // Add dependent relay command notifications, if needed
82
- foreach ( string commandName in attributeData . GetConstructorArguments < string > ( ) )
83
- {
84
- notifiedCommandNames . Add ( commandName ) ;
85
- }
86
- }
87
- else if ( attributeData . AttributeClass ? . InheritsFrom ( "global::System.ComponentModel.DataAnnotations.ValidationAttribute" ) == true )
82
+
83
+ // Track the current validation attribute, if applicable
84
+ if ( attributeData . AttributeClass ? . InheritsFrom ( "global::System.ComponentModel.DataAnnotations.ValidationAttribute" ) == true )
88
85
{
89
- // Track the current validation attribute
90
86
validationAttributes . Add ( AttributeInfo . From ( attributeData ) ) ;
91
87
}
92
88
}
@@ -156,6 +152,49 @@ private static bool TryGatherDependentPropertyChangedNames(
156
152
return false ;
157
153
}
158
154
155
+ /// <summary>
156
+ /// Tries to gather dependent commands from the given attribute.
157
+ /// </summary>
158
+ /// <param name="fieldSymbol">The input <see cref="IFieldSymbol"/> instance to process.</param>
159
+ /// <param name="attributeData">The <see cref="AttributeData"/> instance for <paramref name="fieldSymbol"/>.</param>
160
+ /// <param name="notifiedCommandNames">The target collection of dependent command names to populate.</param>
161
+ /// <param name="diagnostics">The current collection of gathered diagnostics.</param>
162
+ /// <returns>Whether or not <paramref name="attributeData"/> was an attribute containing any dependent commands.</returns>
163
+ private static bool TryGatherDependentCommandNames (
164
+ IFieldSymbol fieldSymbol ,
165
+ AttributeData attributeData ,
166
+ ImmutableArray < string > . Builder notifiedCommandNames ,
167
+ ImmutableArray < Diagnostic > . Builder diagnostics )
168
+ {
169
+ if ( attributeData . AttributeClass ? . HasFullyQualifiedName ( "global::CommunityToolkit.Mvvm.ComponentModel.AlsoNotifyCanExecuteForAttribute" ) == true )
170
+ {
171
+ foreach ( string ? commandName in attributeData . GetConstructorArguments < string > ( ) )
172
+ {
173
+ // Each target must be a string matching the name of a property from the containing type of the annotated field, and the
174
+ // property must be of type IRelayCommand, or any type that implements that interface (to avoid generating invalid code).
175
+ if ( commandName is { Length : > 0 } &&
176
+ fieldSymbol . ContainingType . GetMembers ( commandName ) . OfType < IPropertySymbol > ( ) . FirstOrDefault ( ) is IPropertySymbol propertySymbol &&
177
+ propertySymbol is INamedTypeSymbol typeSymbol &&
178
+ typeSymbol . InheritsFrom ( "global::CommunityToolkit.Mvvm.Input.IRelayCommand" ) )
179
+ {
180
+ notifiedCommandNames . Add ( commandName ) ;
181
+ }
182
+ else
183
+ {
184
+ diagnostics . Add (
185
+ AlsoNotifyCanExecuteForInvalidTargetError ,
186
+ fieldSymbol ,
187
+ commandName ?? "" ,
188
+ fieldSymbol . ContainingType ) ;
189
+ }
190
+ }
191
+
192
+ return true ;
193
+ }
194
+
195
+ return false ;
196
+ }
197
+
159
198
/// <summary>
160
199
/// Gets a <see cref="CompilationUnitSyntax"/> instance with the cached args for property changing notifications.
161
200
/// </summary>
0 commit comments