Skip to content

Add warning waves infrastructure to Razor compiler#13016

Merged
chsienki merged 2 commits intodotnet:mainfrom
chsienki:warning_waves
Apr 10, 2026
Merged

Add warning waves infrastructure to Razor compiler#13016
chsienki merged 2 commits intodotnet:mainfrom
chsienki:warning_waves

Conversation

@chsienki
Copy link
Copy Markdown
Member

@chsienki chsienki commented Apr 7, 2026

Summary

Implement the plumbing for Razor warning waves, modeled after C#'s warning waves feature. This PR adds the infrastructure only - no new warnings are introduced yet.

Design: #12713

Changes

Core Infrastructure

  • RazorDiagnosticDescriptor - Added uint WarningLevel property (0 = always reported)
  • RazorDiagnostic - Exposed WarningLevel from descriptor
  • RazorConfiguration - Added uint RazorWarningLevel parameter (default 0)
  • RazorCodeGenerationOptions - Threaded RazorWarningLevel through options, builder, and all With* methods
  • RazorLanguageVersion - Added GetDefaultWarningLevel() (returns major version as uint)
  • RazorProjectEngine - Wires config warning level into code gen options

Diagnostic Filtering

  • CodeRenderingContext.GetDiagnostics() - Central filtering: suppresses diagnostics where WarningLevel > configured level. Level 0 diagnostics are always reported.

Source Generator

  • Parses build_property.RazorWarningLevel from MSBuild
  • Reports RZ3601 for invalid values
  • Defaults to language version's major number when not set

Tests

  • CodeRenderingContextTest - 9 tests for warning level filtering (levels 0-13, multi-level, always-on)
  • Source generator tests - Validation of RazorWarningLevel parsing (invalid, valid, empty)

Implement the plumbing for Razor warning waves, modeled after C#'s warning waves feature.
This PR adds the infrastructure only — no new warnings are introduced yet.

Changes:
- Add WarningLevel (uint) to RazorDiagnosticDescriptor and RazorDiagnostic
  - 0 = always reported, >0 = suppressed when exceeding configured level
- Add RazorWarningLevel (uint) to RazorConfiguration and RazorCodeGenerationOptions
  - Default derived from RazorLanguageVersion major version
- Add central diagnostic filtering in CodeRenderingContext.GetDiagnostics()
- Add GetDefaultWarningLevel() to RazorLanguageVersion
- Parse/validate build_property.RazorWarningLevel in source generator (RZ3601 for invalid values)
- Add ReportDiagnostics overload for ImmutableArray<Diagnostic>

Tests:
- CodeRenderingContextTest: 9 tests for warning level filtering
- Source generator tests: validation of RazorWarningLevel parsing

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

// Filter out diagnostics whose warning level exceeds the configured level.
// Diagnostics with level 0 are always reported regardless of the configured level.
using var filtered = new PooledArrayBuilder<RazorDiagnostic>(capacity: _diagnostics.Count);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

SelectAndOrderByAsArray?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Oh, was SelectAndOrderByAsArray not a thing?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Never mind the old question, that was dumb. But, the new question is:

Is OrderByAsArray stable in netstandard2.0? Does it matter in this context?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Looks like the stability is the same as the original, so never mind

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

BTW, it feels like this method is pretty high traffic. In which case, maybe your original change is better.

Error "{Id}": "{MessageFormat}"
""";
private string DebuggerToString()
=> $"""Error "{Id}" (level {WarningLevel}): "{MessageFormat}" """;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Is the extra space at the end intentional?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This wasn't the change that I was expecting.

Copy link
Copy Markdown
Member

@davidwengier davidwengier left a comment

Choose a reason for hiding this comment

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

Presumably you'll follow up with an SDK change after this is merged to pass the property through?

razorLanguageVersion = RazorLanguageVersion.Latest;
}

uint razorWarningLevel = razorLanguageVersion.GetDefaultWarningLevel();
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.

Should the default here be 10, so if the SDK doesn't pass the RazorWarningLevel property through to the compiler they essentially get the same behaviour as now?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I went back and forth on this. I ended up here because I think we should always aim to be the language version. In older SDKs we'll be using older language versions, which didn't have WarningWaves, and in newer ones we'll have the property.

There is a question of do we actually set it in the SDK at all, or just leave it empty by default and use language version, and then if the user wants to set it we'll pick up the value.

It comes down to a bit philosophical of where we store the default value: in the compiler or in the targets. I prefer this way, but open to changing it.

Copy link
Copy Markdown
Member

@davidwengier davidwengier Apr 8, 2026

Choose a reason for hiding this comment

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

To be clear, because I wasn't in my first comment, I wasn't thinking about the value of RazorWarningLevel provided by the SDK (and I don't think we need to go and necessarily set a value for it in the SDK), I was talking about for when the SDK doesn't set <CompilerVisibleMetadata Include="RazorWarningLevel">, which is what this default would be for. In other words, what this is now is "RazorWarningLevel defaults to RazorLangVersion", what I was wondering about is "RazorWarningLevel defaults to RazorLangVersion, unless you're using an SDK that doesn't know about RazorWarningLevel at all, in which case it defaults to 10 to baseline to current behaviour". Or something.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Oh yeah, that's a really good catch on the distinction, let me update it.

@chsienki chsienki force-pushed the warning_waves branch 5 times, most recently from b276092 to b78889f Compare April 10, 2026 00:35
- Use Where().OrderByAsArray() for non-destructive filtering in GetDiagnostics
- Revert uint back to int for consistency with Roslyn
- Fix copypasta bug in WithSuppressUniqueIds (RootNamespace -> SuppressUniqueIds)
- Remove trailing space in debugger display string
- Extract CreateDescriptor helper and refactor all descriptors in RazorDiagnostics.cs
- Make lambdas static in IncrementalValueProviderExtensions
- Use PooledArrayBuilder(capacity: 2) in source generator RazorProviders
- Extract ParseRazorLanguageVersion and ParseRazorWarningLevel helper methods
- Distinguish absent vs empty RazorWarningLevel: absent (old SDK) defaults to 0,
  empty (new SDK) defaults to language version
- Fix encoding issues in comments (em dash -> dash)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@chsienki chsienki marked this pull request as ready for review April 10, 2026 00:42
@chsienki chsienki requested a review from a team as a code owner April 10, 2026 00:42
@chsienki chsienki added the feature request Large improvement request label Apr 10, 2026
@chsienki
Copy link
Copy Markdown
Member Author

@ToddGrun Addressed your feedback, let me know if you have anything else.

Copy link
Copy Markdown

@ToddGrun ToddGrun left a comment

Choose a reason for hiding this comment

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

:shipit:

@chsienki chsienki enabled auto-merge (squash) April 10, 2026 01:02
@chsienki chsienki merged commit a027f8a into dotnet:main Apr 10, 2026
10 checks passed
@dotnet-policy-service dotnet-policy-service Bot added this to the Next milestone Apr 10, 2026
chsienki added a commit to chsienki/razor-tooling that referenced this pull request Apr 13, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@JoeRobich JoeRobich modified the milestones: Next, 18.7 Apr 28, 2026
chsienki added a commit to chsienki/razor-tooling that referenced this pull request Apr 28, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature request Large improvement request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants