Fix source generator diagnostics to support #pragma warning disable - #124994

Merged
eiriktsarpalis merged 30 commits into
mainfrom
fix/sourcegen-suppressions
Mar 11, 2026
Merged

Fix source generator diagnostics to support #pragma warning disable#124994
eiriktsarpalis merged 30 commits into
mainfrom
fix/sourcegen-suppressions

Conversation

@eiriktsarpalis

@eiriktsarpaliseiriktsarpalis commented Feb 27, 2026

Copy link
Copy Markdown
Member

Summary

Source generator diagnostics emitted by incremental generators in dotnet/runtime can now be suppressed using #pragma warning disable inline directives.

Fixes#92509

Problem

Incremental source generators that store diagnostic locations in equatable intermediate representations "trim" the Location to avoid holding references to the Compilation object. The standard workaround — Location.Create(filePath, textSpan, linePositionSpan) — creates an ExternalFileLocation (LocationKind.ExternalFile) which bypasses Roslyn's pragma suppression checks. Only SourceLocation (LocationKind.SourceFile) instances, created via Location.Create(SyntaxTree, TextSpan), are checked against the SyntaxTree's pragma directive table.

This is the issue described in dotnet/roslyn#68291.

Solution

Following the technique first applied in eiriktsarpalis/PolyType#401, split each affected generator's RegisterSourceOutput pipeline into two separate pipelines:

  1. Source generation pipeline — Fully incremental. Uses Select to extract just the equatable model (which contains no Location/SyntaxTree references), deduplicates by model equality, only re-fires on structural changes.
  2. Diagnostic pipeline — Reports raw Diagnostic objects that preserve the original SourceLocation (LocationKind.SourceFile) from the syntax tree. This enables Roslyn to check the SyntaxTree's pragma directive table for #pragma warning disable directives.

Parsers now create Diagnostic.Create(descriptor, location, ...) directly instead of wrapping in DiagnosticInfo.Create(...) which trimmed the location. Since ImmutableArray<Diagnostic> does not have value equality, the diagnostic pipeline fires more frequently but the work is trivially cheap (just iterating and reporting).

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Enables Roslyn pragma-based suppression (#pragma warning disable) for diagnostics emitted by several incremental source generators by separating source emission from diagnostic emission and recreating diagnostics with SourceFile locations recovered from the current Compilation.

Changes:

  • Split generator pipelines into (1) fully-incremental source generation and (2) diagnostics combined with CompilationProvider for pragma-suppressible locations.
  • Add DiagnosticInfo.CreateDiagnostic(Compilation) (and Regex-specific equivalent) to rebuild Location as LocationKind.SourceFile.
  • Update Json source generator incremental test commentary to reflect the new diagnostics pipeline behavior.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.

Show a summary per file
FileDescription
src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.csAdds a diagnostics-only pipeline and recreates diagnostics with SourceFile locations.
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorIncrementalTests.csAdjusts incremental-model encapsulation test commentary given diagnostics now reference SyntaxTree.
src/libraries/System.Text.Json/gen/JsonSourceGenerator.Roslyn4.0.csSplits Json SG into separate source + diagnostics pipelines; diagnostics now created with compilation context.
src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Roslyn4.0.csSplits LoggerMessage SG pipelines and preserves diagnostic deduping while making locations pragma-suppressible.
src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.csSplits binder SG pipelines and reports diagnostics using compilation-backed SourceFile locations.
src/libraries/Common/src/SourceGenerators/DiagnosticInfo.csAdds CreateDiagnostic(Compilation) to recreate pragma-suppressible SourceFile locations from trimmed locations.

Comment threadsrc/libraries/Common/src/SourceGenerators/DiagnosticInfo.cs Outdated
Comment threadsrc/libraries/Common/src/SourceGenerators/DiagnosticInfo.cs Outdated
Comment threadsrc/libraries/System.Text.RegularExpressions/gen/RegexGenerator.cs Outdated
Split each affected generator's RegisterSourceOutput pipeline into two
separate pipelines:
1. Source generation pipeline - fully incremental, uses Select to extract
just the equatable model. Only re-fires on structural changes.
2. Diagnostic pipeline - combines with CompilationProvider to recover the
SyntaxTree from the Compilation at emission time. Uses
Location.Create(SyntaxTree, TextSpan) to produce SourceLocation
instances that support pragma suppression checks.
Affected generators:
- System.Text.Json (JsonSourceGenerator)
- Microsoft.Extensions.Logging (LoggerMessageGenerator)
- Microsoft.Extensions.Configuration.Binder (ConfigurationBindingGenerator)
- System.Text.RegularExpressions (RegexGenerator)
The shared DiagnosticInfo type gains a CreateDiagnostic(Compilation) overload
that recovers the SyntaxTree from the trimmed ExternalFileLocation's file
path, converting it back to a SourceLocation. The RegexGenerator's private
DiagnosticData type gets an analogous ToDiagnostic(Compilation) overload.
Fixes#92509
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis
eiriktsarpalisforce-pushed the fix/sourcegen-suppressions branch from 50000b3 to e95c4adCompareFebruary 28, 2026 09:42
CopilotAI review requested due to automatic review settings February 28, 2026 09:42

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • The catch block rethrows with throw ex;, which resets the exception stack trace. Use throw; (or remove the catch entirely) to preserve the original call stack for diagnosing generator failures.
 catch (Exception ex)
{
throw ex;
}

…tors
Verifies that diagnostics from all 4 affected source generators (Regex,
JSON, Logger, ConfigBinder) have LocationKind.SourceFile, which is the
prerequisite for #pragma warning disable to work. Before this fix,
diagnostics had LocationKind.ExternalFile which bypasses Roslyn's pragma
suppression checks.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds RegexGeneratorIncrementalTests with 4 tests following the patterns
established by JsonSourceGeneratorIncrementalTests and ConfigBinder's
GeneratorTests.Incremental.cs:
- SameInput_DoesNotRegenerate: verifies caching on identical compilations
- EquivalentSources_Regenerates: documents that semantically equivalent
sources trigger regeneration (pre-existing limitation due to Dictionary
in model lacking value equality)
- DifferentSources_Regenerates: verifies model changes trigger output
- SourceGenModelDoesNotEncapsulateSymbolsOrCompilationData: walks the
object graph to ensure no Compilation/ISymbol references leak
Also adds SourceGenerationTrackingName constant and WithTrackingName()
to the source pipeline to enable step tracking.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings February 28, 2026 14:09

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • The catch (Exception ex) { throw ex; } pattern resets the original stack trace. If the intent is just to propagate, use throw; (or remove the try/catch entirely) so failures in the generator preserve useful call stacks.
 catch (Exception ex)
{
throw ex;
}

Replace inline lambda callbacks in the diagnostic pipelines with named
EmitDiagnostics static methods, complementing the existing EmitSource
methods in all 4 generators (JSON, Logger, ConfigBinder, Regex).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.

Create named IncrementalValueProvider variables for the equatable model
projections in all 4 generators, with detailed comments explaining how
Roslyn's Select operator uses model equality to guard source production.
For the Regex generator, also extract the source emission lambda into a
named EmitSource method, complementing EmitDiagnostics.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis
eiriktsarpalisforce-pushed the fix/sourcegen-suppressions branch from 3323beb to a4e500eCompareMarch 2, 2026 17:10
Create named IncrementalValueProvider variables for the diagnostic
projections in all 4 generators, with comments explaining that
ImmutableArray<Diagnostic> uses reference equality in the incremental
pipeline — the callback fires on every compilation change by design.
This also simplifies the EmitDiagnostics signatures to accept just
the projected diagnostics rather than the full model+diagnostics tuple.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings March 2, 2026 17:16

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • Avoid throw ex; here; it resets the original stack trace and makes failures harder to diagnose. Use throw; to preserve the stack trace, or remove the try/catch entirely if it's only rethrowing.
 catch (Exception ex)
{
throw ex;
}

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • throw ex; resets the stack trace and makes failures harder to diagnose. Either remove this try/catch (letting the original exception propagate) or use a bare throw; to preserve the original stack trace.
 catch (Exception ex)
{
throw ex;
}

You can also share your feedback on Copilot code review. Take the survey.

RegexPatternAndSyntax is not part of the incremental model — it is
consumed in the first Select and never reaches the Collect phase.
Restoring DiagnosticLocation simplifies the first Select by removing
the (RegexPatternAndSyntax, Location) tuple indirection. RegexMethod
remains Location-free since it is part of the cached model.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis
eiriktsarpalis marked this pull request as ready for review March 10, 2026 21:14
CopilotAI review requested due to automatic review settings March 10, 2026 21:14
@eiriktsarpaliseiriktsarpalis added the source-generator Indicates an issue with a source generator feature label Mar 10, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.


You can also share your feedback on Copilot code review. Take the survey.

@ericstjericstj left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to do anything in the interop generators?

@ericstjericstj left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good and surprisingly minimal. Feedback is straightforward fix or non-blocking.

- Delete Common/src/SourceGenerators/DiagnosticInfo.cs (no longer used)
- Remove orphaned DiagnosticInfo.cs Compile Include from Logging and STJ
targets files
- Add diagnostic.GetMessage() to Logger dedup key to avoid collapsing
distinct diagnostics with same Id/location but different messages
- Add pragma suppression test cases: negative (no pragma), partial
(multiple diagnostics, only some suppressed), and scoping (suppress
then restore before diagnostic site)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis

Copy link
Copy Markdown
MemberAuthor

/ba-g stalled browser-wasm tests. Changes are compile-time only.

@eiriktsarpalis
eiriktsarpalis merged commit 2c7ee19 into mainMar 11, 2026
88 of 90 checks passed
@eiriktsarpalis
eiriktsarpalis deleted the fix/sourcegen-suppressions branch March 11, 2026 09:41
CopilotAI pushed a commit that referenced this pull request Mar 13, 2026
…124994)
## Summary
Source generator diagnostics emitted by incremental generators in
dotnet/runtime can now be suppressed using `#pragma warning disable`
inline directives.
Fixes#92509
## Problem
Incremental source generators that store diagnostic locations in
equatable intermediate representations "trim" the `Location` to avoid
holding references to the `Compilation` object. The standard workaround
— `Location.Create(filePath, textSpan, linePositionSpan)` — creates an
`ExternalFileLocation` (`LocationKind.ExternalFile`) which **bypasses
Roslyn's pragma suppression checks**. Only `SourceLocation`
(`LocationKind.SourceFile`) instances, created via
`Location.Create(SyntaxTree, TextSpan)`, are checked against the
`SyntaxTree`'s pragma directive table.
This is the issue described in
[dotnet/roslyn#68291](dotnet/roslyn#68291).
## Solution
Following the technique first applied in
[eiriktsarpalis/PolyType#401](eiriktsarpalis/PolyType#401),
split each affected generator's `RegisterSourceOutput` pipeline into two
separate pipelines:
1. **Source generation pipeline** — Fully incremental. Uses `Select` to
extract just the equatable model (which contains no
`Location`/`SyntaxTree` references), deduplicates by model equality,
only re-fires on structural changes.
2. **Diagnostic pipeline** — Reports raw `Diagnostic` objects that
preserve the original `SourceLocation` (`LocationKind.SourceFile`) from
the syntax tree. This enables Roslyn to check the `SyntaxTree`'s pragma
directive table for `#pragma warning disable` directives.
Parsers now create `Diagnostic.Create(descriptor, location, ...)`
directly instead of wrapping in `DiagnosticInfo.Create(...)` which
trimmed the location. Since `ImmutableArray<Diagnostic>` does not have
value equality, the diagnostic pipeline fires more frequently but the
work is trivially cheap (just iterating and reporting).
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Frulfump

Copy link
Copy Markdown

Can this one be backported to 10.0.1xx and 10.0.3xx? (If that happens I assume it would also automatically be a part of upcoming 10.0.4xx?)

@eiriktsarpalis

Copy link
Copy Markdown
MemberAuthor

This is not something we could backport to .NET 10, unfortunately.

@Frulfump

Copy link
Copy Markdown

Ah ok thanks for responding. Looking forward to .NET 11 then, is it available in preview 3 or will it be preview 4?

@eiriktsarpalis

Copy link
Copy Markdown
MemberAuthor

Preview 4 I think.

@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 16, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Text.Jsonsource-generatorIndicates an issue with a source generator feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Address diagnostic issues in runtime incremental source generators

4 participants

@eiriktsarpalis@Frulfump@ericstj
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Fix source generator diagnostics to support #pragma warning disable - #124994

Merged
eiriktsarpalis merged 30 commits into
mainfrom
fix/sourcegen-suppressions
Mar 11, 2026
Merged

Fix source generator diagnostics to support #pragma warning disable#124994
eiriktsarpalis merged 30 commits into
mainfrom
fix/sourcegen-suppressions

Conversation

@eiriktsarpalis

@eiriktsarpaliseiriktsarpalis commented Feb 27, 2026

Copy link
Copy Markdown
Member

Summary

Source generator diagnostics emitted by incremental generators in dotnet/runtime can now be suppressed using #pragma warning disable inline directives.

Fixes#92509

Problem

Incremental source generators that store diagnostic locations in equatable intermediate representations "trim" the Location to avoid holding references to the Compilation object. The standard workaround — Location.Create(filePath, textSpan, linePositionSpan) — creates an ExternalFileLocation (LocationKind.ExternalFile) which bypasses Roslyn's pragma suppression checks. Only SourceLocation (LocationKind.SourceFile) instances, created via Location.Create(SyntaxTree, TextSpan), are checked against the SyntaxTree's pragma directive table.

This is the issue described in dotnet/roslyn#68291.

Solution

Following the technique first applied in eiriktsarpalis/PolyType#401, split each affected generator's RegisterSourceOutput pipeline into two separate pipelines:

  1. Source generation pipeline — Fully incremental. Uses Select to extract just the equatable model (which contains no Location/SyntaxTree references), deduplicates by model equality, only re-fires on structural changes.
  2. Diagnostic pipeline — Reports raw Diagnostic objects that preserve the original SourceLocation (LocationKind.SourceFile) from the syntax tree. This enables Roslyn to check the SyntaxTree's pragma directive table for #pragma warning disable directives.

Parsers now create Diagnostic.Create(descriptor, location, ...) directly instead of wrapping in DiagnosticInfo.Create(...) which trimmed the location. Since ImmutableArray<Diagnostic> does not have value equality, the diagnostic pipeline fires more frequently but the work is trivially cheap (just iterating and reporting).

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Enables Roslyn pragma-based suppression (#pragma warning disable) for diagnostics emitted by several incremental source generators by separating source emission from diagnostic emission and recreating diagnostics with SourceFile locations recovered from the current Compilation.

Changes:

  • Split generator pipelines into (1) fully-incremental source generation and (2) diagnostics combined with CompilationProvider for pragma-suppressible locations.
  • Add DiagnosticInfo.CreateDiagnostic(Compilation) (and Regex-specific equivalent) to rebuild Location as LocationKind.SourceFile.
  • Update Json source generator incremental test commentary to reflect the new diagnostics pipeline behavior.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.

Show a summary per file
FileDescription
src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.csAdds a diagnostics-only pipeline and recreates diagnostics with SourceFile locations.
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorIncrementalTests.csAdjusts incremental-model encapsulation test commentary given diagnostics now reference SyntaxTree.
src/libraries/System.Text.Json/gen/JsonSourceGenerator.Roslyn4.0.csSplits Json SG into separate source + diagnostics pipelines; diagnostics now created with compilation context.
src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Roslyn4.0.csSplits LoggerMessage SG pipelines and preserves diagnostic deduping while making locations pragma-suppressible.
src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.csSplits binder SG pipelines and reports diagnostics using compilation-backed SourceFile locations.
src/libraries/Common/src/SourceGenerators/DiagnosticInfo.csAdds CreateDiagnostic(Compilation) to recreate pragma-suppressible SourceFile locations from trimmed locations.

Comment threadsrc/libraries/Common/src/SourceGenerators/DiagnosticInfo.cs Outdated
Comment threadsrc/libraries/Common/src/SourceGenerators/DiagnosticInfo.cs Outdated
Comment threadsrc/libraries/System.Text.RegularExpressions/gen/RegexGenerator.cs Outdated
Split each affected generator's RegisterSourceOutput pipeline into two
separate pipelines:
1. Source generation pipeline - fully incremental, uses Select to extract
just the equatable model. Only re-fires on structural changes.
2. Diagnostic pipeline - combines with CompilationProvider to recover the
SyntaxTree from the Compilation at emission time. Uses
Location.Create(SyntaxTree, TextSpan) to produce SourceLocation
instances that support pragma suppression checks.
Affected generators:
- System.Text.Json (JsonSourceGenerator)
- Microsoft.Extensions.Logging (LoggerMessageGenerator)
- Microsoft.Extensions.Configuration.Binder (ConfigurationBindingGenerator)
- System.Text.RegularExpressions (RegexGenerator)
The shared DiagnosticInfo type gains a CreateDiagnostic(Compilation) overload
that recovers the SyntaxTree from the trimmed ExternalFileLocation's file
path, converting it back to a SourceLocation. The RegexGenerator's private
DiagnosticData type gets an analogous ToDiagnostic(Compilation) overload.
Fixes#92509
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis
eiriktsarpalisforce-pushed the fix/sourcegen-suppressions branch from 50000b3 to e95c4adCompareFebruary 28, 2026 09:42
CopilotAI review requested due to automatic review settings February 28, 2026 09:42

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • The catch block rethrows with throw ex;, which resets the exception stack trace. Use throw; (or remove the catch entirely) to preserve the original call stack for diagnosing generator failures.
 catch (Exception ex)
{
throw ex;
}

…tors
Verifies that diagnostics from all 4 affected source generators (Regex,
JSON, Logger, ConfigBinder) have LocationKind.SourceFile, which is the
prerequisite for #pragma warning disable to work. Before this fix,
diagnostics had LocationKind.ExternalFile which bypasses Roslyn's pragma
suppression checks.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds RegexGeneratorIncrementalTests with 4 tests following the patterns
established by JsonSourceGeneratorIncrementalTests and ConfigBinder's
GeneratorTests.Incremental.cs:
- SameInput_DoesNotRegenerate: verifies caching on identical compilations
- EquivalentSources_Regenerates: documents that semantically equivalent
sources trigger regeneration (pre-existing limitation due to Dictionary
in model lacking value equality)
- DifferentSources_Regenerates: verifies model changes trigger output
- SourceGenModelDoesNotEncapsulateSymbolsOrCompilationData: walks the
object graph to ensure no Compilation/ISymbol references leak
Also adds SourceGenerationTrackingName constant and WithTrackingName()
to the source pipeline to enable step tracking.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings February 28, 2026 14:09

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • The catch (Exception ex) { throw ex; } pattern resets the original stack trace. If the intent is just to propagate, use throw; (or remove the try/catch entirely) so failures in the generator preserve useful call stacks.
 catch (Exception ex)
{
throw ex;
}

Replace inline lambda callbacks in the diagnostic pipelines with named
EmitDiagnostics static methods, complementing the existing EmitSource
methods in all 4 generators (JSON, Logger, ConfigBinder, Regex).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.

Create named IncrementalValueProvider variables for the equatable model
projections in all 4 generators, with detailed comments explaining how
Roslyn's Select operator uses model equality to guard source production.
For the Regex generator, also extract the source emission lambda into a
named EmitSource method, complementing EmitDiagnostics.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis
eiriktsarpalisforce-pushed the fix/sourcegen-suppressions branch from 3323beb to a4e500eCompareMarch 2, 2026 17:10
Create named IncrementalValueProvider variables for the diagnostic
projections in all 4 generators, with comments explaining that
ImmutableArray<Diagnostic> uses reference equality in the incremental
pipeline — the callback fires on every compilation change by design.
This also simplifies the EmitDiagnostics signatures to accept just
the projected diagnostics rather than the full model+diagnostics tuple.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings March 2, 2026 17:16

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • Avoid throw ex; here; it resets the original stack trace and makes failures harder to diagnose. Use throw; to preserve the stack trace, or remove the try/catch entirely if it's only rethrowing.
 catch (Exception ex)
{
throw ex;
}

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • throw ex; resets the stack trace and makes failures harder to diagnose. Either remove this try/catch (letting the original exception propagate) or use a bare throw; to preserve the original stack trace.
 catch (Exception ex)
{
throw ex;
}

You can also share your feedback on Copilot code review. Take the survey.

RegexPatternAndSyntax is not part of the incremental model — it is
consumed in the first Select and never reaches the Collect phase.
Restoring DiagnosticLocation simplifies the first Select by removing
the (RegexPatternAndSyntax, Location) tuple indirection. RegexMethod
remains Location-free since it is part of the cached model.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis
eiriktsarpalis marked this pull request as ready for review March 10, 2026 21:14
CopilotAI review requested due to automatic review settings March 10, 2026 21:14
@eiriktsarpaliseiriktsarpalis added the source-generator Indicates an issue with a source generator feature label Mar 10, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.


You can also share your feedback on Copilot code review. Take the survey.

@ericstjericstj left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to do anything in the interop generators?

@ericstjericstj left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good and surprisingly minimal. Feedback is straightforward fix or non-blocking.

- Delete Common/src/SourceGenerators/DiagnosticInfo.cs (no longer used)
- Remove orphaned DiagnosticInfo.cs Compile Include from Logging and STJ
targets files
- Add diagnostic.GetMessage() to Logger dedup key to avoid collapsing
distinct diagnostics with same Id/location but different messages
- Add pragma suppression test cases: negative (no pragma), partial
(multiple diagnostics, only some suppressed), and scoping (suppress
then restore before diagnostic site)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis

Copy link
Copy Markdown
MemberAuthor

/ba-g stalled browser-wasm tests. Changes are compile-time only.

@eiriktsarpalis
eiriktsarpalis merged commit 2c7ee19 into mainMar 11, 2026
88 of 90 checks passed
@eiriktsarpalis
eiriktsarpalis deleted the fix/sourcegen-suppressions branch March 11, 2026 09:41
CopilotAI pushed a commit that referenced this pull request Mar 13, 2026
…124994)
## Summary
Source generator diagnostics emitted by incremental generators in
dotnet/runtime can now be suppressed using `#pragma warning disable`
inline directives.
Fixes#92509
## Problem
Incremental source generators that store diagnostic locations in
equatable intermediate representations "trim" the `Location` to avoid
holding references to the `Compilation` object. The standard workaround
— `Location.Create(filePath, textSpan, linePositionSpan)` — creates an
`ExternalFileLocation` (`LocationKind.ExternalFile`) which **bypasses
Roslyn's pragma suppression checks**. Only `SourceLocation`
(`LocationKind.SourceFile`) instances, created via
`Location.Create(SyntaxTree, TextSpan)`, are checked against the
`SyntaxTree`'s pragma directive table.
This is the issue described in
[dotnet/roslyn#68291](dotnet/roslyn#68291).
## Solution
Following the technique first applied in
[eiriktsarpalis/PolyType#401](eiriktsarpalis/PolyType#401),
split each affected generator's `RegisterSourceOutput` pipeline into two
separate pipelines:
1. **Source generation pipeline** — Fully incremental. Uses `Select` to
extract just the equatable model (which contains no
`Location`/`SyntaxTree` references), deduplicates by model equality,
only re-fires on structural changes.
2. **Diagnostic pipeline** — Reports raw `Diagnostic` objects that
preserve the original `SourceLocation` (`LocationKind.SourceFile`) from
the syntax tree. This enables Roslyn to check the `SyntaxTree`'s pragma
directive table for `#pragma warning disable` directives.
Parsers now create `Diagnostic.Create(descriptor, location, ...)`
directly instead of wrapping in `DiagnosticInfo.Create(...)` which
trimmed the location. Since `ImmutableArray<Diagnostic>` does not have
value equality, the diagnostic pipeline fires more frequently but the
work is trivially cheap (just iterating and reporting).
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Frulfump

Copy link
Copy Markdown

Can this one be backported to 10.0.1xx and 10.0.3xx? (If that happens I assume it would also automatically be a part of upcoming 10.0.4xx?)

@eiriktsarpalis

Copy link
Copy Markdown
MemberAuthor

This is not something we could backport to .NET 10, unfortunately.

@Frulfump

Copy link
Copy Markdown

Ah ok thanks for responding. Looking forward to .NET 11 then, is it available in preview 3 or will it be preview 4?

@eiriktsarpalis

Copy link
Copy Markdown
MemberAuthor

Preview 4 I think.

@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 16, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Text.Jsonsource-generatorIndicates an issue with a source generator feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Address diagnostic issues in runtime incremental source generators

4 participants

@eiriktsarpalis@Frulfump@ericstj
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix source generator diagnostics to support #pragma warning disable - #124994

Merged
eiriktsarpalis merged 30 commits into
mainfrom
fix/sourcegen-suppressions
Mar 11, 2026
Merged

Fix source generator diagnostics to support #pragma warning disable#124994
eiriktsarpalis merged 30 commits into
mainfrom
fix/sourcegen-suppressions

Conversation

@eiriktsarpalis

@eiriktsarpaliseiriktsarpalis commented Feb 27, 2026

Copy link
Copy Markdown
Member

Summary

Source generator diagnostics emitted by incremental generators in dotnet/runtime can now be suppressed using #pragma warning disable inline directives.

Fixes#92509

Problem

Incremental source generators that store diagnostic locations in equatable intermediate representations "trim" the Location to avoid holding references to the Compilation object. The standard workaround — Location.Create(filePath, textSpan, linePositionSpan) — creates an ExternalFileLocation (LocationKind.ExternalFile) which bypasses Roslyn's pragma suppression checks. Only SourceLocation (LocationKind.SourceFile) instances, created via Location.Create(SyntaxTree, TextSpan), are checked against the SyntaxTree's pragma directive table.

This is the issue described in dotnet/roslyn#68291.

Solution

Following the technique first applied in eiriktsarpalis/PolyType#401, split each affected generator's RegisterSourceOutput pipeline into two separate pipelines:

  1. Source generation pipeline — Fully incremental. Uses Select to extract just the equatable model (which contains no Location/SyntaxTree references), deduplicates by model equality, only re-fires on structural changes.
  2. Diagnostic pipeline — Reports raw Diagnostic objects that preserve the original SourceLocation (LocationKind.SourceFile) from the syntax tree. This enables Roslyn to check the SyntaxTree's pragma directive table for #pragma warning disable directives.

Parsers now create Diagnostic.Create(descriptor, location, ...) directly instead of wrapping in DiagnosticInfo.Create(...) which trimmed the location. Since ImmutableArray<Diagnostic> does not have value equality, the diagnostic pipeline fires more frequently but the work is trivially cheap (just iterating and reporting).

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Enables Roslyn pragma-based suppression (#pragma warning disable) for diagnostics emitted by several incremental source generators by separating source emission from diagnostic emission and recreating diagnostics with SourceFile locations recovered from the current Compilation.

Changes:

  • Split generator pipelines into (1) fully-incremental source generation and (2) diagnostics combined with CompilationProvider for pragma-suppressible locations.
  • Add DiagnosticInfo.CreateDiagnostic(Compilation) (and Regex-specific equivalent) to rebuild Location as LocationKind.SourceFile.
  • Update Json source generator incremental test commentary to reflect the new diagnostics pipeline behavior.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.

Show a summary per file
FileDescription
src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.csAdds a diagnostics-only pipeline and recreates diagnostics with SourceFile locations.
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorIncrementalTests.csAdjusts incremental-model encapsulation test commentary given diagnostics now reference SyntaxTree.
src/libraries/System.Text.Json/gen/JsonSourceGenerator.Roslyn4.0.csSplits Json SG into separate source + diagnostics pipelines; diagnostics now created with compilation context.
src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Roslyn4.0.csSplits LoggerMessage SG pipelines and preserves diagnostic deduping while making locations pragma-suppressible.
src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.csSplits binder SG pipelines and reports diagnostics using compilation-backed SourceFile locations.
src/libraries/Common/src/SourceGenerators/DiagnosticInfo.csAdds CreateDiagnostic(Compilation) to recreate pragma-suppressible SourceFile locations from trimmed locations.

Comment threadsrc/libraries/Common/src/SourceGenerators/DiagnosticInfo.cs Outdated
Comment threadsrc/libraries/Common/src/SourceGenerators/DiagnosticInfo.cs Outdated
Comment threadsrc/libraries/System.Text.RegularExpressions/gen/RegexGenerator.cs Outdated
Split each affected generator's RegisterSourceOutput pipeline into two
separate pipelines:
1. Source generation pipeline - fully incremental, uses Select to extract
just the equatable model. Only re-fires on structural changes.
2. Diagnostic pipeline - combines with CompilationProvider to recover the
SyntaxTree from the Compilation at emission time. Uses
Location.Create(SyntaxTree, TextSpan) to produce SourceLocation
instances that support pragma suppression checks.
Affected generators:
- System.Text.Json (JsonSourceGenerator)
- Microsoft.Extensions.Logging (LoggerMessageGenerator)
- Microsoft.Extensions.Configuration.Binder (ConfigurationBindingGenerator)
- System.Text.RegularExpressions (RegexGenerator)
The shared DiagnosticInfo type gains a CreateDiagnostic(Compilation) overload
that recovers the SyntaxTree from the trimmed ExternalFileLocation's file
path, converting it back to a SourceLocation. The RegexGenerator's private
DiagnosticData type gets an analogous ToDiagnostic(Compilation) overload.
Fixes#92509
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis
eiriktsarpalisforce-pushed the fix/sourcegen-suppressions branch from 50000b3 to e95c4adCompareFebruary 28, 2026 09:42
CopilotAI review requested due to automatic review settings February 28, 2026 09:42

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • The catch block rethrows with throw ex;, which resets the exception stack trace. Use throw; (or remove the catch entirely) to preserve the original call stack for diagnosing generator failures.
 catch (Exception ex)
{
throw ex;
}

…tors
Verifies that diagnostics from all 4 affected source generators (Regex,
JSON, Logger, ConfigBinder) have LocationKind.SourceFile, which is the
prerequisite for #pragma warning disable to work. Before this fix,
diagnostics had LocationKind.ExternalFile which bypasses Roslyn's pragma
suppression checks.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds RegexGeneratorIncrementalTests with 4 tests following the patterns
established by JsonSourceGeneratorIncrementalTests and ConfigBinder's
GeneratorTests.Incremental.cs:
- SameInput_DoesNotRegenerate: verifies caching on identical compilations
- EquivalentSources_Regenerates: documents that semantically equivalent
sources trigger regeneration (pre-existing limitation due to Dictionary
in model lacking value equality)
- DifferentSources_Regenerates: verifies model changes trigger output
- SourceGenModelDoesNotEncapsulateSymbolsOrCompilationData: walks the
object graph to ensure no Compilation/ISymbol references leak
Also adds SourceGenerationTrackingName constant and WithTrackingName()
to the source pipeline to enable step tracking.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings February 28, 2026 14:09

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • The catch (Exception ex) { throw ex; } pattern resets the original stack trace. If the intent is just to propagate, use throw; (or remove the try/catch entirely) so failures in the generator preserve useful call stacks.
 catch (Exception ex)
{
throw ex;
}

Replace inline lambda callbacks in the diagnostic pipelines with named
EmitDiagnostics static methods, complementing the existing EmitSource
methods in all 4 generators (JSON, Logger, ConfigBinder, Regex).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.

Create named IncrementalValueProvider variables for the equatable model
projections in all 4 generators, with detailed comments explaining how
Roslyn's Select operator uses model equality to guard source production.
For the Regex generator, also extract the source emission lambda into a
named EmitSource method, complementing EmitDiagnostics.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis
eiriktsarpalisforce-pushed the fix/sourcegen-suppressions branch from 3323beb to a4e500eCompareMarch 2, 2026 17:10
Create named IncrementalValueProvider variables for the diagnostic
projections in all 4 generators, with comments explaining that
ImmutableArray<Diagnostic> uses reference equality in the incremental
pipeline — the callback fires on every compilation change by design.
This also simplifies the EmitDiagnostics signatures to accept just
the projected diagnostics rather than the full model+diagnostics tuple.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings March 2, 2026 17:16

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • Avoid throw ex; here; it resets the original stack trace and makes failures harder to diagnose. Use throw; to preserve the stack trace, or remove the try/catch entirely if it's only rethrowing.
 catch (Exception ex)
{
throw ex;
}

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • throw ex; resets the stack trace and makes failures harder to diagnose. Either remove this try/catch (letting the original exception propagate) or use a bare throw; to preserve the original stack trace.
 catch (Exception ex)
{
throw ex;
}

You can also share your feedback on Copilot code review. Take the survey.

RegexPatternAndSyntax is not part of the incremental model — it is
consumed in the first Select and never reaches the Collect phase.
Restoring DiagnosticLocation simplifies the first Select by removing
the (RegexPatternAndSyntax, Location) tuple indirection. RegexMethod
remains Location-free since it is part of the cached model.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis
eiriktsarpalis marked this pull request as ready for review March 10, 2026 21:14
CopilotAI review requested due to automatic review settings March 10, 2026 21:14
@eiriktsarpaliseiriktsarpalis added the source-generator Indicates an issue with a source generator feature label Mar 10, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.


You can also share your feedback on Copilot code review. Take the survey.

@ericstjericstj left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to do anything in the interop generators?

@ericstjericstj left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good and surprisingly minimal. Feedback is straightforward fix or non-blocking.

- Delete Common/src/SourceGenerators/DiagnosticInfo.cs (no longer used)
- Remove orphaned DiagnosticInfo.cs Compile Include from Logging and STJ
targets files
- Add diagnostic.GetMessage() to Logger dedup key to avoid collapsing
distinct diagnostics with same Id/location but different messages
- Add pragma suppression test cases: negative (no pragma), partial
(multiple diagnostics, only some suppressed), and scoping (suppress
then restore before diagnostic site)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis

Copy link
Copy Markdown
MemberAuthor

/ba-g stalled browser-wasm tests. Changes are compile-time only.

@eiriktsarpalis
eiriktsarpalis merged commit 2c7ee19 into mainMar 11, 2026
88 of 90 checks passed
@eiriktsarpalis
eiriktsarpalis deleted the fix/sourcegen-suppressions branch March 11, 2026 09:41
CopilotAI pushed a commit that referenced this pull request Mar 13, 2026
…124994)
## Summary
Source generator diagnostics emitted by incremental generators in
dotnet/runtime can now be suppressed using `#pragma warning disable`
inline directives.
Fixes#92509
## Problem
Incremental source generators that store diagnostic locations in
equatable intermediate representations "trim" the `Location` to avoid
holding references to the `Compilation` object. The standard workaround
— `Location.Create(filePath, textSpan, linePositionSpan)` — creates an
`ExternalFileLocation` (`LocationKind.ExternalFile`) which **bypasses
Roslyn's pragma suppression checks**. Only `SourceLocation`
(`LocationKind.SourceFile`) instances, created via
`Location.Create(SyntaxTree, TextSpan)`, are checked against the
`SyntaxTree`'s pragma directive table.
This is the issue described in
[dotnet/roslyn#68291](dotnet/roslyn#68291).
## Solution
Following the technique first applied in
[eiriktsarpalis/PolyType#401](eiriktsarpalis/PolyType#401),
split each affected generator's `RegisterSourceOutput` pipeline into two
separate pipelines:
1. **Source generation pipeline** — Fully incremental. Uses `Select` to
extract just the equatable model (which contains no
`Location`/`SyntaxTree` references), deduplicates by model equality,
only re-fires on structural changes.
2. **Diagnostic pipeline** — Reports raw `Diagnostic` objects that
preserve the original `SourceLocation` (`LocationKind.SourceFile`) from
the syntax tree. This enables Roslyn to check the `SyntaxTree`'s pragma
directive table for `#pragma warning disable` directives.
Parsers now create `Diagnostic.Create(descriptor, location, ...)`
directly instead of wrapping in `DiagnosticInfo.Create(...)` which
trimmed the location. Since `ImmutableArray<Diagnostic>` does not have
value equality, the diagnostic pipeline fires more frequently but the
work is trivially cheap (just iterating and reporting).
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Frulfump

Copy link
Copy Markdown

Can this one be backported to 10.0.1xx and 10.0.3xx? (If that happens I assume it would also automatically be a part of upcoming 10.0.4xx?)

@eiriktsarpalis

Copy link
Copy Markdown
MemberAuthor

This is not something we could backport to .NET 10, unfortunately.

@Frulfump

Copy link
Copy Markdown

Ah ok thanks for responding. Looking forward to .NET 11 then, is it available in preview 3 or will it be preview 4?

@eiriktsarpalis

Copy link
Copy Markdown
MemberAuthor

Preview 4 I think.

@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 16, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Text.Jsonsource-generatorIndicates an issue with a source generator feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Address diagnostic issues in runtime incremental source generators

4 participants

@eiriktsarpalis@Frulfump@ericstj
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix source generator diagnostics to support #pragma warning disable - #124994

Merged
eiriktsarpalis merged 30 commits into
mainfrom
fix/sourcegen-suppressions
Mar 11, 2026
Merged

Fix source generator diagnostics to support #pragma warning disable#124994
eiriktsarpalis merged 30 commits into
mainfrom
fix/sourcegen-suppressions

Conversation

@eiriktsarpalis

@eiriktsarpaliseiriktsarpalis commented Feb 27, 2026

Copy link
Copy Markdown
Member

Summary

Source generator diagnostics emitted by incremental generators in dotnet/runtime can now be suppressed using #pragma warning disable inline directives.

Fixes#92509

Problem

Incremental source generators that store diagnostic locations in equatable intermediate representations "trim" the Location to avoid holding references to the Compilation object. The standard workaround — Location.Create(filePath, textSpan, linePositionSpan) — creates an ExternalFileLocation (LocationKind.ExternalFile) which bypasses Roslyn's pragma suppression checks. Only SourceLocation (LocationKind.SourceFile) instances, created via Location.Create(SyntaxTree, TextSpan), are checked against the SyntaxTree's pragma directive table.

This is the issue described in dotnet/roslyn#68291.

Solution

Following the technique first applied in eiriktsarpalis/PolyType#401, split each affected generator's RegisterSourceOutput pipeline into two separate pipelines:

  1. Source generation pipeline — Fully incremental. Uses Select to extract just the equatable model (which contains no Location/SyntaxTree references), deduplicates by model equality, only re-fires on structural changes.
  2. Diagnostic pipeline — Reports raw Diagnostic objects that preserve the original SourceLocation (LocationKind.SourceFile) from the syntax tree. This enables Roslyn to check the SyntaxTree's pragma directive table for #pragma warning disable directives.

Parsers now create Diagnostic.Create(descriptor, location, ...) directly instead of wrapping in DiagnosticInfo.Create(...) which trimmed the location. Since ImmutableArray<Diagnostic> does not have value equality, the diagnostic pipeline fires more frequently but the work is trivially cheap (just iterating and reporting).

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Enables Roslyn pragma-based suppression (#pragma warning disable) for diagnostics emitted by several incremental source generators by separating source emission from diagnostic emission and recreating diagnostics with SourceFile locations recovered from the current Compilation.

Changes:

  • Split generator pipelines into (1) fully-incremental source generation and (2) diagnostics combined with CompilationProvider for pragma-suppressible locations.
  • Add DiagnosticInfo.CreateDiagnostic(Compilation) (and Regex-specific equivalent) to rebuild Location as LocationKind.SourceFile.
  • Update Json source generator incremental test commentary to reflect the new diagnostics pipeline behavior.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.

Show a summary per file
FileDescription
src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.csAdds a diagnostics-only pipeline and recreates diagnostics with SourceFile locations.
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorIncrementalTests.csAdjusts incremental-model encapsulation test commentary given diagnostics now reference SyntaxTree.
src/libraries/System.Text.Json/gen/JsonSourceGenerator.Roslyn4.0.csSplits Json SG into separate source + diagnostics pipelines; diagnostics now created with compilation context.
src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Roslyn4.0.csSplits LoggerMessage SG pipelines and preserves diagnostic deduping while making locations pragma-suppressible.
src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.csSplits binder SG pipelines and reports diagnostics using compilation-backed SourceFile locations.
src/libraries/Common/src/SourceGenerators/DiagnosticInfo.csAdds CreateDiagnostic(Compilation) to recreate pragma-suppressible SourceFile locations from trimmed locations.

Comment threadsrc/libraries/Common/src/SourceGenerators/DiagnosticInfo.cs Outdated
Comment threadsrc/libraries/Common/src/SourceGenerators/DiagnosticInfo.cs Outdated
Comment threadsrc/libraries/System.Text.RegularExpressions/gen/RegexGenerator.cs Outdated
Split each affected generator's RegisterSourceOutput pipeline into two
separate pipelines:
1. Source generation pipeline - fully incremental, uses Select to extract
just the equatable model. Only re-fires on structural changes.
2. Diagnostic pipeline - combines with CompilationProvider to recover the
SyntaxTree from the Compilation at emission time. Uses
Location.Create(SyntaxTree, TextSpan) to produce SourceLocation
instances that support pragma suppression checks.
Affected generators:
- System.Text.Json (JsonSourceGenerator)
- Microsoft.Extensions.Logging (LoggerMessageGenerator)
- Microsoft.Extensions.Configuration.Binder (ConfigurationBindingGenerator)
- System.Text.RegularExpressions (RegexGenerator)
The shared DiagnosticInfo type gains a CreateDiagnostic(Compilation) overload
that recovers the SyntaxTree from the trimmed ExternalFileLocation's file
path, converting it back to a SourceLocation. The RegexGenerator's private
DiagnosticData type gets an analogous ToDiagnostic(Compilation) overload.
Fixes#92509
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis
eiriktsarpalisforce-pushed the fix/sourcegen-suppressions branch from 50000b3 to e95c4adCompareFebruary 28, 2026 09:42
CopilotAI review requested due to automatic review settings February 28, 2026 09:42

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • The catch block rethrows with throw ex;, which resets the exception stack trace. Use throw; (or remove the catch entirely) to preserve the original call stack for diagnosing generator failures.
 catch (Exception ex)
{
throw ex;
}

…tors
Verifies that diagnostics from all 4 affected source generators (Regex,
JSON, Logger, ConfigBinder) have LocationKind.SourceFile, which is the
prerequisite for #pragma warning disable to work. Before this fix,
diagnostics had LocationKind.ExternalFile which bypasses Roslyn's pragma
suppression checks.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds RegexGeneratorIncrementalTests with 4 tests following the patterns
established by JsonSourceGeneratorIncrementalTests and ConfigBinder's
GeneratorTests.Incremental.cs:
- SameInput_DoesNotRegenerate: verifies caching on identical compilations
- EquivalentSources_Regenerates: documents that semantically equivalent
sources trigger regeneration (pre-existing limitation due to Dictionary
in model lacking value equality)
- DifferentSources_Regenerates: verifies model changes trigger output
- SourceGenModelDoesNotEncapsulateSymbolsOrCompilationData: walks the
object graph to ensure no Compilation/ISymbol references leak
Also adds SourceGenerationTrackingName constant and WithTrackingName()
to the source pipeline to enable step tracking.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings February 28, 2026 14:09

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • The catch (Exception ex) { throw ex; } pattern resets the original stack trace. If the intent is just to propagate, use throw; (or remove the try/catch entirely) so failures in the generator preserve useful call stacks.
 catch (Exception ex)
{
throw ex;
}

Replace inline lambda callbacks in the diagnostic pipelines with named
EmitDiagnostics static methods, complementing the existing EmitSource
methods in all 4 generators (JSON, Logger, ConfigBinder, Regex).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.

Create named IncrementalValueProvider variables for the equatable model
projections in all 4 generators, with detailed comments explaining how
Roslyn's Select operator uses model equality to guard source production.
For the Regex generator, also extract the source emission lambda into a
named EmitSource method, complementing EmitDiagnostics.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis
eiriktsarpalisforce-pushed the fix/sourcegen-suppressions branch from 3323beb to a4e500eCompareMarch 2, 2026 17:10
Create named IncrementalValueProvider variables for the diagnostic
projections in all 4 generators, with comments explaining that
ImmutableArray<Diagnostic> uses reference equality in the incremental
pipeline — the callback fires on every compilation change by design.
This also simplifies the EmitDiagnostics signatures to accept just
the projected diagnostics rather than the full model+diagnostics tuple.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings March 2, 2026 17:16

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • Avoid throw ex; here; it resets the original stack trace and makes failures harder to diagnose. Use throw; to preserve the stack trace, or remove the try/catch entirely if it's only rethrowing.
 catch (Exception ex)
{
throw ex;
}

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • throw ex; resets the stack trace and makes failures harder to diagnose. Either remove this try/catch (letting the original exception propagate) or use a bare throw; to preserve the original stack trace.
 catch (Exception ex)
{
throw ex;
}

You can also share your feedback on Copilot code review. Take the survey.

RegexPatternAndSyntax is not part of the incremental model — it is
consumed in the first Select and never reaches the Collect phase.
Restoring DiagnosticLocation simplifies the first Select by removing
the (RegexPatternAndSyntax, Location) tuple indirection. RegexMethod
remains Location-free since it is part of the cached model.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis
eiriktsarpalis marked this pull request as ready for review March 10, 2026 21:14
CopilotAI review requested due to automatic review settings March 10, 2026 21:14
@eiriktsarpaliseiriktsarpalis added the source-generator Indicates an issue with a source generator feature label Mar 10, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.


You can also share your feedback on Copilot code review. Take the survey.

@ericstjericstj left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to do anything in the interop generators?

@ericstjericstj left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good and surprisingly minimal. Feedback is straightforward fix or non-blocking.

- Delete Common/src/SourceGenerators/DiagnosticInfo.cs (no longer used)
- Remove orphaned DiagnosticInfo.cs Compile Include from Logging and STJ
targets files
- Add diagnostic.GetMessage() to Logger dedup key to avoid collapsing
distinct diagnostics with same Id/location but different messages
- Add pragma suppression test cases: negative (no pragma), partial
(multiple diagnostics, only some suppressed), and scoping (suppress
then restore before diagnostic site)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis

Copy link
Copy Markdown
MemberAuthor

/ba-g stalled browser-wasm tests. Changes are compile-time only.

@eiriktsarpalis
eiriktsarpalis merged commit 2c7ee19 into mainMar 11, 2026
88 of 90 checks passed
@eiriktsarpalis
eiriktsarpalis deleted the fix/sourcegen-suppressions branch March 11, 2026 09:41
CopilotAI pushed a commit that referenced this pull request Mar 13, 2026
…124994)
## Summary
Source generator diagnostics emitted by incremental generators in
dotnet/runtime can now be suppressed using `#pragma warning disable`
inline directives.
Fixes#92509
## Problem
Incremental source generators that store diagnostic locations in
equatable intermediate representations "trim" the `Location` to avoid
holding references to the `Compilation` object. The standard workaround
— `Location.Create(filePath, textSpan, linePositionSpan)` — creates an
`ExternalFileLocation` (`LocationKind.ExternalFile`) which **bypasses
Roslyn's pragma suppression checks**. Only `SourceLocation`
(`LocationKind.SourceFile`) instances, created via
`Location.Create(SyntaxTree, TextSpan)`, are checked against the
`SyntaxTree`'s pragma directive table.
This is the issue described in
[dotnet/roslyn#68291](dotnet/roslyn#68291).
## Solution
Following the technique first applied in
[eiriktsarpalis/PolyType#401](eiriktsarpalis/PolyType#401),
split each affected generator's `RegisterSourceOutput` pipeline into two
separate pipelines:
1. **Source generation pipeline** — Fully incremental. Uses `Select` to
extract just the equatable model (which contains no
`Location`/`SyntaxTree` references), deduplicates by model equality,
only re-fires on structural changes.
2. **Diagnostic pipeline** — Reports raw `Diagnostic` objects that
preserve the original `SourceLocation` (`LocationKind.SourceFile`) from
the syntax tree. This enables Roslyn to check the `SyntaxTree`'s pragma
directive table for `#pragma warning disable` directives.
Parsers now create `Diagnostic.Create(descriptor, location, ...)`
directly instead of wrapping in `DiagnosticInfo.Create(...)` which
trimmed the location. Since `ImmutableArray<Diagnostic>` does not have
value equality, the diagnostic pipeline fires more frequently but the
work is trivially cheap (just iterating and reporting).
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Frulfump

Copy link
Copy Markdown

Can this one be backported to 10.0.1xx and 10.0.3xx? (If that happens I assume it would also automatically be a part of upcoming 10.0.4xx?)

@eiriktsarpalis

Copy link
Copy Markdown
MemberAuthor

This is not something we could backport to .NET 10, unfortunately.

@Frulfump

Copy link
Copy Markdown

Ah ok thanks for responding. Looking forward to .NET 11 then, is it available in preview 3 or will it be preview 4?

@eiriktsarpalis

Copy link
Copy Markdown
MemberAuthor

Preview 4 I think.

@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 16, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Text.Jsonsource-generatorIndicates an issue with a source generator feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Address diagnostic issues in runtime incremental source generators

4 participants

@eiriktsarpalis@Frulfump@ericstj
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Fix source generator diagnostics to support #pragma warning disable - #124994

Merged
eiriktsarpalis merged 30 commits into
mainfrom
fix/sourcegen-suppressions
Mar 11, 2026
Merged

Fix source generator diagnostics to support #pragma warning disable#124994
eiriktsarpalis merged 30 commits into
mainfrom
fix/sourcegen-suppressions

Conversation

@eiriktsarpalis

@eiriktsarpaliseiriktsarpalis commented Feb 27, 2026

Copy link
Copy Markdown
Member

Summary

Source generator diagnostics emitted by incremental generators in dotnet/runtime can now be suppressed using #pragma warning disable inline directives.

Fixes#92509

Problem

Incremental source generators that store diagnostic locations in equatable intermediate representations "trim" the Location to avoid holding references to the Compilation object. The standard workaround — Location.Create(filePath, textSpan, linePositionSpan) — creates an ExternalFileLocation (LocationKind.ExternalFile) which bypasses Roslyn's pragma suppression checks. Only SourceLocation (LocationKind.SourceFile) instances, created via Location.Create(SyntaxTree, TextSpan), are checked against the SyntaxTree's pragma directive table.

This is the issue described in dotnet/roslyn#68291.

Solution

Following the technique first applied in eiriktsarpalis/PolyType#401, split each affected generator's RegisterSourceOutput pipeline into two separate pipelines:

  1. Source generation pipeline — Fully incremental. Uses Select to extract just the equatable model (which contains no Location/SyntaxTree references), deduplicates by model equality, only re-fires on structural changes.
  2. Diagnostic pipeline — Reports raw Diagnostic objects that preserve the original SourceLocation (LocationKind.SourceFile) from the syntax tree. This enables Roslyn to check the SyntaxTree's pragma directive table for #pragma warning disable directives.

Parsers now create Diagnostic.Create(descriptor, location, ...) directly instead of wrapping in DiagnosticInfo.Create(...) which trimmed the location. Since ImmutableArray<Diagnostic> does not have value equality, the diagnostic pipeline fires more frequently but the work is trivially cheap (just iterating and reporting).

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Enables Roslyn pragma-based suppression (#pragma warning disable) for diagnostics emitted by several incremental source generators by separating source emission from diagnostic emission and recreating diagnostics with SourceFile locations recovered from the current Compilation.

Changes:

  • Split generator pipelines into (1) fully-incremental source generation and (2) diagnostics combined with CompilationProvider for pragma-suppressible locations.
  • Add DiagnosticInfo.CreateDiagnostic(Compilation) (and Regex-specific equivalent) to rebuild Location as LocationKind.SourceFile.
  • Update Json source generator incremental test commentary to reflect the new diagnostics pipeline behavior.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.

Show a summary per file
FileDescription
src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.csAdds a diagnostics-only pipeline and recreates diagnostics with SourceFile locations.
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorIncrementalTests.csAdjusts incremental-model encapsulation test commentary given diagnostics now reference SyntaxTree.
src/libraries/System.Text.Json/gen/JsonSourceGenerator.Roslyn4.0.csSplits Json SG into separate source + diagnostics pipelines; diagnostics now created with compilation context.
src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Roslyn4.0.csSplits LoggerMessage SG pipelines and preserves diagnostic deduping while making locations pragma-suppressible.
src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.csSplits binder SG pipelines and reports diagnostics using compilation-backed SourceFile locations.
src/libraries/Common/src/SourceGenerators/DiagnosticInfo.csAdds CreateDiagnostic(Compilation) to recreate pragma-suppressible SourceFile locations from trimmed locations.

Comment threadsrc/libraries/Common/src/SourceGenerators/DiagnosticInfo.cs Outdated
Comment threadsrc/libraries/Common/src/SourceGenerators/DiagnosticInfo.cs Outdated
Comment threadsrc/libraries/System.Text.RegularExpressions/gen/RegexGenerator.cs Outdated
Split each affected generator's RegisterSourceOutput pipeline into two
separate pipelines:
1. Source generation pipeline - fully incremental, uses Select to extract
just the equatable model. Only re-fires on structural changes.
2. Diagnostic pipeline - combines with CompilationProvider to recover the
SyntaxTree from the Compilation at emission time. Uses
Location.Create(SyntaxTree, TextSpan) to produce SourceLocation
instances that support pragma suppression checks.
Affected generators:
- System.Text.Json (JsonSourceGenerator)
- Microsoft.Extensions.Logging (LoggerMessageGenerator)
- Microsoft.Extensions.Configuration.Binder (ConfigurationBindingGenerator)
- System.Text.RegularExpressions (RegexGenerator)
The shared DiagnosticInfo type gains a CreateDiagnostic(Compilation) overload
that recovers the SyntaxTree from the trimmed ExternalFileLocation's file
path, converting it back to a SourceLocation. The RegexGenerator's private
DiagnosticData type gets an analogous ToDiagnostic(Compilation) overload.
Fixes#92509
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis
eiriktsarpalisforce-pushed the fix/sourcegen-suppressions branch from 50000b3 to e95c4adCompareFebruary 28, 2026 09:42
CopilotAI review requested due to automatic review settings February 28, 2026 09:42

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • The catch block rethrows with throw ex;, which resets the exception stack trace. Use throw; (or remove the catch entirely) to preserve the original call stack for diagnosing generator failures.
 catch (Exception ex)
{
throw ex;
}

…tors
Verifies that diagnostics from all 4 affected source generators (Regex,
JSON, Logger, ConfigBinder) have LocationKind.SourceFile, which is the
prerequisite for #pragma warning disable to work. Before this fix,
diagnostics had LocationKind.ExternalFile which bypasses Roslyn's pragma
suppression checks.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds RegexGeneratorIncrementalTests with 4 tests following the patterns
established by JsonSourceGeneratorIncrementalTests and ConfigBinder's
GeneratorTests.Incremental.cs:
- SameInput_DoesNotRegenerate: verifies caching on identical compilations
- EquivalentSources_Regenerates: documents that semantically equivalent
sources trigger regeneration (pre-existing limitation due to Dictionary
in model lacking value equality)
- DifferentSources_Regenerates: verifies model changes trigger output
- SourceGenModelDoesNotEncapsulateSymbolsOrCompilationData: walks the
object graph to ensure no Compilation/ISymbol references leak
Also adds SourceGenerationTrackingName constant and WithTrackingName()
to the source pipeline to enable step tracking.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings February 28, 2026 14:09

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • The catch (Exception ex) { throw ex; } pattern resets the original stack trace. If the intent is just to propagate, use throw; (or remove the try/catch entirely) so failures in the generator preserve useful call stacks.
 catch (Exception ex)
{
throw ex;
}

Replace inline lambda callbacks in the diagnostic pipelines with named
EmitDiagnostics static methods, complementing the existing EmitSource
methods in all 4 generators (JSON, Logger, ConfigBinder, Regex).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.

Create named IncrementalValueProvider variables for the equatable model
projections in all 4 generators, with detailed comments explaining how
Roslyn's Select operator uses model equality to guard source production.
For the Regex generator, also extract the source emission lambda into a
named EmitSource method, complementing EmitDiagnostics.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis
eiriktsarpalisforce-pushed the fix/sourcegen-suppressions branch from 3323beb to a4e500eCompareMarch 2, 2026 17:10
Create named IncrementalValueProvider variables for the diagnostic
projections in all 4 generators, with comments explaining that
ImmutableArray<Diagnostic> uses reference equality in the incremental
pipeline — the callback fires on every compilation change by design.
This also simplifies the EmitDiagnostics signatures to accept just
the projected diagnostics rather than the full model+diagnostics tuple.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings March 2, 2026 17:16

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • Avoid throw ex; here; it resets the original stack trace and makes failures harder to diagnose. Use throw; to preserve the stack trace, or remove the try/catch entirely if it's only rethrowing.
 catch (Exception ex)
{
throw ex;
}

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • throw ex; resets the stack trace and makes failures harder to diagnose. Either remove this try/catch (letting the original exception propagate) or use a bare throw; to preserve the original stack trace.
 catch (Exception ex)
{
throw ex;
}

You can also share your feedback on Copilot code review. Take the survey.

RegexPatternAndSyntax is not part of the incremental model — it is
consumed in the first Select and never reaches the Collect phase.
Restoring DiagnosticLocation simplifies the first Select by removing
the (RegexPatternAndSyntax, Location) tuple indirection. RegexMethod
remains Location-free since it is part of the cached model.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis
eiriktsarpalis marked this pull request as ready for review March 10, 2026 21:14
CopilotAI review requested due to automatic review settings March 10, 2026 21:14
@eiriktsarpaliseiriktsarpalis added the source-generator Indicates an issue with a source generator feature label Mar 10, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.


You can also share your feedback on Copilot code review. Take the survey.

@ericstjericstj left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to do anything in the interop generators?

@ericstjericstj left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good and surprisingly minimal. Feedback is straightforward fix or non-blocking.

- Delete Common/src/SourceGenerators/DiagnosticInfo.cs (no longer used)
- Remove orphaned DiagnosticInfo.cs Compile Include from Logging and STJ
targets files
- Add diagnostic.GetMessage() to Logger dedup key to avoid collapsing
distinct diagnostics with same Id/location but different messages
- Add pragma suppression test cases: negative (no pragma), partial
(multiple diagnostics, only some suppressed), and scoping (suppress
then restore before diagnostic site)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis

Copy link
Copy Markdown
MemberAuthor

/ba-g stalled browser-wasm tests. Changes are compile-time only.

@eiriktsarpalis
eiriktsarpalis merged commit 2c7ee19 into mainMar 11, 2026
88 of 90 checks passed
@eiriktsarpalis
eiriktsarpalis deleted the fix/sourcegen-suppressions branch March 11, 2026 09:41
CopilotAI pushed a commit that referenced this pull request Mar 13, 2026
…124994)
## Summary
Source generator diagnostics emitted by incremental generators in
dotnet/runtime can now be suppressed using `#pragma warning disable`
inline directives.
Fixes#92509
## Problem
Incremental source generators that store diagnostic locations in
equatable intermediate representations "trim" the `Location` to avoid
holding references to the `Compilation` object. The standard workaround
— `Location.Create(filePath, textSpan, linePositionSpan)` — creates an
`ExternalFileLocation` (`LocationKind.ExternalFile`) which **bypasses
Roslyn's pragma suppression checks**. Only `SourceLocation`
(`LocationKind.SourceFile`) instances, created via
`Location.Create(SyntaxTree, TextSpan)`, are checked against the
`SyntaxTree`'s pragma directive table.
This is the issue described in
[dotnet/roslyn#68291](dotnet/roslyn#68291).
## Solution
Following the technique first applied in
[eiriktsarpalis/PolyType#401](eiriktsarpalis/PolyType#401),
split each affected generator's `RegisterSourceOutput` pipeline into two
separate pipelines:
1. **Source generation pipeline** — Fully incremental. Uses `Select` to
extract just the equatable model (which contains no
`Location`/`SyntaxTree` references), deduplicates by model equality,
only re-fires on structural changes.
2. **Diagnostic pipeline** — Reports raw `Diagnostic` objects that
preserve the original `SourceLocation` (`LocationKind.SourceFile`) from
the syntax tree. This enables Roslyn to check the `SyntaxTree`'s pragma
directive table for `#pragma warning disable` directives.
Parsers now create `Diagnostic.Create(descriptor, location, ...)`
directly instead of wrapping in `DiagnosticInfo.Create(...)` which
trimmed the location. Since `ImmutableArray<Diagnostic>` does not have
value equality, the diagnostic pipeline fires more frequently but the
work is trivially cheap (just iterating and reporting).
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Frulfump

Copy link
Copy Markdown

Can this one be backported to 10.0.1xx and 10.0.3xx? (If that happens I assume it would also automatically be a part of upcoming 10.0.4xx?)

@eiriktsarpalis

Copy link
Copy Markdown
MemberAuthor

This is not something we could backport to .NET 10, unfortunately.

@Frulfump

Copy link
Copy Markdown

Ah ok thanks for responding. Looking forward to .NET 11 then, is it available in preview 3 or will it be preview 4?

@eiriktsarpalis

Copy link
Copy Markdown
MemberAuthor

Preview 4 I think.

@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 16, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Text.Jsonsource-generatorIndicates an issue with a source generator feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Address diagnostic issues in runtime incremental source generators

4 participants

@eiriktsarpalis@Frulfump@ericstj
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix source generator diagnostics to support #pragma warning disable - #124994

Merged
eiriktsarpalis merged 30 commits into
mainfrom
fix/sourcegen-suppressions
Mar 11, 2026
Merged

Fix source generator diagnostics to support #pragma warning disable#124994
eiriktsarpalis merged 30 commits into
mainfrom
fix/sourcegen-suppressions

Conversation

@eiriktsarpalis

@eiriktsarpaliseiriktsarpalis commented Feb 27, 2026

Copy link
Copy Markdown
Member

Summary

Source generator diagnostics emitted by incremental generators in dotnet/runtime can now be suppressed using #pragma warning disable inline directives.

Fixes#92509

Problem

Incremental source generators that store diagnostic locations in equatable intermediate representations "trim" the Location to avoid holding references to the Compilation object. The standard workaround — Location.Create(filePath, textSpan, linePositionSpan) — creates an ExternalFileLocation (LocationKind.ExternalFile) which bypasses Roslyn's pragma suppression checks. Only SourceLocation (LocationKind.SourceFile) instances, created via Location.Create(SyntaxTree, TextSpan), are checked against the SyntaxTree's pragma directive table.

This is the issue described in dotnet/roslyn#68291.

Solution

Following the technique first applied in eiriktsarpalis/PolyType#401, split each affected generator's RegisterSourceOutput pipeline into two separate pipelines:

  1. Source generation pipeline — Fully incremental. Uses Select to extract just the equatable model (which contains no Location/SyntaxTree references), deduplicates by model equality, only re-fires on structural changes.
  2. Diagnostic pipeline — Reports raw Diagnostic objects that preserve the original SourceLocation (LocationKind.SourceFile) from the syntax tree. This enables Roslyn to check the SyntaxTree's pragma directive table for #pragma warning disable directives.

Parsers now create Diagnostic.Create(descriptor, location, ...) directly instead of wrapping in DiagnosticInfo.Create(...) which trimmed the location. Since ImmutableArray<Diagnostic> does not have value equality, the diagnostic pipeline fires more frequently but the work is trivially cheap (just iterating and reporting).

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Enables Roslyn pragma-based suppression (#pragma warning disable) for diagnostics emitted by several incremental source generators by separating source emission from diagnostic emission and recreating diagnostics with SourceFile locations recovered from the current Compilation.

Changes:

  • Split generator pipelines into (1) fully-incremental source generation and (2) diagnostics combined with CompilationProvider for pragma-suppressible locations.
  • Add DiagnosticInfo.CreateDiagnostic(Compilation) (and Regex-specific equivalent) to rebuild Location as LocationKind.SourceFile.
  • Update Json source generator incremental test commentary to reflect the new diagnostics pipeline behavior.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.

Show a summary per file
FileDescription
src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.csAdds a diagnostics-only pipeline and recreates diagnostics with SourceFile locations.
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorIncrementalTests.csAdjusts incremental-model encapsulation test commentary given diagnostics now reference SyntaxTree.
src/libraries/System.Text.Json/gen/JsonSourceGenerator.Roslyn4.0.csSplits Json SG into separate source + diagnostics pipelines; diagnostics now created with compilation context.
src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Roslyn4.0.csSplits LoggerMessage SG pipelines and preserves diagnostic deduping while making locations pragma-suppressible.
src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.csSplits binder SG pipelines and reports diagnostics using compilation-backed SourceFile locations.
src/libraries/Common/src/SourceGenerators/DiagnosticInfo.csAdds CreateDiagnostic(Compilation) to recreate pragma-suppressible SourceFile locations from trimmed locations.

Comment threadsrc/libraries/Common/src/SourceGenerators/DiagnosticInfo.cs Outdated
Comment threadsrc/libraries/Common/src/SourceGenerators/DiagnosticInfo.cs Outdated
Comment threadsrc/libraries/System.Text.RegularExpressions/gen/RegexGenerator.cs Outdated
Split each affected generator's RegisterSourceOutput pipeline into two
separate pipelines:
1. Source generation pipeline - fully incremental, uses Select to extract
just the equatable model. Only re-fires on structural changes.
2. Diagnostic pipeline - combines with CompilationProvider to recover the
SyntaxTree from the Compilation at emission time. Uses
Location.Create(SyntaxTree, TextSpan) to produce SourceLocation
instances that support pragma suppression checks.
Affected generators:
- System.Text.Json (JsonSourceGenerator)
- Microsoft.Extensions.Logging (LoggerMessageGenerator)
- Microsoft.Extensions.Configuration.Binder (ConfigurationBindingGenerator)
- System.Text.RegularExpressions (RegexGenerator)
The shared DiagnosticInfo type gains a CreateDiagnostic(Compilation) overload
that recovers the SyntaxTree from the trimmed ExternalFileLocation's file
path, converting it back to a SourceLocation. The RegexGenerator's private
DiagnosticData type gets an analogous ToDiagnostic(Compilation) overload.
Fixes#92509
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis
eiriktsarpalisforce-pushed the fix/sourcegen-suppressions branch from 50000b3 to e95c4adCompareFebruary 28, 2026 09:42
CopilotAI review requested due to automatic review settings February 28, 2026 09:42

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • The catch block rethrows with throw ex;, which resets the exception stack trace. Use throw; (or remove the catch entirely) to preserve the original call stack for diagnosing generator failures.
 catch (Exception ex)
{
throw ex;
}

…tors
Verifies that diagnostics from all 4 affected source generators (Regex,
JSON, Logger, ConfigBinder) have LocationKind.SourceFile, which is the
prerequisite for #pragma warning disable to work. Before this fix,
diagnostics had LocationKind.ExternalFile which bypasses Roslyn's pragma
suppression checks.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds RegexGeneratorIncrementalTests with 4 tests following the patterns
established by JsonSourceGeneratorIncrementalTests and ConfigBinder's
GeneratorTests.Incremental.cs:
- SameInput_DoesNotRegenerate: verifies caching on identical compilations
- EquivalentSources_Regenerates: documents that semantically equivalent
sources trigger regeneration (pre-existing limitation due to Dictionary
in model lacking value equality)
- DifferentSources_Regenerates: verifies model changes trigger output
- SourceGenModelDoesNotEncapsulateSymbolsOrCompilationData: walks the
object graph to ensure no Compilation/ISymbol references leak
Also adds SourceGenerationTrackingName constant and WithTrackingName()
to the source pipeline to enable step tracking.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings February 28, 2026 14:09

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • The catch (Exception ex) { throw ex; } pattern resets the original stack trace. If the intent is just to propagate, use throw; (or remove the try/catch entirely) so failures in the generator preserve useful call stacks.
 catch (Exception ex)
{
throw ex;
}

Replace inline lambda callbacks in the diagnostic pipelines with named
EmitDiagnostics static methods, complementing the existing EmitSource
methods in all 4 generators (JSON, Logger, ConfigBinder, Regex).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.

Create named IncrementalValueProvider variables for the equatable model
projections in all 4 generators, with detailed comments explaining how
Roslyn's Select operator uses model equality to guard source production.
For the Regex generator, also extract the source emission lambda into a
named EmitSource method, complementing EmitDiagnostics.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis
eiriktsarpalisforce-pushed the fix/sourcegen-suppressions branch from 3323beb to a4e500eCompareMarch 2, 2026 17:10
Create named IncrementalValueProvider variables for the diagnostic
projections in all 4 generators, with comments explaining that
ImmutableArray<Diagnostic> uses reference equality in the incremental
pipeline — the callback fires on every compilation change by design.
This also simplifies the EmitDiagnostics signatures to accept just
the projected diagnostics rather than the full model+diagnostics tuple.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings March 2, 2026 17:16

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • Avoid throw ex; here; it resets the original stack trace and makes failures harder to diagnose. Use throw; to preserve the stack trace, or remove the try/catch entirely if it's only rethrowing.
 catch (Exception ex)
{
throw ex;
}

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • throw ex; resets the stack trace and makes failures harder to diagnose. Either remove this try/catch (letting the original exception propagate) or use a bare throw; to preserve the original stack trace.
 catch (Exception ex)
{
throw ex;
}

You can also share your feedback on Copilot code review. Take the survey.

RegexPatternAndSyntax is not part of the incremental model — it is
consumed in the first Select and never reaches the Collect phase.
Restoring DiagnosticLocation simplifies the first Select by removing
the (RegexPatternAndSyntax, Location) tuple indirection. RegexMethod
remains Location-free since it is part of the cached model.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis
eiriktsarpalis marked this pull request as ready for review March 10, 2026 21:14
CopilotAI review requested due to automatic review settings March 10, 2026 21:14
@eiriktsarpaliseiriktsarpalis added the source-generator Indicates an issue with a source generator feature label Mar 10, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.


You can also share your feedback on Copilot code review. Take the survey.

@ericstjericstj left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to do anything in the interop generators?

@ericstjericstj left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good and surprisingly minimal. Feedback is straightforward fix or non-blocking.

- Delete Common/src/SourceGenerators/DiagnosticInfo.cs (no longer used)
- Remove orphaned DiagnosticInfo.cs Compile Include from Logging and STJ
targets files
- Add diagnostic.GetMessage() to Logger dedup key to avoid collapsing
distinct diagnostics with same Id/location but different messages
- Add pragma suppression test cases: negative (no pragma), partial
(multiple diagnostics, only some suppressed), and scoping (suppress
then restore before diagnostic site)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis

Copy link
Copy Markdown
MemberAuthor

/ba-g stalled browser-wasm tests. Changes are compile-time only.

@eiriktsarpalis
eiriktsarpalis merged commit 2c7ee19 into mainMar 11, 2026
88 of 90 checks passed
@eiriktsarpalis
eiriktsarpalis deleted the fix/sourcegen-suppressions branch March 11, 2026 09:41
CopilotAI pushed a commit that referenced this pull request Mar 13, 2026
…124994)
## Summary
Source generator diagnostics emitted by incremental generators in
dotnet/runtime can now be suppressed using `#pragma warning disable`
inline directives.
Fixes#92509
## Problem
Incremental source generators that store diagnostic locations in
equatable intermediate representations "trim" the `Location` to avoid
holding references to the `Compilation` object. The standard workaround
— `Location.Create(filePath, textSpan, linePositionSpan)` — creates an
`ExternalFileLocation` (`LocationKind.ExternalFile`) which **bypasses
Roslyn's pragma suppression checks**. Only `SourceLocation`
(`LocationKind.SourceFile`) instances, created via
`Location.Create(SyntaxTree, TextSpan)`, are checked against the
`SyntaxTree`'s pragma directive table.
This is the issue described in
[dotnet/roslyn#68291](dotnet/roslyn#68291).
## Solution
Following the technique first applied in
[eiriktsarpalis/PolyType#401](eiriktsarpalis/PolyType#401),
split each affected generator's `RegisterSourceOutput` pipeline into two
separate pipelines:
1. **Source generation pipeline** — Fully incremental. Uses `Select` to
extract just the equatable model (which contains no
`Location`/`SyntaxTree` references), deduplicates by model equality,
only re-fires on structural changes.
2. **Diagnostic pipeline** — Reports raw `Diagnostic` objects that
preserve the original `SourceLocation` (`LocationKind.SourceFile`) from
the syntax tree. This enables Roslyn to check the `SyntaxTree`'s pragma
directive table for `#pragma warning disable` directives.
Parsers now create `Diagnostic.Create(descriptor, location, ...)`
directly instead of wrapping in `DiagnosticInfo.Create(...)` which
trimmed the location. Since `ImmutableArray<Diagnostic>` does not have
value equality, the diagnostic pipeline fires more frequently but the
work is trivially cheap (just iterating and reporting).
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Frulfump

Copy link
Copy Markdown

Can this one be backported to 10.0.1xx and 10.0.3xx? (If that happens I assume it would also automatically be a part of upcoming 10.0.4xx?)

@eiriktsarpalis

Copy link
Copy Markdown
MemberAuthor

This is not something we could backport to .NET 10, unfortunately.

@Frulfump

Copy link
Copy Markdown

Ah ok thanks for responding. Looking forward to .NET 11 then, is it available in preview 3 or will it be preview 4?

@eiriktsarpalis

Copy link
Copy Markdown
MemberAuthor

Preview 4 I think.

@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 16, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Text.Jsonsource-generatorIndicates an issue with a source generator feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Address diagnostic issues in runtime incremental source generators

4 participants

@eiriktsarpalis@Frulfump@ericstj
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix source generator diagnostics to support #pragma warning disable - #124994

Merged
eiriktsarpalis merged 30 commits into
mainfrom
fix/sourcegen-suppressions
Mar 11, 2026
Merged

Fix source generator diagnostics to support #pragma warning disable#124994
eiriktsarpalis merged 30 commits into
mainfrom
fix/sourcegen-suppressions

Conversation

@eiriktsarpalis

@eiriktsarpaliseiriktsarpalis commented Feb 27, 2026

Copy link
Copy Markdown
Member

Summary

Source generator diagnostics emitted by incremental generators in dotnet/runtime can now be suppressed using #pragma warning disable inline directives.

Fixes#92509

Problem

Incremental source generators that store diagnostic locations in equatable intermediate representations "trim" the Location to avoid holding references to the Compilation object. The standard workaround — Location.Create(filePath, textSpan, linePositionSpan) — creates an ExternalFileLocation (LocationKind.ExternalFile) which bypasses Roslyn's pragma suppression checks. Only SourceLocation (LocationKind.SourceFile) instances, created via Location.Create(SyntaxTree, TextSpan), are checked against the SyntaxTree's pragma directive table.

This is the issue described in dotnet/roslyn#68291.

Solution

Following the technique first applied in eiriktsarpalis/PolyType#401, split each affected generator's RegisterSourceOutput pipeline into two separate pipelines:

  1. Source generation pipeline — Fully incremental. Uses Select to extract just the equatable model (which contains no Location/SyntaxTree references), deduplicates by model equality, only re-fires on structural changes.
  2. Diagnostic pipeline — Reports raw Diagnostic objects that preserve the original SourceLocation (LocationKind.SourceFile) from the syntax tree. This enables Roslyn to check the SyntaxTree's pragma directive table for #pragma warning disable directives.

Parsers now create Diagnostic.Create(descriptor, location, ...) directly instead of wrapping in DiagnosticInfo.Create(...) which trimmed the location. Since ImmutableArray<Diagnostic> does not have value equality, the diagnostic pipeline fires more frequently but the work is trivially cheap (just iterating and reporting).

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Enables Roslyn pragma-based suppression (#pragma warning disable) for diagnostics emitted by several incremental source generators by separating source emission from diagnostic emission and recreating diagnostics with SourceFile locations recovered from the current Compilation.

Changes:

  • Split generator pipelines into (1) fully-incremental source generation and (2) diagnostics combined with CompilationProvider for pragma-suppressible locations.
  • Add DiagnosticInfo.CreateDiagnostic(Compilation) (and Regex-specific equivalent) to rebuild Location as LocationKind.SourceFile.
  • Update Json source generator incremental test commentary to reflect the new diagnostics pipeline behavior.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.

Show a summary per file
FileDescription
src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.csAdds a diagnostics-only pipeline and recreates diagnostics with SourceFile locations.
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorIncrementalTests.csAdjusts incremental-model encapsulation test commentary given diagnostics now reference SyntaxTree.
src/libraries/System.Text.Json/gen/JsonSourceGenerator.Roslyn4.0.csSplits Json SG into separate source + diagnostics pipelines; diagnostics now created with compilation context.
src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Roslyn4.0.csSplits LoggerMessage SG pipelines and preserves diagnostic deduping while making locations pragma-suppressible.
src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.csSplits binder SG pipelines and reports diagnostics using compilation-backed SourceFile locations.
src/libraries/Common/src/SourceGenerators/DiagnosticInfo.csAdds CreateDiagnostic(Compilation) to recreate pragma-suppressible SourceFile locations from trimmed locations.

Comment threadsrc/libraries/Common/src/SourceGenerators/DiagnosticInfo.cs Outdated
Comment threadsrc/libraries/Common/src/SourceGenerators/DiagnosticInfo.cs Outdated
Comment threadsrc/libraries/System.Text.RegularExpressions/gen/RegexGenerator.cs Outdated
Split each affected generator's RegisterSourceOutput pipeline into two
separate pipelines:
1. Source generation pipeline - fully incremental, uses Select to extract
just the equatable model. Only re-fires on structural changes.
2. Diagnostic pipeline - combines with CompilationProvider to recover the
SyntaxTree from the Compilation at emission time. Uses
Location.Create(SyntaxTree, TextSpan) to produce SourceLocation
instances that support pragma suppression checks.
Affected generators:
- System.Text.Json (JsonSourceGenerator)
- Microsoft.Extensions.Logging (LoggerMessageGenerator)
- Microsoft.Extensions.Configuration.Binder (ConfigurationBindingGenerator)
- System.Text.RegularExpressions (RegexGenerator)
The shared DiagnosticInfo type gains a CreateDiagnostic(Compilation) overload
that recovers the SyntaxTree from the trimmed ExternalFileLocation's file
path, converting it back to a SourceLocation. The RegexGenerator's private
DiagnosticData type gets an analogous ToDiagnostic(Compilation) overload.
Fixes#92509
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis
eiriktsarpalisforce-pushed the fix/sourcegen-suppressions branch from 50000b3 to e95c4adCompareFebruary 28, 2026 09:42
CopilotAI review requested due to automatic review settings February 28, 2026 09:42

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • The catch block rethrows with throw ex;, which resets the exception stack trace. Use throw; (or remove the catch entirely) to preserve the original call stack for diagnosing generator failures.
 catch (Exception ex)
{
throw ex;
}

…tors
Verifies that diagnostics from all 4 affected source generators (Regex,
JSON, Logger, ConfigBinder) have LocationKind.SourceFile, which is the
prerequisite for #pragma warning disable to work. Before this fix,
diagnostics had LocationKind.ExternalFile which bypasses Roslyn's pragma
suppression checks.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds RegexGeneratorIncrementalTests with 4 tests following the patterns
established by JsonSourceGeneratorIncrementalTests and ConfigBinder's
GeneratorTests.Incremental.cs:
- SameInput_DoesNotRegenerate: verifies caching on identical compilations
- EquivalentSources_Regenerates: documents that semantically equivalent
sources trigger regeneration (pre-existing limitation due to Dictionary
in model lacking value equality)
- DifferentSources_Regenerates: verifies model changes trigger output
- SourceGenModelDoesNotEncapsulateSymbolsOrCompilationData: walks the
object graph to ensure no Compilation/ISymbol references leak
Also adds SourceGenerationTrackingName constant and WithTrackingName()
to the source pipeline to enable step tracking.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings February 28, 2026 14:09

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • The catch (Exception ex) { throw ex; } pattern resets the original stack trace. If the intent is just to propagate, use throw; (or remove the try/catch entirely) so failures in the generator preserve useful call stacks.
 catch (Exception ex)
{
throw ex;
}

Replace inline lambda callbacks in the diagnostic pipelines with named
EmitDiagnostics static methods, complementing the existing EmitSource
methods in all 4 generators (JSON, Logger, ConfigBinder, Regex).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.

Create named IncrementalValueProvider variables for the equatable model
projections in all 4 generators, with detailed comments explaining how
Roslyn's Select operator uses model equality to guard source production.
For the Regex generator, also extract the source emission lambda into a
named EmitSource method, complementing EmitDiagnostics.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis
eiriktsarpalisforce-pushed the fix/sourcegen-suppressions branch from 3323beb to a4e500eCompareMarch 2, 2026 17:10
Create named IncrementalValueProvider variables for the diagnostic
projections in all 4 generators, with comments explaining that
ImmutableArray<Diagnostic> uses reference equality in the incremental
pipeline — the callback fires on every compilation change by design.
This also simplifies the EmitDiagnostics signatures to accept just
the projected diagnostics rather than the full model+diagnostics tuple.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings March 2, 2026 17:16

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • Avoid throw ex; here; it resets the original stack trace and makes failures harder to diagnose. Use throw; to preserve the stack trace, or remove the try/catch entirely if it's only rethrowing.
 catch (Exception ex)
{
throw ex;
}

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • throw ex; resets the stack trace and makes failures harder to diagnose. Either remove this try/catch (letting the original exception propagate) or use a bare throw; to preserve the original stack trace.
 catch (Exception ex)
{
throw ex;
}

You can also share your feedback on Copilot code review. Take the survey.

RegexPatternAndSyntax is not part of the incremental model — it is
consumed in the first Select and never reaches the Collect phase.
Restoring DiagnosticLocation simplifies the first Select by removing
the (RegexPatternAndSyntax, Location) tuple indirection. RegexMethod
remains Location-free since it is part of the cached model.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis
eiriktsarpalis marked this pull request as ready for review March 10, 2026 21:14
CopilotAI review requested due to automatic review settings March 10, 2026 21:14
@eiriktsarpaliseiriktsarpalis added the source-generator Indicates an issue with a source generator feature label Mar 10, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.


You can also share your feedback on Copilot code review. Take the survey.

@ericstjericstj left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to do anything in the interop generators?

@ericstjericstj left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good and surprisingly minimal. Feedback is straightforward fix or non-blocking.

- Delete Common/src/SourceGenerators/DiagnosticInfo.cs (no longer used)
- Remove orphaned DiagnosticInfo.cs Compile Include from Logging and STJ
targets files
- Add diagnostic.GetMessage() to Logger dedup key to avoid collapsing
distinct diagnostics with same Id/location but different messages
- Add pragma suppression test cases: negative (no pragma), partial
(multiple diagnostics, only some suppressed), and scoping (suppress
then restore before diagnostic site)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis

Copy link
Copy Markdown
MemberAuthor

/ba-g stalled browser-wasm tests. Changes are compile-time only.

@eiriktsarpalis
eiriktsarpalis merged commit 2c7ee19 into mainMar 11, 2026
88 of 90 checks passed
@eiriktsarpalis
eiriktsarpalis deleted the fix/sourcegen-suppressions branch March 11, 2026 09:41
CopilotAI pushed a commit that referenced this pull request Mar 13, 2026
…124994)
## Summary
Source generator diagnostics emitted by incremental generators in
dotnet/runtime can now be suppressed using `#pragma warning disable`
inline directives.
Fixes#92509
## Problem
Incremental source generators that store diagnostic locations in
equatable intermediate representations "trim" the `Location` to avoid
holding references to the `Compilation` object. The standard workaround
— `Location.Create(filePath, textSpan, linePositionSpan)` — creates an
`ExternalFileLocation` (`LocationKind.ExternalFile`) which **bypasses
Roslyn's pragma suppression checks**. Only `SourceLocation`
(`LocationKind.SourceFile`) instances, created via
`Location.Create(SyntaxTree, TextSpan)`, are checked against the
`SyntaxTree`'s pragma directive table.
This is the issue described in
[dotnet/roslyn#68291](dotnet/roslyn#68291).
## Solution
Following the technique first applied in
[eiriktsarpalis/PolyType#401](eiriktsarpalis/PolyType#401),
split each affected generator's `RegisterSourceOutput` pipeline into two
separate pipelines:
1. **Source generation pipeline** — Fully incremental. Uses `Select` to
extract just the equatable model (which contains no
`Location`/`SyntaxTree` references), deduplicates by model equality,
only re-fires on structural changes.
2. **Diagnostic pipeline** — Reports raw `Diagnostic` objects that
preserve the original `SourceLocation` (`LocationKind.SourceFile`) from
the syntax tree. This enables Roslyn to check the `SyntaxTree`'s pragma
directive table for `#pragma warning disable` directives.
Parsers now create `Diagnostic.Create(descriptor, location, ...)`
directly instead of wrapping in `DiagnosticInfo.Create(...)` which
trimmed the location. Since `ImmutableArray<Diagnostic>` does not have
value equality, the diagnostic pipeline fires more frequently but the
work is trivially cheap (just iterating and reporting).
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Frulfump

Copy link
Copy Markdown

Can this one be backported to 10.0.1xx and 10.0.3xx? (If that happens I assume it would also automatically be a part of upcoming 10.0.4xx?)

@eiriktsarpalis

Copy link
Copy Markdown
MemberAuthor

This is not something we could backport to .NET 10, unfortunately.

@Frulfump

Copy link
Copy Markdown

Ah ok thanks for responding. Looking forward to .NET 11 then, is it available in preview 3 or will it be preview 4?

@eiriktsarpalis

Copy link
Copy Markdown
MemberAuthor

Preview 4 I think.

@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 16, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Text.Jsonsource-generatorIndicates an issue with a source generator feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Address diagnostic issues in runtime incremental source generators

4 participants

@eiriktsarpalis@Frulfump@ericstj
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Fix source generator diagnostics to support #pragma warning disable - #124994

Merged
eiriktsarpalis merged 30 commits into
mainfrom
fix/sourcegen-suppressions
Mar 11, 2026
Merged

Fix source generator diagnostics to support #pragma warning disable#124994
eiriktsarpalis merged 30 commits into
mainfrom
fix/sourcegen-suppressions

Conversation

@eiriktsarpalis

@eiriktsarpaliseiriktsarpalis commented Feb 27, 2026

Copy link
Copy Markdown
Member

Summary

Source generator diagnostics emitted by incremental generators in dotnet/runtime can now be suppressed using #pragma warning disable inline directives.

Fixes#92509

Problem

Incremental source generators that store diagnostic locations in equatable intermediate representations "trim" the Location to avoid holding references to the Compilation object. The standard workaround — Location.Create(filePath, textSpan, linePositionSpan) — creates an ExternalFileLocation (LocationKind.ExternalFile) which bypasses Roslyn's pragma suppression checks. Only SourceLocation (LocationKind.SourceFile) instances, created via Location.Create(SyntaxTree, TextSpan), are checked against the SyntaxTree's pragma directive table.

This is the issue described in dotnet/roslyn#68291.

Solution

Following the technique first applied in eiriktsarpalis/PolyType#401, split each affected generator's RegisterSourceOutput pipeline into two separate pipelines:

  1. Source generation pipeline — Fully incremental. Uses Select to extract just the equatable model (which contains no Location/SyntaxTree references), deduplicates by model equality, only re-fires on structural changes.
  2. Diagnostic pipeline — Reports raw Diagnostic objects that preserve the original SourceLocation (LocationKind.SourceFile) from the syntax tree. This enables Roslyn to check the SyntaxTree's pragma directive table for #pragma warning disable directives.

Parsers now create Diagnostic.Create(descriptor, location, ...) directly instead of wrapping in DiagnosticInfo.Create(...) which trimmed the location. Since ImmutableArray<Diagnostic> does not have value equality, the diagnostic pipeline fires more frequently but the work is trivially cheap (just iterating and reporting).

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Enables Roslyn pragma-based suppression (#pragma warning disable) for diagnostics emitted by several incremental source generators by separating source emission from diagnostic emission and recreating diagnostics with SourceFile locations recovered from the current Compilation.

Changes:

  • Split generator pipelines into (1) fully-incremental source generation and (2) diagnostics combined with CompilationProvider for pragma-suppressible locations.
  • Add DiagnosticInfo.CreateDiagnostic(Compilation) (and Regex-specific equivalent) to rebuild Location as LocationKind.SourceFile.
  • Update Json source generator incremental test commentary to reflect the new diagnostics pipeline behavior.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.

Show a summary per file
FileDescription
src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.csAdds a diagnostics-only pipeline and recreates diagnostics with SourceFile locations.
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorIncrementalTests.csAdjusts incremental-model encapsulation test commentary given diagnostics now reference SyntaxTree.
src/libraries/System.Text.Json/gen/JsonSourceGenerator.Roslyn4.0.csSplits Json SG into separate source + diagnostics pipelines; diagnostics now created with compilation context.
src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Roslyn4.0.csSplits LoggerMessage SG pipelines and preserves diagnostic deduping while making locations pragma-suppressible.
src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.csSplits binder SG pipelines and reports diagnostics using compilation-backed SourceFile locations.
src/libraries/Common/src/SourceGenerators/DiagnosticInfo.csAdds CreateDiagnostic(Compilation) to recreate pragma-suppressible SourceFile locations from trimmed locations.

Comment threadsrc/libraries/Common/src/SourceGenerators/DiagnosticInfo.cs Outdated
Comment threadsrc/libraries/Common/src/SourceGenerators/DiagnosticInfo.cs Outdated
Comment threadsrc/libraries/System.Text.RegularExpressions/gen/RegexGenerator.cs Outdated
Split each affected generator's RegisterSourceOutput pipeline into two
separate pipelines:
1. Source generation pipeline - fully incremental, uses Select to extract
just the equatable model. Only re-fires on structural changes.
2. Diagnostic pipeline - combines with CompilationProvider to recover the
SyntaxTree from the Compilation at emission time. Uses
Location.Create(SyntaxTree, TextSpan) to produce SourceLocation
instances that support pragma suppression checks.
Affected generators:
- System.Text.Json (JsonSourceGenerator)
- Microsoft.Extensions.Logging (LoggerMessageGenerator)
- Microsoft.Extensions.Configuration.Binder (ConfigurationBindingGenerator)
- System.Text.RegularExpressions (RegexGenerator)
The shared DiagnosticInfo type gains a CreateDiagnostic(Compilation) overload
that recovers the SyntaxTree from the trimmed ExternalFileLocation's file
path, converting it back to a SourceLocation. The RegexGenerator's private
DiagnosticData type gets an analogous ToDiagnostic(Compilation) overload.
Fixes#92509
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis
eiriktsarpalisforce-pushed the fix/sourcegen-suppressions branch from 50000b3 to e95c4adCompareFebruary 28, 2026 09:42
CopilotAI review requested due to automatic review settings February 28, 2026 09:42

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • The catch block rethrows with throw ex;, which resets the exception stack trace. Use throw; (or remove the catch entirely) to preserve the original call stack for diagnosing generator failures.
 catch (Exception ex)
{
throw ex;
}

…tors
Verifies that diagnostics from all 4 affected source generators (Regex,
JSON, Logger, ConfigBinder) have LocationKind.SourceFile, which is the
prerequisite for #pragma warning disable to work. Before this fix,
diagnostics had LocationKind.ExternalFile which bypasses Roslyn's pragma
suppression checks.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds RegexGeneratorIncrementalTests with 4 tests following the patterns
established by JsonSourceGeneratorIncrementalTests and ConfigBinder's
GeneratorTests.Incremental.cs:
- SameInput_DoesNotRegenerate: verifies caching on identical compilations
- EquivalentSources_Regenerates: documents that semantically equivalent
sources trigger regeneration (pre-existing limitation due to Dictionary
in model lacking value equality)
- DifferentSources_Regenerates: verifies model changes trigger output
- SourceGenModelDoesNotEncapsulateSymbolsOrCompilationData: walks the
object graph to ensure no Compilation/ISymbol references leak
Also adds SourceGenerationTrackingName constant and WithTrackingName()
to the source pipeline to enable step tracking.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings February 28, 2026 14:09

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • The catch (Exception ex) { throw ex; } pattern resets the original stack trace. If the intent is just to propagate, use throw; (or remove the try/catch entirely) so failures in the generator preserve useful call stacks.
 catch (Exception ex)
{
throw ex;
}

Replace inline lambda callbacks in the diagnostic pipelines with named
EmitDiagnostics static methods, complementing the existing EmitSource
methods in all 4 generators (JSON, Logger, ConfigBinder, Regex).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.

Create named IncrementalValueProvider variables for the equatable model
projections in all 4 generators, with detailed comments explaining how
Roslyn's Select operator uses model equality to guard source production.
For the Regex generator, also extract the source emission lambda into a
named EmitSource method, complementing EmitDiagnostics.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis
eiriktsarpalisforce-pushed the fix/sourcegen-suppressions branch from 3323beb to a4e500eCompareMarch 2, 2026 17:10
Create named IncrementalValueProvider variables for the diagnostic
projections in all 4 generators, with comments explaining that
ImmutableArray<Diagnostic> uses reference equality in the incremental
pipeline — the callback fires on every compilation change by design.
This also simplifies the EmitDiagnostics signatures to accept just
the projected diagnostics rather than the full model+diagnostics tuple.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings March 2, 2026 17:16

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • Avoid throw ex; here; it resets the original stack trace and makes failures harder to diagnose. Use throw; to preserve the stack trace, or remove the try/catch entirely if it's only rethrowing.
 catch (Exception ex)
{
throw ex;
}

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs:66

  • throw ex; resets the stack trace and makes failures harder to diagnose. Either remove this try/catch (letting the original exception propagate) or use a bare throw; to preserve the original stack trace.
 catch (Exception ex)
{
throw ex;
}

You can also share your feedback on Copilot code review. Take the survey.

RegexPatternAndSyntax is not part of the incremental model — it is
consumed in the first Select and never reaches the Collect phase.
Restoring DiagnosticLocation simplifies the first Select by removing
the (RegexPatternAndSyntax, Location) tuple indirection. RegexMethod
remains Location-free since it is part of the cached model.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis
eiriktsarpalis marked this pull request as ready for review March 10, 2026 21:14
CopilotAI review requested due to automatic review settings March 10, 2026 21:14
@eiriktsarpaliseiriktsarpalis added the source-generator Indicates an issue with a source generator feature label Mar 10, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.


You can also share your feedback on Copilot code review. Take the survey.

@ericstjericstj left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to do anything in the interop generators?

@ericstjericstj left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good and surprisingly minimal. Feedback is straightforward fix or non-blocking.

- Delete Common/src/SourceGenerators/DiagnosticInfo.cs (no longer used)
- Remove orphaned DiagnosticInfo.cs Compile Include from Logging and STJ
targets files
- Add diagnostic.GetMessage() to Logger dedup key to avoid collapsing
distinct diagnostics with same Id/location but different messages
- Add pragma suppression test cases: negative (no pragma), partial
(multiple diagnostics, only some suppressed), and scoping (suppress
then restore before diagnostic site)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis

Copy link
Copy Markdown
MemberAuthor

/ba-g stalled browser-wasm tests. Changes are compile-time only.

@eiriktsarpalis
eiriktsarpalis merged commit 2c7ee19 into mainMar 11, 2026
88 of 90 checks passed
@eiriktsarpalis
eiriktsarpalis deleted the fix/sourcegen-suppressions branch March 11, 2026 09:41
CopilotAI pushed a commit that referenced this pull request Mar 13, 2026
…124994)
## Summary
Source generator diagnostics emitted by incremental generators in
dotnet/runtime can now be suppressed using `#pragma warning disable`
inline directives.
Fixes#92509
## Problem
Incremental source generators that store diagnostic locations in
equatable intermediate representations "trim" the `Location` to avoid
holding references to the `Compilation` object. The standard workaround
— `Location.Create(filePath, textSpan, linePositionSpan)` — creates an
`ExternalFileLocation` (`LocationKind.ExternalFile`) which **bypasses
Roslyn's pragma suppression checks**. Only `SourceLocation`
(`LocationKind.SourceFile`) instances, created via
`Location.Create(SyntaxTree, TextSpan)`, are checked against the
`SyntaxTree`'s pragma directive table.
This is the issue described in
[dotnet/roslyn#68291](dotnet/roslyn#68291).
## Solution
Following the technique first applied in
[eiriktsarpalis/PolyType#401](eiriktsarpalis/PolyType#401),
split each affected generator's `RegisterSourceOutput` pipeline into two
separate pipelines:
1. **Source generation pipeline** — Fully incremental. Uses `Select` to
extract just the equatable model (which contains no
`Location`/`SyntaxTree` references), deduplicates by model equality,
only re-fires on structural changes.
2. **Diagnostic pipeline** — Reports raw `Diagnostic` objects that
preserve the original `SourceLocation` (`LocationKind.SourceFile`) from
the syntax tree. This enables Roslyn to check the `SyntaxTree`'s pragma
directive table for `#pragma warning disable` directives.
Parsers now create `Diagnostic.Create(descriptor, location, ...)`
directly instead of wrapping in `DiagnosticInfo.Create(...)` which
trimmed the location. Since `ImmutableArray<Diagnostic>` does not have
value equality, the diagnostic pipeline fires more frequently but the
work is trivially cheap (just iterating and reporting).
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Frulfump

Copy link
Copy Markdown

Can this one be backported to 10.0.1xx and 10.0.3xx? (If that happens I assume it would also automatically be a part of upcoming 10.0.4xx?)

@eiriktsarpalis

Copy link
Copy Markdown
MemberAuthor

This is not something we could backport to .NET 10, unfortunately.

@Frulfump

Copy link
Copy Markdown

Ah ok thanks for responding. Looking forward to .NET 11 then, is it available in preview 3 or will it be preview 4?

@eiriktsarpalis

Copy link
Copy Markdown
MemberAuthor

Preview 4 I think.

@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 16, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Text.Jsonsource-generatorIndicates an issue with a source generator feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Address diagnostic issues in runtime incremental source generators

4 participants

@eiriktsarpalis@Frulfump@ericstj