Skip to content

Commit 2acbff6

Browse files
committed
Switched to SyntaxReceiver in remaining generators
1 parent 9a543e8 commit 2acbff6

File tree

5 files changed

+113
-30
lines changed

5 files changed

+113
-30
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Collections.Generic;
6+
using Microsoft.CodeAnalysis;
7+
using Microsoft.CodeAnalysis.CSharp;
8+
using Microsoft.CodeAnalysis.CSharp.Syntax;
9+
using Microsoft.Toolkit.Mvvm.SourceGenerators.Extensions;
10+
11+
namespace Microsoft.Toolkit.Mvvm.SourceGenerators
12+
{
13+
/// <inheritdoc cref="ObservableValidatorValidateAllPropertiesGenerator"/>
14+
public sealed partial class ObservableValidatorValidateAllPropertiesGenerator
15+
{
16+
/// <summary>
17+
/// An <see cref="ISyntaxContextReceiver"/> that selects candidate nodes to process.
18+
/// </summary>
19+
private sealed class SyntaxReceiver : ISyntaxContextReceiver
20+
{
21+
/// <summary>
22+
/// The list of info gathered during exploration.
23+
/// </summary>
24+
private readonly List<INamedTypeSymbol> gatheredInfo = new();
25+
26+
/// <summary>
27+
/// Gets the collection of gathered info to process.
28+
/// </summary>
29+
public IReadOnlyCollection<INamedTypeSymbol> GatheredInfo => this.gatheredInfo;
30+
31+
/// <inheritdoc/>
32+
public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
33+
{
34+
if (context.Node is ClassDeclarationSyntax classDeclaration &&
35+
context.SemanticModel.GetDeclaredSymbol(classDeclaration) is INamedTypeSymbol classSymbol &&
36+
!classSymbol.IsGenericType &&
37+
context.SemanticModel.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.ComponentModel.ObservableValidator") is INamedTypeSymbol validatorSymbol &&
38+
classSymbol.InheritsFrom(validatorSymbol))
39+
{
40+
this.gatheredInfo.Add(classSymbol);
41+
}
42+
}
43+
}
44+
}
45+
}

Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/ObservableValidatorValidateAllPropertiesGenerator.cs

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,31 +21,26 @@ namespace Microsoft.Toolkit.Mvvm.SourceGenerators
2121
/// A source generator for properties validation without relying on compiled LINQ expressions.
2222
/// </summary>
2323
[Generator]
24-
public sealed class ObservableValidatorValidateAllPropertiesGenerator : ISourceGenerator
24+
public sealed partial class ObservableValidatorValidateAllPropertiesGenerator : ISourceGenerator
2525
{
2626
/// <inheritdoc/>
2727
public void Initialize(GeneratorInitializationContext context)
2828
{
29+
context.RegisterForSyntaxNotifications(static () => new SyntaxReceiver());
2930
}
3031

3132
/// <inheritdoc/>
3233
public void Execute(GeneratorExecutionContext context)
3334
{
34-
// Get the symbol for the ObservableValidator type and the ValidationAttribute type
35-
INamedTypeSymbol
36-
validatorSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.ComponentModel.ObservableValidator")!,
37-
validationSymbol = context.Compilation.GetTypeByMetadataName("System.ComponentModel.DataAnnotations.ValidationAttribute")!;
35+
// Get the syntax receiver with the candidate nodes
36+
if (context.SyntaxContextReceiver is not SyntaxReceiver syntaxReceiver ||
37+
syntaxReceiver.GatheredInfo.Count == 0)
38+
{
39+
return;
40+
}
3841

39-
// Find all the class symbols inheriting from ObservableValidator, that are not generic
40-
IEnumerable<INamedTypeSymbol> classSymbols =
41-
from syntaxTree in context.Compilation.SyntaxTrees
42-
let semanticModel = context.Compilation.GetSemanticModel(syntaxTree)
43-
from classDeclaration in syntaxTree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>()
44-
let classSymbol = semanticModel.GetDeclaredSymbol(classDeclaration)
45-
where
46-
classSymbol is { IsGenericType: false } &&
47-
classSymbol.InheritsFrom(validatorSymbol)
48-
select classSymbol;
42+
// Get the symbol for the ValidationAttribute type
43+
INamedTypeSymbol validationSymbol = context.Compilation.GetTypeByMetadataName("System.ComponentModel.DataAnnotations.ValidationAttribute")!;
4944

5045
// Prepare the attributes to add to the first class declaration
5146
AttributeListSyntax[] classAttributes = new[]
@@ -60,7 +55,7 @@ from classDeclaration in syntaxTree.GetRoot().DescendantNodes().OfType<ClassDecl
6055
Literal("This type is not intended to be used directly by user code"))))))
6156
};
6257

63-
foreach (INamedTypeSymbol classSymbol in classSymbols)
58+
foreach (INamedTypeSymbol classSymbol in syntaxReceiver.GatheredInfo)
6459
{
6560
// Create a static method to validate all properties in a given class.
6661
// This code takes a class symbol and produces a compilation unit as follows:

Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/TransitiveMembersGenerator.SyntaxReceiver.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
3434
if (context.Node is ClassDeclarationSyntax classDeclaration &&
3535
classDeclaration.AttributeLists.Count > 0 &&
3636
context.SemanticModel.GetDeclaredSymbol(classDeclaration) is INamedTypeSymbol classSymbol &&
37-
classSymbol.GetAttributes().FirstOrDefault(static a => a.AttributeClass?.ToDisplayString() == typeof(TAttribute).FullName) is AttributeData attributeData &&
37+
context.SemanticModel.Compilation.GetTypeByMetadataName(typeof(TAttribute).FullName) is INamedTypeSymbol attributeSymbol &&
38+
classSymbol.GetAttributes().FirstOrDefault(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, attributeSymbol)) is AttributeData attributeData &&
3839
attributeData.ApplicationSyntaxReference is SyntaxReference syntaxReference &&
3940
syntaxReference.GetSyntax() is AttributeSyntax attributeSyntax)
4041
{
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using Microsoft.CodeAnalysis;
8+
using Microsoft.CodeAnalysis.CSharp;
9+
using Microsoft.CodeAnalysis.CSharp.Syntax;
10+
11+
namespace Microsoft.Toolkit.Mvvm.SourceGenerators
12+
{
13+
/// <inheritdoc cref="IMessengerRegisterAllGenerator"/>
14+
public sealed partial class IMessengerRegisterAllGenerator
15+
{
16+
/// <summary>
17+
/// An <see cref="ISyntaxContextReceiver"/> that selects candidate nodes to process.
18+
/// </summary>
19+
private sealed class SyntaxReceiver : ISyntaxContextReceiver
20+
{
21+
/// <summary>
22+
/// The list of info gathered during exploration.
23+
/// </summary>
24+
private readonly List<INamedTypeSymbol> gatheredInfo = new();
25+
26+
/// <summary>
27+
/// Gets the collection of gathered info to process.
28+
/// </summary>
29+
public IReadOnlyCollection<INamedTypeSymbol> GatheredInfo => this.gatheredInfo;
30+
31+
/// <inheritdoc/>
32+
public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
33+
{
34+
if (context.Node is ClassDeclarationSyntax classDeclaration &&
35+
context.SemanticModel.GetDeclaredSymbol(classDeclaration) is INamedTypeSymbol classSymbol &&
36+
!classSymbol.IsGenericType &&
37+
context.SemanticModel.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.Messaging.IRecipient`1") is INamedTypeSymbol iRecipientSymbol &&
38+
classSymbol.AllInterfaces.Any(i => SymbolEqualityComparer.Default.Equals(i.OriginalDefinition, iRecipientSymbol)))
39+
{
40+
this.gatheredInfo.Add(classSymbol);
41+
}
42+
}
43+
}
44+
}
45+
}

Microsoft.Toolkit.Mvvm.SourceGenerators/Messaging/IMessengerRegisterAllGenerator.cs

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,30 +20,27 @@ namespace Microsoft.Toolkit.Mvvm.SourceGenerators
2020
/// A source generator for message registration without relying on compiled LINQ expressions.
2121
/// </summary>
2222
[Generator]
23-
public sealed class IMessengerRegisterAllGenerator : ISourceGenerator
23+
public sealed partial class IMessengerRegisterAllGenerator : ISourceGenerator
2424
{
2525
/// <inheritdoc/>
2626
public void Initialize(GeneratorInitializationContext context)
2727
{
28+
context.RegisterForSyntaxNotifications(static () => new SyntaxReceiver());
2829
}
2930

3031
/// <inheritdoc/>
3132
public void Execute(GeneratorExecutionContext context)
3233
{
34+
// Get the syntax receiver with the candidate nodes
35+
if (context.SyntaxContextReceiver is not SyntaxReceiver syntaxReceiver ||
36+
syntaxReceiver.GatheredInfo.Count == 0)
37+
{
38+
return;
39+
}
40+
3341
// Get the symbol for the IRecipient<T> interface type
3442
INamedTypeSymbol iRecipientSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.Messaging.IRecipient`1")!;
3543

36-
// Find all the class symbols with at least one IRecipient<T> usage, that are not generic
37-
IEnumerable<INamedTypeSymbol> classSymbols =
38-
from syntaxTree in context.Compilation.SyntaxTrees
39-
let semanticModel = context.Compilation.GetSemanticModel(syntaxTree)
40-
from classDeclaration in syntaxTree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>()
41-
let classSymbol = semanticModel.GetDeclaredSymbol(classDeclaration)
42-
where
43-
classSymbol is { IsGenericType: false } &&
44-
classSymbol.AllInterfaces.Any(i => SymbolEqualityComparer.Default.Equals(i.OriginalDefinition, iRecipientSymbol))
45-
select classSymbol;
46-
4744
// Prepare the attributes to add to the first class declaration
4845
AttributeListSyntax[] classAttributes = new[]
4946
{
@@ -57,7 +54,7 @@ from classDeclaration in syntaxTree.GetRoot().DescendantNodes().OfType<ClassDecl
5754
Literal("This type is not intended to be used directly by user code"))))))
5855
};
5956

60-
foreach (INamedTypeSymbol classSymbol in classSymbols)
57+
foreach (INamedTypeSymbol classSymbol in syntaxReceiver.GatheredInfo)
6158
{
6259
// Create a static method to register all messages for a given recipient type.
6360
// There are two versions that are generated: a non-generic one doing the registration

0 commit comments

Comments
 (0)