I have following Execute method in my source generator. I can get all the properties (x.Item1.Members.AsEnumerable()...) of my class. But I can't manage to get the properties of the base class too. How can I list all properties of the base class?
private static void Execute(Compilation compilation, ImmutableArray<(ClassDeclarationSyntax, AttributeData)> classes, SourceProductionContext context)
{
foreach (var (x, i) in classes.Select((x, i) => (x, i)))
{
TypedConstant aggregateParam = x.Item2.ConstructorArguments[0];
if (aggregateParam.Kind == TypedConstantKind.Primitive &&
aggregateParam.Value is string fullyQualifiedAggregateName)
{
var aggregateName = "Aggregate";
var props = x.Item1.Members.AsEnumerable().Where(o => o.IsKind(SyntaxKind.PropertyDeclaration));
var sb = new StringBuilder();
context.AddSource(
$"generated_{aggregateName}_{i}.g.cs",
sb.ToString());
}
}
}
Update
public void Initialize(IncrementalGeneratorInitializationContext context)
{
IncrementalValuesProvider<(ClassDeclarationSyntax, AttributeData)> classDeclarations = context.SyntaxProvider
.ForAttributeWithMetadataName(
EventApplyAttribute,
predicate: static (node, _) => node is ClassDeclarationSyntax,
transform: static (ctx, ct) => GetSemanticTargetForGeneration(ctx, ct))
.Where(m => m.Item1 is not null && m.Item2 is not null);
IncrementalValueProvider<(Compilation, ImmutableArray<(ClassDeclarationSyntax, AttributeData)>)> compilationAndClasses
= context.CompilationProvider.Combine(classDeclarations.Collect());
context.RegisterSourceOutput(compilationAndClasses,
static (spc, source) => Execute(source.Item1, source.Item2, spc));
}
private static (ClassDeclarationSyntax, AttributeData) GetSemanticTargetForGeneration(GeneratorAttributeSyntaxContext context, CancellationToken ct)
{
if (context.TargetNode is not ClassDeclarationSyntax classDeclaration)
{
return (null, null);
}
AttributeData? attribute = context.Attributes.FirstOrDefault(a => a.AttributeClass?.Name == "EventApplyAttribute");
return (classDeclaration, attribute);
}
Really, you want the semantic model for this - not the syntax model. And you don't need to wait for Execute
for that - when using incremental generators, presumably you're using CreateSyntaxProvider
, where you provide a pre-filter on the syntax model, and a transform that is more flexible. In that transform, you can access the semantic model. This is advantageous for performance, as it allows any processing in your transform step to be cached as long as the relevant syntax tree is not invalidated.
During transform, you can get the semantic model via ctx.SemanticModel
- you probably want ctx.SemanticModel.GetDeclaredSymbol(...)
on the ClassDeclarationSyntax
. Since this is declaring a type, the symbol it gives you should be an ITypeSymbol
and very likely a INamedTypeSymbol
. This means you have now full access to the type data, including .BaseType
.
But: at either transform or execute, you can get the semantic model
You can also get the semantic model at Execute
via state.Compilation.GetSemanticModel(...)
passing in the syntax-tree for a relevant syntax-node, but: if you're doing that, you're probably repeating a lot of work, adversely impacting IDE performance.