Skip to content

breaking: satisfy the analyzer gate across every target framework - #4432

Merged
glennawatson merged 2 commits into
mainfrom
build/analyzer-gate-all-rules-error
Aug 13, 2026
Merged

breaking: satisfy the analyzer gate across every target framework#4432
glennawatson merged 2 commits into
mainfrom
build/analyzer-gate-all-rules-error

Conversation

@glennawatson

@glennawatson glennawatson commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

BREAKING CHANGE. Public tuple element names are now PascalCase, and two ReactiveTableViewSource overrides now return string?. See What might this PR break? for the migration. This warrants a major version bump.

What kind of change does this PR introduce?

Build, and breaking. Raises the analyzer gate to error across the whole repo, fixes everything it reports, and fixes five real defects found along the way.

What is the new behavior?

Every analyzer rule is enforced as an error on every target framework, and the sources satisfy it.

  • The analyzer suite moves to 3.43.2. The .editorconfig already set 18 rules to error that the pinned version did not ship, so those severity lines were silently inert. 3.43.1 also stopped requiring [DebuggerDisplay] on types with no members, so ~118 content-free [DebuggerDisplay("TypeName")] attributes and 71 now-orphaned using System.Diagnostics; directives are gone; 54 remain where the type genuinely has members.
  • Public tuple element names are PascalCase. An earlier pass renamed several declarations but left their use sites camelCase, so those members never bound. Completing the rename is what unblocked the Apple target frameworks.
  • [MemberNotNull] actually works off .NET Core. The polyfill declared the attribute in the global namespace; the compiler matches it by fully qualified name, so wherever the BCL lacks the type it bound but performed no null-state analysis. It now lives in System.Diagnostics.CodeAnalysis like every other polyfill in that folder.
  • ReactiveUI.Reactive no longer collides with ReactiveUI. Platforms/net/ComponentModelFallbackConverter.cs was missing its #if REACTIVE_SHIM namespace switch, so ReactiveUI.Reactive.dll published ReactiveUI.ComponentModelFallbackConverter -- the exact full name ReactiveUI.dll publishes -- on 11 target frameworks. Referencing both packages gave CS0433.
  • Restoring a navigation stack pushes each entry. RoutedViewHost resolved the current view model on every iteration of the stack loop, so an N-deep stack came back as the same view model repeated N times.
  • The auto data template resolves on WinUI. AutoDataTemplateBindingHook built its template with WPF's clr-namespace: prefix, which WinUI's parser rejects, and hardcoded the namespace so it was also wrong under REACTIVE_SHIM. The hook threw in exactly the case it exists for.
  • Product code carries no warning suppressions. The two that existed were each hiding one of the defects above.
  • WinUI has a test project. ReactiveUI.WinUI.Tests hosts a real WinUI application on a dedicated STA thread and marshals each test onto its dispatcher queue; that surface previously had no tests at all.
  • Duplication is down. The platform view types share their activation plumbing through one helper linked into each leaf, the WhenAnyObservable sinks now delegate to SwitchSignal<T>/MergeSignal<T> from ReactiveUI.Primitives, and several event-subscription sinks, converters and registrations were folded together.

What is the current behavior?

Most of this code was never analysed, because it was never compiled.

The Apple target frameworks failed to build, so every project downstream of them was skipped. Unblocking them took the solution from 82 assembly outputs to 16000, and each newly-reachable project then reported violations for the first time.

What might this PR break?

  • Public tuple element names are now PascalCase. Anything destructuring by name -- x.self, x.section, x.requestCode -- must use the PascalCase form. This is the main reason the PR is marked breaking.
  • ReactiveTableViewSource.TitleForHeader and TitleForFooter now return string?. They always could return null; the signature said otherwise and a suppression hid it.
  • <RootNamespace>.Resource no longer exists in the Android assemblies. It was generated automatically, was empty, and was never declared public API. A project that needs it can set AndroidGenerateResourceDesigner=true.
  • ReactiveUI.Reactive now publishes ReactiveUI.Reactive.ComponentModelFallbackConverter on the 11 target frameworks where it previously published the colliding ReactiveUI. name. Code referencing the old full name must update; code referencing both packages compiles for the first time.
  • Null-state analysis is now live on the .NET Framework targets for [MemberNotNull]-annotated members. Only RoutingState uses the attribute, and it is clean.
  • UpdateType members now carry explicit values, preserving the existing numbering.

Checklist

  • I have read the Contribute guide
  • Tests have been added or updated (for bug fixes / features)
  • Docs have been added or updated (for bug fixes / features)
  • Changes target the main branch
  • PR title follows Conventional Commits

Additional information

Most of the diff is mechanical attribute and doc-comment churn. The parts worth reviewing are:

  • src/Polyfills/MemberNotNullAttribute.cs -- the namespace correction that makes the attribute functional.
  • src/ReactiveUI/Platforms/net/ComponentModelFallbackConverter.cs and the 11 corrected ReactiveUI.Reactive baselines -- the type-collision fix.
  • src/ReactiveUI/Platforms/uikit-common/RoutedViewHost.cs -- the navigation stack restore.
  • src/ReactiveUI.Maui/Common/AutoDataTemplateBindingHook.cs -- the WinUI namespace fix, with a regression guard that materialises the template.
  • src/ReactiveUI.Shared/Internal/WhenAny/ -- the sinks now delegate to ReactiveUI.Primitives. This depends on the generation-safety fixes in ReactiveUI.Primitives 7.1.1; against 7.1.0 three concurrency tests fail.
  • src/Shared/ActivationSignals.cs and the other new src/Shared/ helpers -- linked into each leaf rather than shared through a project reference, so every assembly compiles its own internal copy.
  • src/tests/ReactiveUI.WinUI.Tests/ -- the new test project and its WinUI host.

Verification: a clean rebuild of reactiveui.slnx on a real Windows host produces 16002 assembly outputs with 0 errors across every target framework, including Apple, Android, Windows desktop, .NET Framework and WinUI.

- Every dotnet_diagnostic severity is now error.
- Adds the newest analyzer rules: SST1535, SST1536, SST1537, SST2019,
  SST2497, SST2498 and PSH1023.
- The file is now identical across refit, reactiveui, Primitives, splat and
  Akavache apart from file_header_template.
@glennawatson
glennawatson force-pushed the build/analyzer-gate-all-rules-error branch from 8d9ed71 to 3bcedde Compare August 11, 2026 12:32
@codecov

codecov Bot commented Aug 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.89%. Comparing base (27b0e3c) to head (86e6dd6).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4432      +/-   ##
==========================================
+ Coverage   93.35%   93.89%   +0.54%     
==========================================
  Files         347      359      +12     
  Lines       14921    15104     +183     
  Branches     1553     1568      +15     
==========================================
+ Hits        13929    14182     +253     
+ Misses        731      679      -52     
+ Partials      261      243      -18     

☔ 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.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@glennawatson
glennawatson force-pushed the build/analyzer-gate-all-rules-error branch 11 times, most recently from 5fa7280 to b0e6499 Compare August 12, 2026 22:21
@glennawatson
glennawatson force-pushed the build/analyzer-gate-all-rules-error branch from b0e6499 to 1be8ded Compare August 13, 2026 00:06
- Bump the analyzer suite to 3.43.2, activating 18 rules the .editorconfig
  already set to error but the pinned version did not ship.
- Fix the resulting violations across the Apple, Android, MAUI, WinUI and
  shared sources: missing [DebuggerDisplay] and AggressiveInlining, XML doc
  element ordering, undocumented <exception> elements, single-use locals,
  trailing guard inversion and explicit enum values.
- Complete the PascalCase tuple element rename. Several declarations were
  renamed earlier while their use sites were left camelCase, which broke
  binding and in turn made dead-code and discard rules fire spuriously.
- Replace check-then-set dispose flags with an Interlocked int latch, reading
  it through Volatile.Read where the type also swaps it.
- Declare the MemberNotNull polyfill in System.Diagnostics.CodeAnalysis. The
  compiler matches that attribute by fully qualified name, so in the global
  namespace it bound but performed no null-state analysis wherever the BCL
  lacks the type, which is why RoutingState needed a CS8618 pragma.
- Push each navigation stack entry when restoring the initial stack. The loop
  resolved the current view model every iteration, so an N-deep stack came
  back as the same view model N times.
- Build the auto data template against a namespace WinUI can resolve. It used
  WPF's clr-namespace prefix, which WinUI's parser rejects, and hardcoded the
  namespace so it was also wrong under REACTIVE_SHIM - so the hook threw in
  exactly the case it exists for, an ItemsSource binding with no ItemTemplate.
- Return null from the table view source title overloads by declaring them
  nullable, and assert it where UIKit declares the view overloads non-null.
  Product code now carries no warning suppressions.
- Share the activation-signal plumbing the platform view types each repeated.
  They extend unrelated platform base classes, so the teardown, the manual
  activation scheduling and the appear/disappear handling move to one helper
  under src/Shared, linked into each leaf so every assembly compiles its own
  internal copy.
- Turn off the Android resource designer. None of these libraries ship Android
  resources, so it only added an empty public Resource type to ten assemblies;
  control wiring resolves identifiers by reflection against the consuming app.
- Add ReactiveUI.WinUI.Tests, which hosts a real WinUI application on a
  dedicated STA thread and marshals each test onto its dispatcher queue. The
  WinUI control surface previously had no test project at all.
- Cover the activation helper, the Blazor component state and reactivity sink,
  and the MAUI suspend helper to 100% line and branch. The Blazor types are
  only reachable from their own assembly, so ReactiveUI.Blazor now grants
  InternalsVisibleTo to its test project.
- Exclude ReactiveUI.AndroidX from coverage measurement. Its activities and
  fragments only construct on a device, so it has no test project and always
  reported 0%. ReactiveUI.Maui stays measured - its tests run on the CI leg.
- Relax SST2334 and PSH1410 for tests only, and silence two rules whose intent
  is already enforced by equivalents elsewhere in the configured set.

Most of this code had never been analysed: the Apple target frameworks failed
to compile, so every project downstream of them was skipped. Unblocking those
took the solution from 82 assembly outputs to 16000.

BREAKING CHANGE: tuple element names on public APIs are now PascalCase, and
ReactiveTableViewSource.TitleForHeader/TitleForFooter now return string?.
@glennawatson
glennawatson force-pushed the build/analyzer-gate-all-rules-error branch from 1be8ded to 86e6dd6 Compare August 13, 2026 00:36
@glennawatson glennawatson changed the title build: satisfy the analyzer gate across every target framework build!: satisfy the analyzer gate across every target framework Aug 13, 2026
@glennawatson glennawatson changed the title build!: satisfy the analyzer gate across every target framework breaking: satisfy the analyzer gate across every target framework Aug 13, 2026
@glennawatson
glennawatson enabled auto-merge (squash) August 13, 2026 00:43
@reactiveui reactiveui deleted a comment from sonarqubecloud Bot Aug 13, 2026
@sonarqubecloud

Copy link
Copy Markdown

@glennawatson
glennawatson merged commit e740afe into main Aug 13, 2026
11 checks passed
@glennawatson
glennawatson deleted the build/analyzer-gate-all-rules-error branch August 13, 2026 01:38
@github-actions

Copy link
Copy Markdown

This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Aug 27, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants