-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Description
Background and Motivation
Today, we have an extension method IIncrementalGenerator.AsSourceGenerator() that allows the user to wrap an IIncrementalGenerator as an ISourceGenerator so that it can be passed to other generator APIs that only accept an ISourceGenerator.
The ISourceGenerator returned from this call is opaque, and generally not useful for anything other than passing to the driver.
We also have an API ISourceGenerator GeneratorRunResult.Generator { get; } that returns the instance of the generator that was passed to the driver. If a user passes an IIncrementalGenerator via .AsSourceGenerator() this will be the opaque wrapped type, which is of little use.
We have another extension method ISourceGenerator.GetGeneratorType() that returns the type of the generator in the opaque wrapper, but there is no way of getting the actual instance.
Proposed API
namespace Microsoft.CodeAnalysis
{
public static class GeneratorExtensions
{
+ public static IIncrementalGenerator AsIncrementalGenerator(this ISourceGenerator generator)
+ public static Type GetGeneratorType(this IIncrementalGenerator generator)
}
}This would allow a user to effectively 'unwrap' the opaque ISourceGenerator and get back the original instance of the IIncrementalGenerator that was wrapped.
If a user calls AsIncrementalGenerator on a non-wrapped ISourceGenerator they will get back an opaque type that can be used anywhere the compiler expects and IIncrementalGenerator.
We extend the GetGeneratorType extension method so that you can similarly get at the underlying type without needing to know if it's wrapped or not.
Usage Examples
var generator = new MyIncrementalGenerator();
var driver = new CSharpGeneratorDriver(generator.AsSourceGenerator());
var result = driver.RunGenerators().GetRunResult();
var opaqueWrapper = result.Results[0].Generator;
var underlyingType = opaqueWrapper.GetGeneratorType();
Assert.Equal(typeof(MyIncrementalGenerator), underlyingType);
var unwrapped = opaqueWrapper.AsIncrementalGenerator();
Assert.IsSame(generator, unwrapped);These effectively give the user the inverse of the current AsSourceGenerator API.
Internally, we actually already do this the other way around: we unwrap any wrapped IIncrementalGenerators and actually wrap ISourceGenerators in an adaptor. While we don't want to necessarily prescribe the implementation details of the API it seems expected that the following would hold for these APIs:
IIncrementalGenerator --AsSourceGenerator()--> OpaqueSourceGenerator --AsIncrementalGenerator()--> IIncrementalGenerator
And conversely
ISourceGenerator --AsIncrementalGenerator()--> OpaqueIncrementalGenerator --AsSourceGenerator()--> ISourceGenerator
Alternative Designs
We could expose the existing SourceGeneratorWrapper type that represents the OpaqueSourceGenerator. A user could then just look at the .Generator property to get the instance of the incremental generator.
This opens us up to maintaining the way the generator internally works, although it hasn't changed in years, and we could presumably just shim it if we needed to. But it does require the user to special case by looking for the specific type, rather than just calling AsIncrementalGenerator() and carrying on.