[API Proposal]: RegisterPreCompilationSourceOutput
- Dominant language
- C#
- Stars
- 20.7k
- Forks
- 4.3k
- PR merge metrics
- PR metrics pending
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: https://github.com/dotnet/roslyn/pull/83088
## Proposed API
```diff
namespace Microsoft.CodeAnalysis
{
public readonly partial struct IncrementalGeneratorInitializationContext
{
+ public void RegisterPreCompilationSourceOutput(IncrementalValueProvider source, Action action);
+
+ public void RegisterPreCompilationSourceOutput(IncrementalValuesProvider source, Action action);
}
+ public readonly struct PreCompilationSourceProductionContext
+ {
+ ///
+ /// Adds source code in the form of a to the compilation.
+ ///
+ public void AddSource(string hintName, string source);
+
+ ///
+ /// Adds a to the compilation.
+ ///
+ public void AddSource(string hintName, SourceText sourceText);
+
+ ///
+ /// A that can be checked to see if the generation should be cancelled.
+ ///
+ 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
```csharp
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:
```csharp
// 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.
Contributor guide
Assessment
This issue has not been assessed yet.