Skip to content

Add Nullable.Extended.Analyzer to the StandardAnalyzers module - #412

Merged
rdeago merged 8 commits into
Tenacom:mainfrom
rdeago:408-nullable-extended-analyzer
Oct 26, 2026
Merged

rdeago merged 8 commits into
Tenacom:mainfrom
rdeago:408-nullable-extended-analyzer

Conversation

@rdeago

@rdeago rdeago commented Oct 26, 2026 •

Copy link
Copy Markdown
Member

Checklist of related issues / discussions

Proposed changes

#408 asks whether an analyzer can report the null-forgiving operator (!), so that the rule against it needs no review to enforce it. Nullable.Extended.Analyzer reports every null-forgiving operator as a warning. A comment that starts with // !, on the line above the statement, clears the warning and states the invariant.

This PR adds the analyzer to Buildvana SDK, and prepares this repository for the removal of its operators. The removal comes in a second PR. Buildvana builds itself with the last published release of its own packages, so the analyzer reaches this repository only after a release.

Buildvana SDK

The StandardAnalyzers module has a fourth switch, UseNullableExtendedAnalyzer. It follows UseStyleCopAnalyzers:

  • The default is true, except for a project that uses the Microsoft.Build.NoTargets SDK.
  • A value MSBuild reads as true, such as yes, counts as true, and any other value counts as false.
  • When the switch is true, Module.UseNullableExtendedAnalyzer.targets adds a PackageReference to Nullable.Extended.Analyzer with PrivateAssets="All".

PackageVersions.props pins the package at 1.18.2. docs/sdk-modules/standard-analyzers.md documents the switch, the four rules NX0001 to NX0004, and the // ! comment.

The change is breaking. A project with a null-forgiving operator and no // ! comment gets a warning it did not get before, and an error when it treats warnings as errors.

This repository

  • .globalconfig has a section for Nullable.Extended.Analyzer. It lists NX0001 to NX0004 at warning, the default severity.
  • Common.props sets UseNullableExtendedAnalyzer to false. The repository holds about 50 operators under src/ and about 470 lines with one under tests/, none with a // ! comment. Without the setting, the first build with the next release of Buildvana SDK would fail. The second PR removes the setting.
  • .claude/scratchpad/Common.props sets UseNullableExtendedAnalyzer to false for good. Scratch code is not held to the rule.

MCP server

dotnet-tools.json adds Nullable.Extended.Mcp 1.18.2 as a local tool. .mcp.json, at the root of the repository, runs it as an MCP server, with --root . and --solution Buildvana.slnx. The server finds the null-forgiving operators of the solution from the syntax, without a build. The second PR uses it to survey the operators and plan their removal.

Additional changes

  • Tests of the StandardAnalyzers module. StandardAnalyzersModuleTargetsTests covers every switch and default of Module.targets, not only UseNullableExtendedAnalyzer. A regression in EnableNETAnalyzers, AnalysisLevel, AnalysisMode, EnforceCodeStyleInBuild, UseStyleCopAnalyzers, UsePublicApiAnalyzers, or the search for stylecop.json used to pass the build.
  • .globalconfig section headers. The StyleCop and public API sections warned against setting severities in .DotSettings files. Only ReSharper reads those files, so the warning belongs in the ReSharper section of .editorconfig alone.

Types of changes

This pull request introduces the following types of changes:

  • Bug fix
  • New feature
  • Test addition / update (no changes to non-test code)
  • Refactor (no changes in public API syntax or semantics)
  • Performance improvement (no changes in public API syntax or semantics)
  • Documentation update (not including changelog)
  • Dependency addition / update
  • Changes to toolchain configuration
  • Changes to CI (workflows, bot / app configurations)
  • Changes to repository files (.gitattributes, .gitignore)
  • Other

Breaking changes

This pull request introduces breaking changes:

  • Yes
  • No

Checklist

  • For all types of changes:
  • For code changes only:
    • The project builds on my machine, via the provided build script, with zero warnings
    • I have added tests that prove my feature works / my fix is effective
    • I have added / modified XML documentation according to changes in code
    • I have checked that all the links I added or modified in XML documentation point to their intended destination
  • For documentation changes only:
    • I have checked and proofread documentation locally
    • I have checked that all the links I added or modified point to their intended destination

🤖 Generated with Claude Code

rdeago and others added 6 commits October 26, 2026 10:01
A null-forgiving operator silences a nullable warning on an invariant that
no type states, and Buildvana SDK reported none of them. Unless a `// !`
comment above an operator states the invariant, the analyzer reports it.
`UseNullableExtendedAnalyzer` follows `UseStyleCopAnalyzers`: on by
default, and off for a `Microsoft.Build.NoTargets` project.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only `UseNullableExtendedAnalyzer` had tests in
`StandardAnalyzersModuleTargetsTests`. A regression in `EnableNETAnalyzers`,
`AnalysisLevel`, `AnalysisMode`, `EnforceCodeStyleInBuild`,
`UseStyleCopAnalyzers`, `UsePublicApiAnalyzers`, or the search for
`stylecop.json` in `Module.UseStyleCopAnalyzers.targets` would pass the
build. `EnableNETAnalyzers` and the three `Use*` properties share one
pattern of defaults and stored values, so one matrix of tests covers all
four.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The StyleCop and Public API sections of `.globalconfig` warned against
setting severities in `.DotSettings` files. Only ReSharper reads those
files, so the warning belongs in the ReSharper section of `.editorconfig`
alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Also temporarily disable the analyzer in `Common.props`, lest the solution fail to build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dealing with null-forgiving operators is easier with a specialized MCP server.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It was still at 1.18.0, while `Nullable.Extended.Mcp` was at 1.18.2.
They are versioned together, so they're better kept at the same version.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rdeago rdeago added enhancement [issue/PR] requests / implements new or improved functionality. breaking [issue/PR] requires / contains breaking changes. labels Oct 26, 2026
@github-actions github-actions Bot added area:build [issue/PR] affects project files and/or build settings. area:docs [issue/PR] affects documentation (excluding XML documentation that is part of source code). area:code [issue/PR] affects project code (excluding tests). area:deps [issue/PR] adds, updates, or removes dependencies. area:style [issue/PR] affects editor / code analysis settings. labels Oct 26, 2026
@codecov

codecov Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.28%. Comparing base (3191443) to head (2a0ab46).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #412   +/-   ##
=======================================
  Coverage   87.28%   87.28%           
=======================================
  Files         321      321           
  Lines        9514     9514           
  Branches     1793     1793           
=======================================
  Hits         8304     8304           
  Misses        911      911           
  Partials      299      299           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@rdeago rdeago left a comment

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.

Review by Claude. Self-review.

⚠️ Changes requested.


No blocking findings.

Non-blocking

  • PR text, "This repository" section. The first bullet says that the new .globalconfig section lists NX0001 to NX0004 at the default severity, "as the sections of the other analyzers do". The public API analyzers section does the opposite: its header says "We only list non-default severities here." Drop the comparison, or name only the sections that list every rule.

rdeago and others added 2 commits October 26, 2026 22:29
The rules table in docs/sdk-modules/standard-analyzers.md said that NX0002 covers a `null` or `default` literal, and NX0004 an init-only property assignment. In Nullable.Extended.Analyzer, `IsNullOrDefaultExpression` also matches `default(T)`, and `IsInitOnlyPropertyAssignment` matches only the initializer of a property with an `init` accessor. The analyzer checks both rules before NX0003, so `null!` inside a lambda expression gets NX0002.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The comments in .globalconfig held the titles of NX0001 to NX0004, which no build output shows. A search for the text of a warning found no line there. The four rules share one message.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rdeago

rdeago commented Oct 26, 2026

Copy link
Copy Markdown
Member Author

It lists NX0001 to NX0004 at warning, the default severity, as the sections of the other analyzers do.

Fixed in the PR text. The bullet drops the comparison. No section of .globalconfig follows a policy of listing default severities, or of omitting them. The Nullable.Extended.Analyzer section lists warning because no rule needs another severity.

Two other changes:

  • 28a3a90 fixes the rules table in docs/sdk-modules/standard-analyzers.md. NX0002 also covers default(T)!. NX0004 covers only the initializer of a property with an init accessor. null! inside a lambda expression gets NX0002, not NX0003.
  • 2a0ab46 replaces the rule titles in the .globalconfig comments with the rule message, so a search for the text of a warning finds the line. The four rules share one message.

@rdeago rdeago left a comment

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.

Review by Claude. Self-review.

👍 Approved.


No blocking findings. The PR text no longer compares the Nullable.Extended.Analyzer section of .globalconfig with the other sections.

@rdeago
rdeago merged commit 83627c0 into Tenacom:main Oct 26, 2026
7 checks passed
@rdeago
rdeago deleted the 408-nullable-extended-analyzer branch October 26, 2026 22:03
@rdeago rdeago mentioned this pull request Oct 28, 2026
15 of 24 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:build [issue/PR] affects project files and/or build settings. area:code [issue/PR] affects project code (excluding tests). area:deps [issue/PR] adds, updates, or removes dependencies. area:docs [issue/PR] affects documentation (excluding XML documentation that is part of source code). area:style [issue/PR] affects editor / code analysis settings. breaking [issue/PR] requires / contains breaking changes. enhancement [issue/PR] requests / implements new or improved functionality.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant