11// Licensed to the .NET Foundation under one or more agreements.
22// The .NET Foundation licenses this file to you under the MIT license.
33
4+ using System ;
45using System . Collections . Generic ;
56using System . Collections . Immutable ;
7+ using System . Linq ;
68using System . Threading ;
79using Microsoft . AspNetCore . App . Analyzers . Infrastructure ;
810using Microsoft . CodeAnalysis ;
@@ -12,12 +14,12 @@ namespace Microsoft.Extensions.Validation;
1214
1315public sealed partial class ValidationsGenerator : IIncrementalGenerator
1416{
15- internal static bool ShouldTransformSymbolWithAttribute ( SyntaxNode syntaxNode , CancellationToken cancellationToken )
17+ internal static bool ShouldTransformSymbolWithAttribute ( SyntaxNode syntaxNode , CancellationToken _ )
1618 {
1719 return syntaxNode is ClassDeclarationSyntax or RecordDeclarationSyntax ;
1820 }
1921
20- internal ImmutableArray < ValidatableType > TransformValidatableTypeWithAttribute ( GeneratorAttributeSyntaxContext context , CancellationToken cancellationToken )
22+ internal ImmutableArray < ValidatableType > TransformValidatableTypeWithAttribute ( GeneratorAttributeSyntaxContext context , CancellationToken _ )
2123 {
2224 var validatableTypes = new HashSet < ValidatableType > ( ValidatableTypeComparer . Instance ) ;
2325 List < ITypeSymbol > visitedTypes = [ ] ;
@@ -28,4 +30,88 @@ internal ImmutableArray<ValidatableType> TransformValidatableTypeWithAttribute(G
2830 }
2931 return [ ] ;
3032 }
33+
34+ internal static bool ShouldTransformSymbolWithValidatableTypeAttribute ( SyntaxNode syntaxNode , CancellationToken _ )
35+ {
36+ // Only process class and record declarations
37+ if ( syntaxNode is not ( ClassDeclarationSyntax or RecordDeclarationSyntax ) )
38+ {
39+ return false ;
40+ }
41+
42+ // Check if the type has any attribute that could be ValidatableTypeAttribute
43+ var typeDeclaration = ( TypeDeclarationSyntax ) syntaxNode ;
44+ return typeDeclaration . AttributeLists
45+ . SelectMany ( al => al . Attributes )
46+ . Any ( IsValidatableTypeAttribute ) ;
47+ }
48+
49+ internal ImmutableArray < ValidatableType > TransformValidatableTypeWithValidatableTypeAttribute ( GeneratorSyntaxContext context , CancellationToken cancellationToken )
50+ {
51+ if ( context . Node is not ( ClassDeclarationSyntax or RecordDeclarationSyntax ) )
52+ {
53+ return [ ] ;
54+ }
55+
56+ var typeSymbol = context . SemanticModel . GetDeclaredSymbol ( context . Node , cancellationToken ) as ITypeSymbol ;
57+ if ( typeSymbol == null )
58+ {
59+ return [ ] ;
60+ }
61+
62+ // Check if the type has a ValidatableTypeAttribute (framework or auto-generated)
63+ if ( ! HasValidatableTypeAttribute ( typeSymbol ) )
64+ {
65+ return [ ] ;
66+ }
67+
68+ var validatableTypes = new HashSet < ValidatableType > ( ValidatableTypeComparer . Instance ) ;
69+ List < ITypeSymbol > visitedTypes = [ ] ;
70+ var wellKnownTypes = WellKnownTypes . GetOrCreate ( context . SemanticModel . Compilation ) ;
71+
72+ if ( TryExtractValidatableType ( typeSymbol , wellKnownTypes , ref validatableTypes , ref visitedTypes ) )
73+ {
74+ return [ ..validatableTypes ] ;
75+ }
76+ return [ ] ;
77+ }
78+
79+ private static bool IsValidatableTypeAttribute ( AttributeSyntax attribute )
80+ {
81+ var name = attribute . Name . ToString ( ) ;
82+ return name is "ValidatableType" or "ValidatableTypeAttribute" ||
83+ name . EndsWith ( ".ValidatableType" , StringComparison . Ordinal ) ||
84+ name . EndsWith ( ".ValidatableTypeAttribute" , StringComparison . Ordinal ) ;
85+ }
86+
87+ private static bool HasValidatableTypeAttribute ( ITypeSymbol typeSymbol )
88+ {
89+ return typeSymbol . GetAttributes ( ) . Any ( attr =>
90+ {
91+ var attributeClass = attr . AttributeClass ;
92+ if ( attributeClass == null )
93+ {
94+ return false ;
95+ }
96+
97+ var name = attributeClass . Name ;
98+ var fullName = attributeClass . ToDisplayString ( ) ;
99+
100+ // Check for framework attribute
101+ if ( fullName == "Microsoft.Extensions.Validation.ValidatableTypeAttribute" )
102+ {
103+ return true ;
104+ }
105+
106+ // Check for auto-generated attribute (any namespace)
107+ if ( name == "ValidatableTypeAttribute" )
108+ {
109+ // Additional check: ensure it's marked with [Embedded] to confirm it's auto-generated
110+ return attributeClass . GetAttributes ( ) . Any ( embeddedAttr =>
111+ embeddedAttr . AttributeClass ? . ToDisplayString ( ) == "Microsoft.CodeAnalysis.EmbeddedAttribute" ) ;
112+ }
113+
114+ return false ;
115+ } ) ;
116+ }
31117}
0 commit comments