Sonar Bucket C: reliability bugs + dispose-pattern (S1751/S4586/S3881)#189
Conversation
- S4586: SchedulerMessageHandler.HandleAsync returned a null Task on the (currently unreachable) not-handling branch; a null Task would NRE when the consumer awaits it (ProcessMessageAsync), landing the message in error handling instead of a clean rollback. Return a canceled Task, which flows through the existing OperationCanceledException rollback path. Added a regression test asserting the stopping-worker path surfaces OperationCanceledException. - S1751: Memory ReceiveMessage.GetMessage wrapped straight-line logic in a while(true) that always returned on the first iteration — unwrapped (no behavior change). - S1751: Interception.GetInterceptor used a foreach that returned on the first match — replaced with FirstOrDefault ?? GetOrAdd. - Removed 3 dead Guard.NotNull(CancellationToken) calls (Find*Query) — guarding a non-nullable struct against null is a silent no-op. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Seal 25 leaf classes flagged by S3881 so they conform to the dispose pattern (a sealed class needs only a simple Dispose()), and clean up the dispose members exposed by sealing: - de-virtualize Dispose()/Dispose(bool) that became pointless in sealed types - forward 'disposing' in the ConsumerQueue* overrides (was base.Dispose(true)) - demote now-pointless protected ThrowIfDisposed to private Left unsealed (still flagged): WaitForEventOrCancel and WorkerBase are base classes; their S3881 fix is a virtual-Dispose(bool) cascade through the wait/worker lifecycle, out of scope for a mechanical hygiene pass. Behavior-preserving: full solution builds clean (net10+net8), all unit suites green, Memory integration consumer/producer lifecycle green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (29)
💤 Files with no reviewable changes (3)
📝 WalkthroughWalkthroughThis PR marks many public and internal classes across the codebase as ChangesSealing, Disposal, and Cancellation Fixes
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #189 +/- ##
==========================================
+ Coverage 87.53% 87.54% +0.01%
==========================================
Files 1023 1023
Lines 33621 33614 -7
Branches 2803 2803
==========================================
Hits 29429 29429
+ Misses 3329 3324 -5
+ Partials 863 861 -2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|



Fourth SonarCloud sweep (Bucket C) — the correctness/reliability findings, following #184, #186 (Bucket A), #188 (Bucket D).
Correctness bugs (with tests)
SchedulerMessageHandler.HandleAsyncreturned a nullTaskon its not-handling branch. A null Task NREs when the consumer awaits it (ProcessMessageAsync), landing a stopping-time message in error handling instead of a clean rollback. Now returns a canceled Task, which flows through the existingOperationCanceledExceptionrollback path. (The branch is currently unreachable —ShouldHandlethrows or returns true — but the null return was a latent hazard.) Added a regression test asserting a stopping worker surfacesOperationCanceledException.Memory ReceiveMessage.GetMessagewrapped straight-line code in awhile(true)that always returned on iteration 1 → unwrapped (no behavior change).Interception.GetInterceptorused aforeachthat returned on the first match →FirstOrDefault ?? GetOrAdd.Guard.NotNull(CancellationToken)calls (Find*Query); guarding a non-nullable struct against null is a silent no-op (surfaced while evaluating S2955).S2955 itself (
Guard.NotNullvalue == nullon unconstrainedT) is left as-is — the current behavior is correct (no-op for value types), and the compliantwhere T : classfix cascades into ~9 relational retry-decorator files with SimpleInjector open-generic risk not worth a single low-severity finding.Dispose pattern — S3881 (25 of 27)
Sealed 25 leaf
IDisposableclasses so they conform (a sealed class needs only a simpleDispose()), plus the cleanups sealing requires: de-virtualize now-pointlessDispose()/Dispose(bool), forwarddisposingin theConsumerQueue*overrides (wasbase.Dispose(true)), demote pointlessprotected ThrowIfDisposedtoprivate.Left unsealed (2 still flagged):
WaitForEventOrCancelandWorkerBaseare base classes — their S3881 fix is a virtual-Dispose(bool)cascade through the wait/worker lifecycle, disproportionately risky for a hygiene rule.Sealing includes 11 public classes (ConsumerQueue, DataStorage, MetricsNet, DashboardApiClient, LiteDbConnection, …). This is source-breaking for external code that subclasses them (rare — they're used via interfaces). Explicitly chosen. No changelog/version bump (matches the #186/#188 mechanical-cleanup precedent).
Verification
🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Tests