Skip to content

[API Proposal]: RegisterPreCompilationSourceOutput #83089

@chsienki

Description

@chsienki

Background and Motivation

Currently, incremental source generators can only produce source that is added to the compilation after all generator phases have executed. This means that source produced by one generator is not visible to other generators during their compilation-dependent phases, and generators cannot produce "foundational" types that other parts of the pipeline depend on.

This proposal adds a new RegisterPreCompilationSourceOutput API that allows generators to produce source that is added to the compilation before compilation-dependent phases execute. Pre-compilation sources become part of the initial compilation, making them visible to all generators' standard RegisterSourceOutput phases and to the final compilation itself.

This is particularly useful for scenarios where generators need to produce base types, attributes, or other foundational code that other generators (or even the same generator's standard phase) need to reference.

Implementation PR: #83088

Proposed API

namespace Microsoft.CodeAnalysis
{
    public readonly partial struct IncrementalGeneratorInitializationContext
    {
+       public void RegisterPreCompilationSourceOutput<TSource>(IncrementalValueProvider<TSource> source, Action<PreCompilationSourceProductionContext, TSource> action);
+
+       public void RegisterPreCompilationSourceOutput<TSource>(IncrementalValuesProvider<TSource> source, Action<PreCompilationSourceProductionContext, TSource> action);
    }

+   public readonly struct PreCompilationSourceProductionContext
+   {
+       /// <summary>
+       /// Adds source code in the form of a <see cref="string"/> to the compilation.
+       /// </summary>
+       public void AddSource(string hintName, string source);
+
+       /// <summary>
+       /// Adds a <see cref="SourceText"/> to the compilation.
+       /// </summary>
+       public void AddSource(string hintName, SourceText sourceText);
+
+       /// <summary>
+       /// A <see cref="CancellationToken"/> that can be checked to see if the generation should be cancelled.
+       /// </summary>
+       public CancellationToken CancellationToken { get; }
+   }

    [Flags]
    public enum IncrementalGeneratorOutputKind
    {
+       PreCompilation = 0b10000
    }
}

Note: PreCompilationSourceProductionContext intentionally does not include ReportDiagnostic. Pre-compilation outputs cannot report diagnostics. It also does not provide access to the Compilation, as the compilation is not yet available during this phase — attempting to access compilation-dependent inputs (e.g. CompilationProvider, SyntaxProvider) from a pre-compilation output will result in a generator error.

Usage Examples

public void Initialize(IncrementalGeneratorInitializationContext context)
{
    // Produce a base type before the compilation is available
    context.RegisterPreCompilationSourceOutput(context.ParseOptionsProvider, (ctx, parseOptions) =>
    {
        ctx.AddSource("BaseType", "public abstract class GeneratedBase { }");
    });

    // Standard phase can now see the pre-compilation type
    context.RegisterSourceOutput(context.CompilationProvider, (ctx, compilation) =>
    {
        var baseType = compilation.GetTypeByMetadataName("GeneratedBase");
        // baseType is not null — it was added in the pre-compilation phase
    });
}

Pre-compilation outputs from one generator are also visible to other generators' standard phases:

// Generator A
context.RegisterPreCompilationSourceOutput(context.AdditionalTextsProvider, (ctx, file) =>
{
    ctx.AddSource("SharedType", "public class SharedType { }");
});

// Generator B (different generator)
context.RegisterSourceOutput(context.CompilationProvider, (ctx, compilation) =>
{
    var sharedType = compilation.GetTypeByMetadataName("SharedType");
    // sharedType is available — Generator A's pre-compilation output is in the compilation
});

Alternative Designs

Using RegisterPostInitializationOutput instead

RegisterPostInitializationOutput already adds source before compilation, but it only accepts constant source — it cannot depend on any inputs (additional files, parse options, analyzer config, etc.). RegisterPreCompilationSourceOutput fills the gap for source that depends on non-compilation inputs but still needs to be in the initial compilation.

Including ReportDiagnostic on the context

We considered including diagnostic reporting on PreCompilationSourceProductionContext, but decided against it to keep the context minimal. Pre-compilation is an early phase focused purely on producing source; diagnostic reporting can be done in the standard phase.

Risks

  • Pre-compilation adds a second pass to the generator driver execution, which may have performance implications for generators that register pre-compilation outputs. However, the pre-compilation pass only evaluates nodes of the PreCompilation kind, so generators without pre-compilation outputs are unaffected.
  • The API is marked as experimental (RSEXPERIMENTAL007) to allow for iteration on the design before committing to a stable API surface.

Metadata

Metadata

Labels

Area-CompilersConcept-APIThis issue involves adding, removing, clarification, or modification of an API.Feature Requestapi-ready-for-reviewAPI is ready for review, it is NOT ready for implementation
No fields configured for Feature.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions