Use new ISyntaxContextReceiver to filter candidate symbols
MODIFY
WalletWasabi.Fluent.Generators/AutoNotifyGenerator.cs
WalletWasabi.Fluent.Generators/AutoNotifyGenerator.cs
@@ -11,6 +11,10 @@ namespace WalletWasabi.Fluent.Generators
[Generator]
public class AutoNotifyGenerator : ISourceGenerator
{
+ private const string AutoNotifyAttributeDisplayString = "WalletWasabi.Fluent.AutoNotifyAttribute";
+
+ private const string ReactiveObjectDisplayString = "ReactiveUI.ReactiveObject";
+
private const string AttributeText = @"// <auto-generated />
#nullable enable
using System;
@@ -57,52 +61,26 @@ namespace WalletWasabi.Fluent
public void Execute(GeneratorExecutionContext context)
{
- if (context.SyntaxReceiver is not SyntaxReceiver receiver)
+ if (context.SyntaxContextReceiver is not SyntaxReceiver receiver)
{
return;
}
- var options = (context.Compilation as CSharpCompilation)?.SyntaxTrees[0].Options as CSharpParseOptions;
- var compilation = context.Compilation.AddSyntaxTrees(
- CSharpSyntaxTree.ParseText(SourceText.From(ModifierText, Encoding.UTF8), options),
- CSharpSyntaxTree.ParseText(SourceText.From(AttributeText, Encoding.UTF8), options));
-
- var attributeSymbol = compilation.GetTypeByMetadataName("WalletWasabi.Fluent.AutoNotifyAttribute");
+ var attributeSymbol = context.Compilation.GetTypeByMetadataName(AutoNotifyAttributeDisplayString);
if (attributeSymbol is null)
{
return;
}
- var notifySymbol = compilation.GetTypeByMetadataName("ReactiveUI.ReactiveObject");
+ var notifySymbol = context.Compilation.GetTypeByMetadataName(ReactiveObjectDisplayString);
if (notifySymbol is null)
{
return;
}
- List<IFieldSymbol> fieldSymbols = new ();
-
- foreach (FieldDeclarationSyntax field in receiver.CandidateFields)
- {
- var semanticModel = compilation.GetSemanticModel(field.SyntaxTree);
-
- foreach (VariableDeclaratorSyntax variable in field.Declaration.Variables)
- {
- if (semanticModel.GetDeclaredSymbol(variable) is not IFieldSymbol fieldSymbol)
- {
- continue;
- }
-
- var attributes = fieldSymbol.GetAttributes();
- if (attributes.Any(ad => ad?.AttributeClass?.Equals(attributeSymbol, SymbolEqualityComparer.Default) ?? false))
- {
- fieldSymbols.Add(fieldSymbol);
- }
- }
- }
-
// TODO: https://github.com/dotnet/roslyn/issues/49385
#pragma warning disable RS1024
- var groupedFields = fieldSymbols.GroupBy(f => f.ContainingType);
+ var groupedFields = receiver.FieldSymbols.GroupBy(f => f.ContainingType);
#pragma warning restore RS1024
foreach (var group in groupedFields)
@@ -263,16 +241,28 @@ namespace {namespaceName}
}
}
- private class SyntaxReceiver : ISyntaxReceiver
+ private class SyntaxReceiver : ISyntaxContextReceiver
{
- public List<FieldDeclarationSyntax> CandidateFields { get; } = new List<FieldDeclarationSyntax>();
+ public List<IFieldSymbol> FieldSymbols { get; } = new();
- public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
+ public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
{
- if (syntaxNode is FieldDeclarationSyntax fieldDeclarationSyntax
+ if (context.Node is FieldDeclarationSyntax fieldDeclarationSyntax
&& fieldDeclarationSyntax.AttributeLists.Count > 0)
{
- CandidateFields.Add(fieldDeclarationSyntax);
+ foreach (VariableDeclaratorSyntax variable in fieldDeclarationSyntax.Declaration.Variables)
+ {
+ if (context.SemanticModel.GetDeclaredSymbol(variable) is not IFieldSymbol fieldSymbol)
+ {
+ continue;
+ }
+
+ var attributes = fieldSymbol.GetAttributes();
+ if (attributes.Any(ad => ad?.AttributeClass?.ToDisplayString() == AutoNotifyAttributeDisplayString))
+ {
+ FieldSymbols.Add(fieldSymbol);
+ }
+ }
}
}
}

MODIFY
WalletWasabi.Fluent.Generators/NavigationMetaDataGenerator.cs
WalletWasabi.Fluent.Generators/NavigationMetaDataGenerator.cs
@@ -11,6 +11,12 @@ namespace WalletWasabi.Fluent.Generators
[Generator]
public class NavigationMetaDataGenerator : ISourceGenerator
{
+ private const string NavigationMetaDataAttributeDisplayString = "WalletWasabi.Fluent.NavigationMetaDataAttribute";
+
+ private const string NavigationMetaDataDisplayString = "WalletWasabi.Fluent.NavigationMetaData";
+
+ private const string RoutableViewModelDisplayString = "WalletWasabi.Fluent.ViewModels.Navigation.RoutableViewModel";
+
private const string AttributeText = @"// <auto-generated />
using System;
@@ -93,47 +99,26 @@ namespace WalletWasabi.Fluent
public void Execute(GeneratorExecutionContext context)
{
- if (context.SyntaxReceiver is not SyntaxReceiver receiver)
+ if (context.SyntaxContextReceiver is not SyntaxReceiver receiver)
{
return;
}
- var options = (context.Compilation as CSharpCompilation)?.SyntaxTrees[0].Options as CSharpParseOptions;
- var compilation = context.Compilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(SourceText.From(AttributeText, Encoding.UTF8), options));
-
- var attributeSymbol = compilation.GetTypeByMetadataName("WalletWasabi.Fluent.NavigationMetaDataAttribute");
+ var attributeSymbol = context.Compilation.GetTypeByMetadataName(NavigationMetaDataAttributeDisplayString);
if (attributeSymbol is null)
{
return;
}
- var metadataSymbol = compilation.GetTypeByMetadataName("WalletWasabi.Fluent.NavigationMetaData");
+ var metadataSymbol = context.Compilation.GetTypeByMetadataName(NavigationMetaDataDisplayString);
if (metadataSymbol is null)
{
return;
}
- List<INamedTypeSymbol> namedTypeSymbols = new();
-
- foreach (var candidateClass in receiver.CandidateClasses)
- {
- var semanticModel = compilation.GetSemanticModel(candidateClass.SyntaxTree);
- var namedTypeSymbol = semanticModel.GetDeclaredSymbol(candidateClass);
- if (namedTypeSymbol is null)
- {
- continue;
- }
-
- var attributes = namedTypeSymbol.GetAttributes();
- if (attributes.Any(ad => ad?.AttributeClass?.Equals(attributeSymbol, SymbolEqualityComparer.Default) ?? false))
+ foreach (var namedTypeSymbol in receiver.NamedTypeSymbols)
{
- namedTypeSymbols.Add(namedTypeSymbol);
- }
- }
-
- foreach (var namedTypeSymbol in namedTypeSymbols)
- {
- var classSource = ProcessClass(compilation, namedTypeSymbol, attributeSymbol, metadataSymbol);
+ var classSource = ProcessClass(context.Compilation, namedTypeSymbol, attributeSymbol, metadataSymbol);
if (classSource is not null)
{
context.AddSource($"{namedTypeSymbol.Name}_NavigationMetaData.cs", SourceText.From(classSource, Encoding.UTF8));
@@ -193,7 +178,7 @@ namespace {namespaceName}
source.AppendLine($@" public static void Register(RoutableViewModel createInstance) => NavigationManager.Register(MetaData, createInstance);");
source.AppendLine($@" public override string Title {{get => MetaData.Title; protected set {{}} }} ");
- var routeableClass = compilation.GetTypeByMetadataName("WalletWasabi.Fluent.ViewModels.Navigation.RoutableViewModel");
+ var routeableClass = compilation.GetTypeByMetadataName(RoutableViewModelDisplayString);
if (routeableClass is { })
{
@@ -236,16 +221,26 @@ namespace {namespaceName}
return source.ToString();
}
- private class SyntaxReceiver : ISyntaxReceiver
+ private class SyntaxReceiver : ISyntaxContextReceiver
{
- public List<ClassDeclarationSyntax> CandidateClasses { get; } = new();
+ public List<INamedTypeSymbol> NamedTypeSymbols { get; } = new();
- public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
+ public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
{
- if (syntaxNode is ClassDeclarationSyntax classDeclarationSyntax
+ if (context.Node is ClassDeclarationSyntax classDeclarationSyntax
&& classDeclarationSyntax.AttributeLists.Count > 0)
{
- CandidateClasses.Add(classDeclarationSyntax);
+ var namedTypeSymbol = context.SemanticModel.GetDeclaredSymbol(classDeclarationSyntax);
+ if (namedTypeSymbol is null)
+ {
+ return;
+ }
+
+ var attributes = namedTypeSymbol.GetAttributes();
+ if (attributes.Any(ad => ad?.AttributeClass?.ToDisplayString() == NavigationMetaDataAttributeDisplayString))
+ {
+ NamedTypeSymbols.Add(namedTypeSymbol);
+ }
}
}
}

MODIFY
WalletWasabi.Fluent.Generators/StaticViewLocatorGenerator.cs
WalletWasabi.Fluent.Generators/StaticViewLocatorGenerator.cs
@@ -11,6 +11,12 @@ namespace WalletWasabi.Fluent.Generators
[Generator]
public class StaticViewLocatorGenerator : ISourceGenerator
{
+ private const string StaticViewLocatorAttributeDisplayString = "WalletWasabi.Fluent.StaticViewLocatorAttribute";
+
+ private const string ViewModelSuffix = "ViewModel";
+
+ private const string ViewSuffix = "View";
+
private const string AttributeText = @"// <auto-generated />
using System;
@@ -35,49 +41,20 @@ namespace WalletWasabi.Fluent
public void Execute(GeneratorExecutionContext context)
{
- if (context.SyntaxReceiver is not SyntaxReceiver receiver)
+ if (context.SyntaxContextReceiver is not SyntaxReceiver receiver)
{
return;
}
- var options = (context.Compilation as CSharpCompilation)?.SyntaxTrees[0].Options as CSharpParseOptions;
- var compilation = context.Compilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(SourceText.From(AttributeText, Encoding.UTF8), options));
-
- var attributeSymbol = compilation.GetTypeByMetadataName("WalletWasabi.Fluent.StaticViewLocatorAttribute");
+ var attributeSymbol = context.Compilation.GetTypeByMetadataName(StaticViewLocatorAttributeDisplayString);
if (attributeSymbol is null)
{
return;
}
- List<INamedTypeSymbol> namedTypeSymbolLocators = new();
- List<INamedTypeSymbol> namedTypeSymbolViewModels = new();
-
- foreach (var candidateClass in receiver.CandidateClasses)
- {
- var semanticModel = compilation.GetSemanticModel(candidateClass.SyntaxTree);
- var namedTypeSymbol = semanticModel.GetDeclaredSymbol(candidateClass);
- if (namedTypeSymbol is null)
- {
- continue;
- }
-
- var attributes = namedTypeSymbol.GetAttributes();
- if (attributes.Any(ad => ad?.AttributeClass?.Equals(attributeSymbol, SymbolEqualityComparer.Default) ?? false))
- {
- namedTypeSymbolLocators.Add(namedTypeSymbol);
- }
- else if (namedTypeSymbol.Name.EndsWith("ViewModel"))
- {
- if (!namedTypeSymbol.IsAbstract)
- {
- namedTypeSymbolViewModels.Add(namedTypeSymbol);
- }
- }
- }
-
- foreach (var namedTypeSymbol in namedTypeSymbolLocators)
+ foreach (var namedTypeSymbol in receiver.NamedTypeSymbolLocators)
{
- var classSource = ProcessClass(compilation, namedTypeSymbol, namedTypeSymbolViewModels);
+ var classSource = ProcessClass(context.Compilation, namedTypeSymbol, receiver.NamedTypeSymbolViewModels);
if (classSource is not null)
{
context.AddSource($"{namedTypeSymbol.Name}_StaticViewLocator.cs", SourceText.From(classSource, Encoding.UTF8));
@@ -121,7 +98,7 @@ namespace {namespaceNameLocator}
{
string namespaceNameViewModel = namedTypeSymbolViewModel.ContainingNamespace.ToDisplayString();
string classNameViewModel = $"{namespaceNameViewModel}.{namedTypeSymbolViewModel.ToDisplayString(format)}";
- string classNameView = classNameViewModel.Replace("ViewModel", "View");
+ string classNameView = classNameViewModel.Replace(ViewModelSuffix, ViewSuffix);
var classNameViewSymbol = compilation.GetTypeByMetadataName(classNameView);
if (classNameViewSymbol is null || classNameViewSymbol.BaseType?.Equals(userControlViewSymbol, SymbolEqualityComparer.Default) != true)
@@ -141,15 +118,34 @@ namespace {namespaceNameLocator}
return source.ToString();
}
- private class SyntaxReceiver : ISyntaxReceiver
+ private class SyntaxReceiver : ISyntaxContextReceiver
{
- public List<ClassDeclarationSyntax> CandidateClasses { get; } = new();
+ public List<INamedTypeSymbol> NamedTypeSymbolLocators { get; } = new();
- public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
+ public List<INamedTypeSymbol> NamedTypeSymbolViewModels { get; } = new();
+
+ public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
{
- if (syntaxNode is ClassDeclarationSyntax classDeclarationSyntax)
+ if (context.Node is ClassDeclarationSyntax classDeclarationSyntax)
{
- CandidateClasses.Add(classDeclarationSyntax);
+ var namedTypeSymbol = context.SemanticModel.GetDeclaredSymbol(classDeclarationSyntax);
+ if (namedTypeSymbol is null)
+ {
+ return;
+ }
+
+ var attributes = namedTypeSymbol.GetAttributes();
+ if (attributes.Any(ad => ad?.AttributeClass?.ToDisplayString() == StaticViewLocatorAttributeDisplayString))
+ {
+ NamedTypeSymbolLocators.Add(namedTypeSymbol);
+ }
+ else if (namedTypeSymbol.Name.EndsWith(ViewModelSuffix))
+ {
+ if (!namedTypeSymbol.IsAbstract)
+ {
+ NamedTypeSymbolViewModels.Add(namedTypeSymbol);
+ }
+ }
}
}
}

Use new ISyntaxContextReceiver to filter candidate symbols