Skip to content

Commit b502530

Browse files
authored
Merge pull request #83 from curtisy1/incremental-generator
Use IIncrementalGenerator instead of deprecated ISourceGenerator
2 parents 39c7454 + 98205d5 commit b502530

4 files changed

Lines changed: 86 additions & 113 deletions

File tree

src/AutoFilterer.Generators/AttributeSyntaxReceiver.cs

Lines changed: 0 additions & 26 deletions
This file was deleted.

src/AutoFilterer.Generators/FilterGenerator.cs

Lines changed: 84 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -2,111 +2,130 @@
22
using Microsoft.CodeAnalysis.Text;
33
using System;
44
using System.Collections.Generic;
5+
using System.Collections.Immutable;
56
using System.Linq;
67
using System.Text;
7-
using System.Threading.Tasks;
88
using Microsoft.CodeAnalysis.CSharp.Syntax;
9-
using DotNurse.CodeAnalysis;
10-
using AutoFilterer.Generators.Extensions;
119
using Microsoft.CodeAnalysis.CSharp;
12-
using System.Diagnostics;
1310

1411
namespace AutoFilterer.Generators;
1512

13+
using Extensions;
14+
1615
[Generator]
17-
public class FilterGenerator : ISourceGenerator
16+
public class FilterGenerator : IIncrementalGenerator
1817
{
19-
public void Initialize(GeneratorInitializationContext context)
20-
{
21-
context.RegisterForSyntaxNotifications(() => new AttributeSyntaxReceiver<GenerateAutoFilterAttribute>());
22-
}
23-
24-
public void Execute(GeneratorExecutionContext context)
18+
public void Initialize(IncrementalGeneratorInitializationContext context)
2519
{
26-
if (!(context.SyntaxReceiver is AttributeSyntaxReceiver<GenerateAutoFilterAttribute> receiver))
27-
return;
20+
var classDeclarations = context.SyntaxProvider
21+
.CreateSyntaxProvider(
22+
predicate: static (s, _) => IsSyntaxTargetForGeneration(s),
23+
transform: static (ctx, _) => GetSemanticTargetForGeneration(ctx))
24+
.Where(static m => m is not null);
2825

29-
//if (!Debugger.IsAttached)
30-
//{
31-
// Debugger.Launch();
32-
//}
26+
var compilationAndClasses = context.CompilationProvider.Combine(classDeclarations.Collect());
3327

34-
foreach (var classSyntax in receiver.Classes)
35-
{
36-
var attribute = classSyntax.AttributeLists.SelectMany(sm => sm.Attributes).FirstOrDefault(x => x.Name.ToString().EnsureEndsWith("Attribute").Equals(typeof(GenerateAutoFilterAttribute).Name));
37-
var namespaceParam = attribute.ArgumentList?.Arguments.FirstOrDefault(); // Temprorary... Attribute has only one argument for now.
28+
context.RegisterSourceOutput(compilationAndClasses, (spc, source) => Execute(source.Left, source.Right, spc));
29+
}
3830

39-
var model = context.Compilation.GetSemanticModel(classSyntax.SyntaxTree);
40-
var symbol = model.GetDeclaredSymbol(classSyntax);
41-
var attrs = symbol.GetAttributes();
31+
private static bool IsSyntaxTargetForGeneration(SyntaxNode node)
32+
{
33+
if (node is not ClassDeclarationSyntax classDeclaration || classDeclaration.AttributeLists.Count == 0) {
34+
return false;
35+
}
4236

43-
var realNamespace = GetNamespaceRecursively(symbol.ContainingNamespace);
37+
return classDeclaration.AttributeLists
38+
.SelectMany(al => al.Attributes)
39+
.Any(a => a.Name.ToString().EnsureEndsWith("Attribute").EndsWith(nameof(GenerateAutoFilterAttribute)));
40+
}
4441

45-
var properties = symbol.GetMembers().OfType<IPropertySymbol>()
46-
.Where(x => !x.IsStatic && !x.ContainingType.IsGenericType && x.Kind == SymbolKind.Property);
42+
private static ClassDeclarationSyntax GetSemanticTargetForGeneration(GeneratorSyntaxContext context)
43+
{
44+
var classDeclaration = (ClassDeclarationSyntax)context.Node;
4745

48-
context.AddSource($"{symbol.Name}FilterDto.g.cs",
49-
SourceText.From(GetFilterDtoCode(symbol.Name, properties, namespaceParam?.ToString().Trim('\"') ?? realNamespace), Encoding.UTF8));
46+
foreach (var attributeList in classDeclaration.AttributeLists)
47+
{
48+
foreach (var attribute in attributeList.Attributes)
49+
{
50+
var attributeSymbol = context.SemanticModel.GetSymbolInfo(attribute).Symbol as IMethodSymbol;
51+
var fullName = attributeSymbol?.ContainingType.ToDisplayString() ?? attribute.Name.ToString();
52+
if (fullName.EnsureEndsWith("Attribute").EndsWith(nameof(GenerateAutoFilterAttribute)))
53+
{
54+
return classDeclaration;
55+
}
56+
}
5057
}
58+
59+
return null;
5160
}
5261

53-
private static Dictionary<INamedTypeSymbol, IList<AttributeData>> GetClassAttributePairs(GeneratorExecutionContext context,
54-
SyntaxReceiver receiver)
62+
private static void Execute(Compilation compilation, ImmutableArray<ClassDeclarationSyntax> classes, SourceProductionContext context)
5563
{
56-
var compilation = context.Compilation;
57-
var classSymbols = new Dictionary<INamedTypeSymbol, IList<AttributeData>>();
58-
foreach (var clazz in receiver.CandidateClasses)
64+
if (classes.IsDefaultOrEmpty) {
65+
return;
66+
}
67+
68+
foreach (var classSyntax in classes.Distinct())
5969
{
60-
var model = compilation.GetSemanticModel(clazz.SyntaxTree);
61-
var classSymbol = model.GetDeclaredSymbol(clazz);
62-
var attributes = classSymbol.GetAttributes();
63-
if (attributes.Any(ad => ad.AttributeClass.Name == nameof(GenerateAutoFilterAttribute)))
70+
var model = compilation.GetSemanticModel(classSyntax.SyntaxTree);
71+
if (model.GetDeclaredSymbol(classSyntax) is not { } symbol)
6472
{
65-
classSymbols.Add((INamedTypeSymbol)classSymbol, attributes.ToList());
73+
continue;
6674
}
67-
}
6875

69-
return classSymbols;
76+
var attribute = symbol.GetAttributes()
77+
.FirstOrDefault(a => a.AttributeClass?.Name == nameof(GenerateAutoFilterAttribute));
78+
var targetNamespace = attribute?.ConstructorArguments.FirstOrDefault().Value?.ToString().Trim('\"'); // Temporary... Attribute has only one argument for now.
79+
if (string.IsNullOrEmpty(targetNamespace)) {
80+
targetNamespace = GetNamespaceRecursively(symbol.ContainingNamespace);
81+
}
82+
83+
var properties = symbol.GetMembers()
84+
.OfType<IPropertySymbol>()
85+
.Where(x => !x.IsStatic && !x.IsIndexer && x.Kind == SymbolKind.Property);
86+
87+
var sourceCode = GetFilterDtoCode(symbol.Name, properties, targetNamespace);
88+
89+
context.AddSource($"{symbol.Name}FilterDto.g.cs", SourceText.From(sourceCode, Encoding.UTF8));
90+
}
7091
}
7192

72-
internal string GetFilterDtoCode(string className, IEnumerable<IPropertySymbol> properties,
93+
private static string GetFilterDtoCode(string className, IEnumerable<IPropertySymbol> properties,
7394
string @namespace = null)
7495
{
75-
var start = $@"
76-
using System;
77-
using AutoFilterer;
78-
using AutoFilterer.Attributes;
79-
using AutoFilterer.Types;
80-
81-
namespace {@namespace ?? "AutoFilterer.Filters"}
82-
{{
83-
public partial class {className}Filter : PaginationFilterBase
84-
{{
85-
";
86-
87-
var body = new StringBuilder();
96+
var generatedCode = new StringBuilder();
97+
generatedCode.AppendLine("using System;");
98+
generatedCode.AppendLine("using AutoFilterer.Attributes;");
99+
generatedCode.AppendLine("using AutoFilterer.Types;");
100+
generatedCode.AppendLine();
101+
generatedCode.AppendLine($"namespace {@namespace}");
102+
generatedCode.AppendLine("{");
103+
generatedCode.AppendLine($"\tpublic partial class {className}Filter : PaginationFilterBase");
104+
generatedCode.AppendLine("\t{");
88105

89106
foreach (var property in properties)
90107
{
91-
string propertyType = property.Type.ToDisplayString(NullableFlowState.None);
108+
var propertyType = property.Type.ToDisplayString(NullableFlowState.None);
92109

93-
if (TypeMapping.Mappings.TryGetValue(propertyType, out var mapped))
110+
if (TypeMapping.Mappings.TryGetValue(propertyType, out var mapped)) {
94111
propertyType = mapped;
112+
}
95113

96-
if (propertyType.Equals(nameof(String), StringComparison.InvariantCultureIgnoreCase))
114+
if (property.Type.SpecialType == SpecialType.System_String)
97115
{
98-
body.AppendLine("\t\t[ToLowerContainsComparison]");
116+
generatedCode.AppendLine("\t\t[ToLowerContainsComparison]");
99117
}
100-
body.AppendLine($"\t\tpublic virtual {propertyType} {property.Name} {{ get; set; }}");
101-
}
102118

103-
var end = "\t}\n}";
119+
generatedCode.AppendLine($"\t\tpublic virtual {propertyType} {property.Name} {{ get; set; }}");
120+
}
104121

122+
generatedCode.AppendLine("\t}");
123+
generatedCode.AppendLine("}");
105124

106-
return start + body.ToString() + end;
125+
return generatedCode.ToString();
107126
}
108127

109-
private string GetNamespaceRecursively(INamespaceSymbol symbol)
128+
private static string GetNamespaceRecursively(INamespaceSymbol symbol)
110129
{
111130
if (symbol.ContainingNamespace == null)
112131
{
@@ -115,4 +134,4 @@ private string GetNamespaceRecursively(INamespaceSymbol symbol)
115134

116135
return (GetNamespaceRecursively(symbol.ContainingNamespace) + "." + symbol.Name).Trim('.');
117136
}
118-
}
137+
}

src/AutoFilterer.Generators/GenerateAutoFilterAttribute.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ public GenerateAutoFilterAttribute()
99

1010
public GenerateAutoFilterAttribute(string @namespace)
1111
{
12-
Namespace = @Namespace;
12+
Namespace = @namespace;
1313
}
1414

1515
public string Namespace { get; }
16-
}
16+
}

src/AutoFilterer.Generators/SyntaxReceiver.cs

Lines changed: 0 additions & 20 deletions
This file was deleted.

0 commit comments

Comments
 (0)