2

I'm trying to test source generator. Generator:

[Generator]
public class CustomGenerator : ISourceGenerator
{
    public void Initialize(GeneratorInitializationContext context) { }

    public void Execute(GeneratorExecutionContext context)
    {
        //...
    }
}

Test code:

              Compilation inputCompilation = CreateCompilation(@"
namespace MyCode
{
    public class Program
    {
        public static void Main(string[] args)
        {
        }
    } 
}
");   
            var generator = new CustomGenerator();
             
            var driver = CSharpGeneratorDriver.Create(generator);
             
            driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out var outputCompilation, out var diagnostics);

            static Compilation CreateCompilation(string source)
                => CSharpCompilation.Create("compilation",
                    new[] { CSharpSyntaxTree.ParseText(source) },
                    new[] { MetadataReference.CreateFromFile(typeof(Binder).GetTypeInfo().Assembly.Location) },
                    new CSharpCompilationOptions(OutputKind.ConsoleApplication));

The problem is that context.SyntaxReceiver always is null, how to fix that?

1
  • 1
    You need to first register a syntax receiver in your Initialize method, by calling context.RegisterForSyntaxNotifications. See the cookbook. Commented Sep 9, 2021 at 14:55

1 Answer 1

1

Let's inspect the definition of the GeneratorExecutionContext.SyntaxReceiver Property:

namespace Microsoft.CodeAnalysis
{
    public readonly struct GeneratorExecutionContext
    {
        // Summary:
        //     If the generator registered an Microsoft.CodeAnalysis.ISyntaxReceiver during
        //     initialization, this will be the instance created for this generation pass.
        public ISyntaxReceiver? SyntaxReceiver { get; }
    }
}

We see that the GeneratorExecutionContext.SyntaxReceiver Property is declared as nullable, meaning that there are cases where it is indeed expected to be null. The documentation comment reveals that we first have to register our ISyntaxReceiver in ISourceGenerator.Initialize.

As canton7 has pointed out, we first need to register it via the GeneratorInitializationContext.RegisterForSyntaxNotifications Method:

[Generator]
public class CustomGenerator : ISourceGenerator
{
    public void Initialize(GeneratorInitializationContext context)
    {
        context.RegisterForSyntaxNotifications(static () => new CustomReceiver());
    }

    public void Execute(GeneratorExecutionContext context)
    {
        Debug.Assert(context.SyntaxReceiver is not null, $"No {nameof(ISyntaxReceiver)} registerd via {nameof(GeneratorInitializationContext.RegisterForSyntaxNotifications)}.");

        if (context.SyntaxReceiver is not CustomReceiver receiver)
        {
            return;
        }

        Debug.Assert(receiver is not null);

        //...
    }
}

internal sealed class CustomReceiver : ISyntaxReceiver
{
    public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
    {
    }
}

Keep in mind to avoid expensive operations within the ISyntaxReceiver because it does not support cancellation unlike the Generator itself.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.