Search code examples
c#roslynsourcegenerators

Getting one Roslyn syntax receiver per class


I have a simple source generator defined as

[Generator]
public class NestedObjectGenerator : ISourceGenerator
{
  public void Initialize(GeneratorInitializationContext context)
  {
    context.RegisterForSyntaxNotifications(() => new MySyntaxReceiver());
  }
  public void Execute(GeneratorExecutionContext context)
  {
    var recv = (MySyntaxReceiver)context.SyntaxReceiver;
    ClassDeclarationSyntax cds = recv?.Class;
    if (cds is null) return;

    // use cds here
  }

  private class MySyntaxReceiver : ISyntaxReceiver
  {
    public ClassDeclarationSyntax Class { get; private set; }

    public void OnVisitSyntaxNode(SyntaxNode node)
    {
      if (node is ClassDeclarationSyntax cds &&
        cds.Modifiers.Any(SyntaxKind.PartialKeyword))
      Class = cds;
    }
  }
}

I am running this generator on a file that has several partial classes in it. However, it seems that Execute() gets called only once, and the Class that is included is the class that has the Main() method, even though I have other classes in the file.

So, my question: how can I get Execute() to run once per affected syntax node, i.e., to get it to run once per partial class?


Solution

  • You can change your reciever like this:

    private class MySyntaxReceiver : ISyntaxReceiver
    {
      public List<ClassDeclarationSyntax> ClassCandidates { get; } = new();
    
      public void OnVisitSyntaxNode(SyntaxNode node)
      {
        if (node is ClassDeclarationSyntax cds && cds.Modifiers.Any(SyntaxKind.PartialKeyword))
          ClassCandidates.Add(cds);
      }
    }
    

    And then in the Execute method of the source generator you can loop on all the nodes you collected. I got this approach from a really informative video

    https://www.youtube.com/watch?v=P9Pv5IdinMU

    Here is the github with the demos in the presentation, i learned this from these examples:

    https://github.com/JasonBock/SourceGeneratorDemos