16
16
using static Microsoft . CodeAnalysis . CSharp . SyntaxFactory ;
17
17
using static Microsoft . Toolkit . Mvvm . SourceGenerators . Diagnostics . DiagnosticDescriptors ;
18
18
19
+ #pragma warning disable SA1008
20
+
19
21
namespace Microsoft . Toolkit . Mvvm . SourceGenerators
20
22
{
21
23
/// <summary>
@@ -46,8 +48,10 @@ public void Execute(GeneratorExecutionContext context)
46
48
context . ReportDiagnostic ( Diagnostic . Create ( UnsupportedCSharpLanguageVersionError , null ) ) ;
47
49
}
48
50
49
- // Get the symbol for the ValidationAttribute type
50
- INamedTypeSymbol validationSymbol = context . Compilation . GetTypeByMetadataName ( "System.ComponentModel.DataAnnotations.ValidationAttribute" ) ! ;
51
+ // Get the symbol for the required attributes
52
+ INamedTypeSymbol
53
+ validationSymbol = context . Compilation . GetTypeByMetadataName ( "System.ComponentModel.DataAnnotations.ValidationAttribute" ) ! ,
54
+ observablePropertySymbol = context . Compilation . GetTypeByMetadataName ( "Microsoft.Toolkit.Mvvm.ComponentModel.ObservablePropertyAttribute" ) ! ;
51
55
52
56
// Prepare the attributes to add to the first class declaration
53
57
AttributeListSyntax [ ] classAttributes = new [ ]
@@ -145,14 +149,14 @@ public void Execute(GeneratorExecutionContext context)
145
149
Parameter ( Identifier ( "obj" ) ) . WithType ( PredefinedType ( Token ( SyntaxKind . ObjectKeyword ) ) ) )
146
150
. WithBody ( Block (
147
151
LocalDeclarationStatement (
148
- VariableDeclaration ( IdentifierName ( "var" ) ) // Cannot Token(SyntaxKind.VarKeyword) here (throws an ArgumentException)
152
+ VariableDeclaration ( IdentifierName ( "var" ) ) // Cannot use Token(SyntaxKind.VarKeyword) here (throws an ArgumentException)
149
153
. AddVariables (
150
154
VariableDeclarator ( Identifier ( "instance" ) )
151
155
. WithInitializer ( EqualsValueClause (
152
156
CastExpression (
153
157
IdentifierName ( classSymbol . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) ) ,
154
158
IdentifierName ( "obj" ) ) ) ) ) ) )
155
- . AddStatements ( EnumerateValidationStatements ( classSymbol , validationSymbol ) . ToArray ( ) ) ) ,
159
+ . AddStatements ( EnumerateValidationStatements ( classSymbol , validationSymbol , observablePropertySymbol ) . ToArray ( ) ) ) ,
156
160
ReturnStatement ( IdentifierName ( "ValidateAllProperties" ) ) ) ) ) ) )
157
161
. NormalizeWhitespace ( )
158
162
. ToFullString ( ) ;
@@ -166,28 +170,47 @@ public void Execute(GeneratorExecutionContext context)
166
170
}
167
171
168
172
/// <summary>
169
- /// Gets a sequence of statements to validate declared properties.
173
+ /// Gets a sequence of statements to validate declared properties (including generated ones) .
170
174
/// </summary>
171
175
/// <param name="classSymbol">The input <see cref="INamedTypeSymbol"/> instance to process.</param>
172
176
/// <param name="validationSymbol">The type symbol for the <c>ValidationAttribute</c> type.</param>
177
+ /// <param name="observablePropertySymbol">The type symbol for the <c>ObservablePropertyAttribute</c> type.</param>
173
178
/// <returns>The sequence of <see cref="StatementSyntax"/> instances to validate declared properties.</returns>
174
179
[ Pure ]
175
- private static IEnumerable < StatementSyntax > EnumerateValidationStatements ( INamedTypeSymbol classSymbol , INamedTypeSymbol validationSymbol )
180
+ private static IEnumerable < StatementSyntax > EnumerateValidationStatements ( INamedTypeSymbol classSymbol , INamedTypeSymbol validationSymbol , INamedTypeSymbol observablePropertySymbol )
176
181
{
177
- foreach ( var propertySymbol in classSymbol . GetMembers ( ) . OfType < IPropertySymbol > ( ) )
182
+ foreach ( var memberSymbol in classSymbol . GetMembers ( ) )
178
183
{
179
- if ( propertySymbol . IsIndexer )
184
+ if ( memberSymbol is not ( IPropertySymbol { IsIndexer : false } or IFieldSymbol ) )
180
185
{
181
186
continue ;
182
187
}
183
188
184
- ImmutableArray < AttributeData > attributes = propertySymbol . GetAttributes ( ) ;
189
+ ImmutableArray < AttributeData > attributes = memberSymbol . GetAttributes ( ) ;
185
190
191
+ // Also include fields that are annotated with [ObservableProperty]. This is necessary because
192
+ // all generators run in an undefined order and looking at the same original compilation, so the
193
+ // current one wouldn't be able to see generated properties from other generators directly.
194
+ if ( memberSymbol is IFieldSymbol &&
195
+ ! attributes . Any ( a => SymbolEqualityComparer . Default . Equals ( a . AttributeClass , observablePropertySymbol ) ) )
196
+ {
197
+ continue ;
198
+ }
199
+
200
+ // Skip the current member if there are no validation attributes applied to it
186
201
if ( ! attributes . Any ( a => a . AttributeClass ? . InheritsFrom ( validationSymbol ) == true ) )
187
202
{
188
203
continue ;
189
204
}
190
205
206
+ // Get the target property name either directly or matching the generated one
207
+ string propertyName = memberSymbol switch
208
+ {
209
+ IPropertySymbol propertySymbol => propertySymbol . Name ,
210
+ IFieldSymbol fieldSymbol => ObservablePropertyGenerator . GetGeneratedPropertyName ( fieldSymbol ) ,
211
+ _ => throw new InvalidOperationException ( "Invalid symbol type" )
212
+ } ;
213
+
191
214
// This enumerator produces a sequence of statements as follows:
192
215
//
193
216
// __ObservableValidatorHelper.ValidateProperty(instance, instance.<PROPERTY_0>, nameof(instance.<PROPERTY_0>));
@@ -207,14 +230,14 @@ private static IEnumerable<StatementSyntax> EnumerateValidationStatements(INamed
207
230
MemberAccessExpression (
208
231
SyntaxKind . SimpleMemberAccessExpression ,
209
232
IdentifierName ( "instance" ) ,
210
- IdentifierName ( propertySymbol . Name ) ) ) ,
233
+ IdentifierName ( propertyName ) ) ) ,
211
234
Argument (
212
235
InvocationExpression ( IdentifierName ( "nameof" ) )
213
236
. AddArgumentListArguments ( Argument (
214
237
MemberAccessExpression (
215
238
SyntaxKind . SimpleMemberAccessExpression ,
216
239
IdentifierName ( "instance" ) ,
217
- IdentifierName ( propertySymbol . Name ) ) ) ) ) ) ) ;
240
+ IdentifierName ( propertyName ) ) ) ) ) ) ) ;
218
241
}
219
242
}
220
243
}
0 commit comments