Skip to content

.AsIncrementalGenerator extension method #74039

@chsienki

Description

@chsienki

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.

Metadata

Metadata

Assignees

Labels

Area-CompilersConcept-APIThis issue involves adding, removing, clarification, or modification of an API.Feature Requestapi-approvedAPI was approved in API review, it can be implemented

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions