๐ WAYPOINT LESSON
Incremental generators
โญโญโญ advancedโณ 16 min read๐ Lesson 185 of 265
IIncrementalGenerator pipelines: caching, equality, and not re-running the world per keystroke.
Incremental generators
The classic ISourceGenerator re-runs everything on every change โ fine
for tiny tools, brutal for IDEs. IIncrementalGenerator instead builds a
pipeline of transformations the compiler tracks by value equality: a
keystroke re-runs only the steps whose inputs actually changed.
var provider = ctx.SyntaxProvider.ForAttributeWithMetadataName(...)
.Select(static (name, _) => name.ToUpperInvariant()) // pure transform
.Collect(); // gather into ImmutableArray
ctx.RegisterSourceOutput(provider, static (spc, names) => { /* emit once */ });
The performance contract, from the Roslyn team's own guidance:
- Transformations must be pure and cheap โ they may run many times; their results are cached by equality.
ForAttributeWithMetadataNamefirst. It is a heavily-optimized provider for the dominant pattern (find types with attribute X); hand- rolledCreateSyntaxProviderpredicates run more often and do more work.Collect()deliberately โ collecting defeats incrementalism for the steps after it; do per-item work before collecting, then emit once.- Lambda hygiene: all pipeline lambdas must be
static(no captured variables) โ capturing silently breaks the caching contract.
When generators go wrong in production, it is almost always a pipeline that recomputes a whole-project model per keystroke โ the fix is always "push work later, make steps cheap, cache by value".
โก Now practice
Ready to CodeGenerator drillsPipeline building, generated-code contracts, generator diagnostics, and compile-verification.
2 challenges ยท ยท ~45 min