Reputation: 1073
I'm writing a source generator (IIncrementalGenerator
to be specific) and I want to make sure that the caching is correctly responding to changes in the source code.
Compilation
to modify the code that the source generator is listening forModified
or New
state for the run reason (and that the data itself is correct)This would show that the generator is responsive to changes in the source code, otherwise I'm limited to just testing that the generator creates correct source code in the initial compile, not follow up changes.
Here is the core code that I've got for running the generator over multiple iterations:
public static IReadOnlyList<GeneratorDriverRunResult> RunGeneratorIterations<T>(T generator, params Compilation[] codeIterations)
where T : IIncrementalGenerator
{
GeneratorDriverOptions driverOptions = new(IncrementalGeneratorOutputKind.None, true);
ISourceGenerator[] generators = { generator.AsSourceGenerator() };
GeneratorDriver driver = CSharpGeneratorDriver.Create(generators, driverOptions: driverOptions);
List<GeneratorDriverRunResult> runResults = new();
// Run the generation pass
driver = driver.RunGeneratorsAndUpdateCompilation(codeIterations[0], out _, out _);
runResults.Add(driver.GetRunResult());
for (int i = 1; i < codeIterations.Length; i++)
{
driver = driver.RunGenerators(codeIterations[i], CancellationToken.None);
runResults.Add(driver.GetRunResult());
}
Assert.AreEqual(codeIterations.Length, runResults.Count);
return runResults;
}
The issue with this is that when I run tests using the above code itt throws an exception in the second driver.RunGenerators
call:
Unexpected value '(Modified, Added)' of type 'System.ValueTuple'2[[Microsoft.CodeAnalysis.EntryState, Microsoft.CodeAnalysis.EntryState]]
This tells me that I'm not setting up the next compile pass correctly, How do I run a source generator with incrementally changing source code in unit tests?
Small update on this:
It seems that if I tell driverOptions
not to track the generator stages it doesn't crash... but I then can't inspect the actual data in my unit test. I'm beginning to think I'm fighting a compiler bug.
Upvotes: 4
Views: 357
Reputation: 4338
Are you combining with CompilationProvider? Then you might run into this issue.
Here is a workaround from https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.md:
Consider the following (incorrect) combine where the basic inputs are combined, then used to generate some source:
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var compilation = context.CompilationProvider;
var texts = context.AdditionalTextsProvider;
// Don't do this!
var combined = texts.Combine(compilation);
context.RegisterSourceOutput(combined, static (spc, pair) =>
{
var assemblyName = pair.Right.AssemblyName;
// produce source ...
});
Any time the compilation changes, which it will frequently as the user is typing
in the IDE, then RegisterSourceOutput
will get re-run. Instead, look up the
compilation dependant information first, then combine that with the additional
files:
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var assemblyName = context.CompilationProvider.Select(static (c, _) => c.AssemblyName);
var texts = context.AdditionalTextsProvider;
var combined = texts.Combine(assemblyName);
context.RegisterSourceOutput(combined, (spc, pair) =>
{
var assemblyName = pair.Right;
// produce source ...
});
}
Upvotes: 0