Incremental Source Generator
Overview
Use this skill to build or repair Roslyn incremental generators with an emphasis on correctness, cacheability, and IDE performance. Prefer the Roslyn design docs for API rules, then use the Andrew Lock series for concrete patterns, tests, packaging, and failure analysis.
Workflow
Identify the active problem before editing.
Separate design work, correctness bugs, performance regressions, packaging issues, and test gaps. Locate the generator entry points, post-initialization output, supporting models, emitted hint names, and the tests or consuming projects that prove the behavior.
Gather evidence from the current implementation.
Read the generator and nearby data models before changing code. If output is wrong or missing, rebuild with generated-source emission enabled and inspect the emitted .g.cs files. If behavior depends on project configuration, inspect additional files, analyzer config, compiler-visible MSBuild properties, and any marker attributes added during post-initialization.
Read only the references that match the task.
Start with the bundled Roslyn design docs:
references/roslyn/incremental-generators.md
references/roslyn/incremental-generators.cookbook.md
Then pull in the references/andrew-lock-series/ digests by topic. Each one summarises a post in
Andrew Lock's "Creating a source generator" series in original prose, with the canonical URL to
fetch when you need his full treatment:
- Fundamentals and first implementation:
references/andrew-lock-series/00-series-index.md
references/andrew-lock-series/01-creating-an-incremental-generator.md
references/andrew-lock-series/04-customising-generated-code-with-marker-attributes.md
references/andrew-lock-series/05-finding-namespace-and-type-hierarchy.md
references/andrew-lock-series/06-saving-source-generator-output-in-source-control.md
- Testing, packaging, and cacheability:
references/andrew-lock-series/02-testing-an-incremental-generator-with-snapshot-testing.md
references/andrew-lock-series/03-integration-testing-and-packaging.md
references/andrew-lock-series/09-avoiding-performance-pitfalls.md
references/andrew-lock-series/10-testing-cacheable-pipeline-outputs.md
- Advanced marker, configuration, and versioning topics:
references/andrew-lock-series/07-marker-attribute-problem-part-1.md
references/andrew-lock-series/08-marker-attribute-problem-part-2.md
references/andrew-lock-series/11-implementing-an-interceptor.md
references/andrew-lock-series/12-reading-compilation-options-and-csharp-version.md
references/andrew-lock-series/13-accessing-msbuild-properties-and-user-configuration.md
references/andrew-lock-series/14-supporting-multiple-sdk-versions.md
references/andrew-lock-series/15-marker-attribute-problem-in-dotnet-10.md
These digests are self-contained enough to work from offline. When exact detail matters, or the
digest points forward to something it does not cover, fetch the canonical article it links.
Design or repair the pipeline with incrementality in mind.
- Prefer
SyntaxProvider.ForAttributeWithMetadataName(...) when an attribute can drive discovery.
- Keep predicates purely syntactic and cheap. Push semantic work into later transforms.
- Extract compact, equatable models early. Prefer
record or record struct, strings, enums, and wrapper collections with value equality.
- Remove
ISymbol, SyntaxNode, and Location from long-lived models as soon as possible.
- Combine the smallest derived values that solve the problem. Do not push full
Compilation objects or other high-churn inputs farther downstream than necessary.
- Pass cancellation tokens through Roslyn and file APIs, and check them inside expensive loops.
- Generate source text with
StringBuilder or another text writer. Do not build large SyntaxNode trees just to call NormalizeWhitespace.
- Keep output additive. Use post-initialization output for marker attributes and related helper stubs.
- Keep hint names stable and unique.
- Report diagnostics when invalid user inputs need feedback instead of silently skipping the case.
Check common failure modes explicitly.
- Discovery is too broad or uses
CreateSyntaxProvider where an attribute-driven approach is available.
- Models are not value-equatable, so cache hits are lost. Most common:
ImmutableArray<T>, List<T> or similar are not value-equatable and therefore do not cache. Use EquatableArray<T> or similar instead.
- Arrays, lists, syntax nodes, locations, or symbols flow too far through the pipeline.
- Marker attributes have visibility, duplication, or
EmbeddedAttribute problems.
- Generated code has namespace, containing-type, accessibility,
partial, or #nullable enable issues.
- Analyzer-config or MSBuild settings are not exposed with
CompilerVisibleProperty or CompilerVisibleItemMetadata, or are read from the wrong scope.
- Packaging omits analyzer assets or runtime dependencies from
analyzers/dotnet/cs.
Verify the fix at the right level.
- Add focused tests for semantics and generated output shape.
- Use snapshot or golden-file tests when exact generated text matters.
- Add integration or packaging tests when analyzer layout or consumer behavior matters.
- Rebuild with generated-source emission after changes and inspect the output.
- When incrementality is suspect, test that equivalent inputs produce equivalent models and cached outputs.
Useful Commands
Use these commands when the repository does not already provide more specific wrappers:
PowerShell:
dotnet build <project-or-solution> -t:Rebuild `
/p:EmitCompilerGeneratedFiles=true `
/p:CompilerGeneratedFilesOutputPath=artifacts/generated-src
dotnet test <project-or-solution>
bash / zsh:
dotnet build <project-or-solution> -t:Rebuild \
/p:EmitCompilerGeneratedFiles=true \
/p:CompilerGeneratedFilesOutputPath=artifacts/generated-src
dotnet test <project-or-solution>
Emitted files land under artifacts/generated-src/<assembly>/<generator>/. Use forward slashes in
the MSBuild property so the same command works on every platform.
Notes
- Prefer primary Roslyn docs over blog guidance when they disagree.
- Treat Andrew Lock's series (linked from
references/andrew-lock-series.md) as implementation guidance and worked examples, especially for testing, packaging, marker attributes, and performance pitfalls.
- Keep edits narrow. Generator bugs often come from a small number of pipeline, equality, or symbol-shape mistakes.
1---2name: incremental-source-generator3description: Design, review, debug, and fix Roslyn incremental source generators. Use when creating or modifying `IIncrementalGenerator` implementations, diagnosing incorrect or missing generated code, improving incrementality or IDE performance, adding marker attributes or analyzer-config/MSBuild inputs, inspecting emitted `.g.cs` output, or adding tests and packaging for source generators and analyzers.4license: MIT5---67# Incremental Source Generator89## Overview1011Use this skill to build or repair Roslyn incremental generators with an emphasis on correctness, cacheability, and IDE performance. Prefer the Roslyn design docs for API rules, then use the Andrew Lock series for concrete patterns, tests, packaging, and failure analysis.1213## Workflow14151. Identify the active problem before editing.16 Separate design work, correctness bugs, performance regressions, packaging issues, and test gaps. Locate the generator entry points, post-initialization output, supporting models, emitted hint names, and the tests or consuming projects that prove the behavior.17182. Gather evidence from the current implementation.19 Read the generator and nearby data models before changing code. If output is wrong or missing, rebuild with generated-source emission enabled and inspect the emitted `.g.cs` files. If behavior depends on project configuration, inspect additional files, analyzer config, compiler-visible MSBuild properties, and any marker attributes added during post-initialization.20213. Read only the references that match the task.22 Start with the bundled Roslyn design docs:23 - `references/roslyn/incremental-generators.md`24 - `references/roslyn/incremental-generators.cookbook.md`2526 Then pull in the `references/andrew-lock-series/` digests by topic. Each one summarises a post in27 Andrew Lock's "Creating a source generator" series in original prose, with the canonical URL to28 fetch when you need his full treatment:29 - Fundamentals and first implementation:30 - `references/andrew-lock-series/00-series-index.md`31 - `references/andrew-lock-series/01-creating-an-incremental-generator.md`32 - `references/andrew-lock-series/04-customising-generated-code-with-marker-attributes.md`33 - `references/andrew-lock-series/05-finding-namespace-and-type-hierarchy.md`34 - `references/andrew-lock-series/06-saving-source-generator-output-in-source-control.md`35 - Testing, packaging, and cacheability:36 - `references/andrew-lock-series/02-testing-an-incremental-generator-with-snapshot-testing.md`37 - `references/andrew-lock-series/03-integration-testing-and-packaging.md`38 - `references/andrew-lock-series/09-avoiding-performance-pitfalls.md`39 - `references/andrew-lock-series/10-testing-cacheable-pipeline-outputs.md`40 - Advanced marker, configuration, and versioning topics:41 - `references/andrew-lock-series/07-marker-attribute-problem-part-1.md`42 - `references/andrew-lock-series/08-marker-attribute-problem-part-2.md`43 - `references/andrew-lock-series/11-implementing-an-interceptor.md`44 - `references/andrew-lock-series/12-reading-compilation-options-and-csharp-version.md`45 - `references/andrew-lock-series/13-accessing-msbuild-properties-and-user-configuration.md`46 - `references/andrew-lock-series/14-supporting-multiple-sdk-versions.md`47 - `references/andrew-lock-series/15-marker-attribute-problem-in-dotnet-10.md`4849 These digests are self-contained enough to work from offline. When exact detail matters, or the50 digest points forward to something it does not cover, fetch the canonical article it links.51524. Design or repair the pipeline with incrementality in mind.53 - Prefer `SyntaxProvider.ForAttributeWithMetadataName(...)` when an attribute can drive discovery.54 - Keep predicates purely syntactic and cheap. Push semantic work into later transforms.55 - Extract compact, equatable models early. Prefer `record` or `record struct`, strings, enums, and wrapper collections with value equality.56 - Remove `ISymbol`, `SyntaxNode`, and `Location` from long-lived models as soon as possible.57 - Combine the smallest derived values that solve the problem. Do not push full `Compilation` objects or other high-churn inputs farther downstream than necessary.58 - Pass cancellation tokens through Roslyn and file APIs, and check them inside expensive loops.59 - Generate source text with `StringBuilder` or another text writer. Do not build large `SyntaxNode` trees just to call `NormalizeWhitespace`.60 - Keep output additive. Use post-initialization output for marker attributes and related helper stubs.61 - Keep hint names stable and unique.62 - Report diagnostics when invalid user inputs need feedback instead of silently skipping the case.63645. Check common failure modes explicitly.65 - Discovery is too broad or uses `CreateSyntaxProvider` where an attribute-driven approach is available.66 - Models are not value-equatable, so cache hits are lost. Most common: ``ImmutableArray<T>``, ``List<T>`` or similar are **not** value-equatable and therefore do not cache. Use ``EquatableArray<T>`` or similar instead.67 - Arrays, lists, syntax nodes, locations, or symbols flow too far through the pipeline.68 - Marker attributes have visibility, duplication, or `EmbeddedAttribute` problems.69 - Generated code has namespace, containing-type, accessibility, `partial`, or `#nullable enable` issues.70 - Analyzer-config or MSBuild settings are not exposed with `CompilerVisibleProperty` or `CompilerVisibleItemMetadata`, or are read from the wrong scope.71 - Packaging omits analyzer assets or runtime dependencies from `analyzers/dotnet/cs`.72736. Verify the fix at the right level.74 - Add focused tests for semantics and generated output shape.75 - Use snapshot or golden-file tests when exact generated text matters.76 - Add integration or packaging tests when analyzer layout or consumer behavior matters.77 - Rebuild with generated-source emission after changes and inspect the output.78 - When incrementality is suspect, test that equivalent inputs produce equivalent models and cached outputs.7980## Useful Commands8182Use these commands when the repository does not already provide more specific wrappers:8384PowerShell:8586```powershell87dotnet build <project-or-solution> -t:Rebuild `88 /p:EmitCompilerGeneratedFiles=true `89 /p:CompilerGeneratedFilesOutputPath=artifacts/generated-src9091dotnet test <project-or-solution>92```9394bash / zsh:9596```bash97dotnet build <project-or-solution> -t:Rebuild \98 /p:EmitCompilerGeneratedFiles=true \99 /p:CompilerGeneratedFilesOutputPath=artifacts/generated-src100101dotnet test <project-or-solution>102```103104Emitted files land under `artifacts/generated-src/<assembly>/<generator>/`. Use forward slashes in105the MSBuild property so the same command works on every platform.106107## Notes108109- Prefer primary Roslyn docs over blog guidance when they disagree.110- Treat Andrew Lock's series (linked from `references/andrew-lock-series.md`) as implementation guidance and worked examples, especially for testing, packaging, marker attributes, and performance pitfalls.111- Keep edits narrow. Generator bugs often come from a small number of pipeline, equality, or symbol-shape mistakes.