55using System ;
66using System . Collections . Generic ;
77using System . Diagnostics . CodeAnalysis ;
8+ using System . Diagnostics . Contracts ;
89using System . IO ;
910using System . Linq ;
1011using System . Reflection ;
@@ -25,7 +26,7 @@ namespace Microsoft.Toolkit.Mvvm.SourceGenerators
2526 /// A source generator for the <see cref="ObservableObjectAttribute"/> type.
2627 /// </summary>
2728 /// <typeparam name="TAttribute">The type of the source attribute to look for.</typeparam>
28- public abstract class TransitiveMembersGenerator < TAttribute > : ISourceGenerator
29+ public abstract partial class TransitiveMembersGenerator < TAttribute > : ISourceGenerator
2930 where TAttribute : Attribute
3031 {
3132 /// <summary>
@@ -36,63 +37,62 @@ public abstract class TransitiveMembersGenerator<TAttribute> : ISourceGenerator
3637 /// <inheritdoc/>
3738 public void Initialize ( GeneratorInitializationContext context )
3839 {
40+ context . RegisterForSyntaxNotifications ( static ( ) => new SyntaxReceiver ( ) ) ;
3941 }
4042
4143 /// <inheritdoc/>
4244 public void Execute ( GeneratorExecutionContext context )
4345 {
44- // Get the symbol for the current target attribute
45- INamedTypeSymbol attributeSymbol = context . Compilation . GetTypeByMetadataName ( typeof ( TAttribute ) . FullName ) ! ;
46-
47- // Find all the target attribute usages
48- IEnumerable < AttributeSyntax > attributes =
49- from syntaxTree in context . Compilation . SyntaxTrees
50- let semanticModel = context . Compilation . GetSemanticModel ( syntaxTree )
51- from attribute in syntaxTree . GetRoot ( ) . DescendantNodes ( ) . OfType < AttributeSyntax > ( )
52- let typeInfo = semanticModel . GetTypeInfo ( attribute )
53- where SymbolEqualityComparer . Default . Equals ( typeInfo . Type , attributeSymbol )
54- select attribute ;
55-
56- SyntaxTree ? sourceSyntaxTree = null ;
57-
58- foreach ( AttributeSyntax attribute in attributes )
46+ // Get the syntax receiver with the candidate nodes
47+ if ( context . SyntaxContextReceiver is not SyntaxReceiver syntaxReceiver ||
48+ syntaxReceiver . GatheredInfo . Count == 0 )
5949 {
60- // Load the source syntax tree if needed
61- if ( sourceSyntaxTree is null )
62- {
63- string filename = $ "Microsoft.Toolkit.Mvvm.SourceGenerators.EmbeddedResources.{ typeof ( TAttribute ) . Name . Replace ( "Attribute" , string . Empty ) } .cs";
64-
65- Stream stream = Assembly . GetExecutingAssembly ( ) . GetManifestResourceStream ( filename ) ;
66- StreamReader reader = new ( stream ) ;
67-
68- string observableObjectSource = reader . ReadToEnd ( ) ;
50+ return ;
51+ }
6952
70- sourceSyntaxTree = CSharpSyntaxTree . ParseText ( observableObjectSource ) ;
71- }
53+ // Load the syntax tree with the members to generate
54+ SyntaxTree sourceSyntaxTree = LoadSourceSyntaxTree ( ) ;
7255
73- ClassDeclarationSyntax classDeclaration = attribute . FirstAncestorOrSelf < ClassDeclarationSyntax > ( ) ! ;
74- SemanticModel semanticModel = context . Compilation . GetSemanticModel ( classDeclaration . SyntaxTree ) ;
75- INamedTypeSymbol classDeclarationSymbol = semanticModel . GetDeclaredSymbol ( classDeclaration ) ! ;
76- AttributeData attributeData = classDeclarationSymbol . GetAttributes ( ) . First ( a => a . ApplicationSyntaxReference ? . GetSyntax ( ) == attribute ) ;
56+ foreach ( var info in syntaxReceiver . GatheredInfo )
57+ {
58+ SemanticModel semanticModel = context . Compilation . GetSemanticModel ( info . Class . SyntaxTree ) ;
59+ INamedTypeSymbol classDeclarationSymbol = semanticModel . GetDeclaredSymbol ( info . Class ) ! ;
7760
78- if ( ! ValidateTargetType ( context , attributeData , classDeclaration , classDeclarationSymbol , out var descriptor ) )
61+ if ( ! ValidateTargetType ( context , info . Data , info . Class , classDeclarationSymbol , out var descriptor ) )
7962 {
80- context . ReportDiagnostic ( descriptor , attribute , classDeclarationSymbol ) ;
63+ context . ReportDiagnostic ( descriptor , info . Attribute , classDeclarationSymbol ) ;
8164
8265 continue ;
8366 }
8467
8568 try
8669 {
87- OnExecute ( context , attributeData , classDeclaration , classDeclarationSymbol , sourceSyntaxTree ) ;
70+ OnExecute ( context , info . Data , info . Class , classDeclarationSymbol , sourceSyntaxTree ) ;
8871 }
8972 catch
9073 {
91- context . ReportDiagnostic ( TargetTypeErrorDescriptor , attribute , classDeclarationSymbol ) ;
74+ context . ReportDiagnostic ( TargetTypeErrorDescriptor , info . Attribute , classDeclarationSymbol ) ;
9275 }
9376 }
9477 }
9578
79+ /// <summary>
80+ /// Loads the source syntax tree for the current generator.
81+ /// </summary>
82+ /// <returns>The syntax tree with the elements to emit in the generated code.</returns>
83+ [ Pure ]
84+ private static SyntaxTree LoadSourceSyntaxTree ( )
85+ {
86+ string filename = $ "Microsoft.Toolkit.Mvvm.SourceGenerators.EmbeddedResources.{ typeof ( TAttribute ) . Name . Replace ( "Attribute" , string . Empty ) } .cs";
87+
88+ Stream stream = Assembly . GetExecutingAssembly ( ) . GetManifestResourceStream ( filename ) ;
89+ StreamReader reader = new ( stream ) ;
90+
91+ string observableObjectSource = reader . ReadToEnd ( ) ;
92+
93+ return CSharpSyntaxTree . ParseText ( observableObjectSource ) ;
94+ }
95+
9696 /// <summary>
9797 /// Processes a given target type.
9898 /// </summary>
0 commit comments