-
Notifications
You must be signed in to change notification settings - Fork 4.2k
[API Proposal]: RegisterPreCompilationSourceOutput #83089
Description
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
PreCompilationkind, 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.