Skip to content

chore(sonar): fix CA1859 concrete types + CA1826 indexable collections - #388

Merged
thomasluizon merged 1 commit into
mainfrom
chore/api-sonar-ca1859-ca1826
Jul 13, 2026
Merged

chore(sonar): fix CA1859 concrete types + CA1826 indexable collections#388
thomasluizon merged 1 commit into
mainfrom
chore/api-sonar-ca1859-ca1826

Conversation

@thomasluizon

Copy link
Copy Markdown
Owner

What

SonarCloud smell burn-down (thomasluizon_orbit-api), two Roslyn performance rules, all behavior-preserving:

Rule Count Fix
external_roslyn:CA1859 29 Change interface-typed private locals/fields/params/returns to the concrete type Sonar named (List<T>, Dictionary<K,V>, int[], MemoryCache, MemoryDistributedCache, SystemPromptBuilder) where no polymorphism is needed.
external_roslyn:CA1826 11 Replace .FirstOrDefault() on IReadOnlyList<T> repository results with a Count/indexer check (x.Count > 0 ? x[0] : null); the null-conditional Results?.FirstOrDefault() becomes a { Count: > 0 } pattern.

Notes

  • Only Sonar-flagged members were retyped. Where a rule fired on some overloads/methods but not others (e.g. CreateChallengeCommand.VerifyInvitedFriendsAsync, MoveHabitParentCommand.GetSubtreeHeight, ExportUserDataQuery freshStreakValues), the un-flagged declarations were left as-is to keep the diff surgical.
  • Verified every caller already flows a concrete type and that no return-type change introduces a new CA1859 on a caller (all call sites use var or pass the result directly).
  • dotnet build — 0 errors (13 pre-existing warnings, unrelated: NU1608/RS1038/CS9057/CS0618).
  • dotnet test5284 passed, 0 failed, 0 skipped.

Refs thomasluizon/orbit-ui-mobile#243

Burn down two Roslyn performance smells flagged by SonarCloud, all
behavior-preserving:

- CA1859 (29): change interface-typed private locals, fields, parameters,
  and return types to the concrete type (List<T>, Dictionary<K,V>, int[],
  MemoryCache, MemoryDistributedCache, SystemPromptBuilder) where no
  polymorphism is needed. Only Sonar-flagged members changed; callers
  already flow concrete types.
- CA1826 (11): replace .FirstOrDefault() on IReadOnlyList repository
  results with a Count/indexer check (x.Count > 0 ? x[0] : null), and the
  null-conditional Results?.FirstOrDefault() with a { Count: > 0 } pattern.

Refs thomasluizon/orbit-ui-mobile#243

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review: PR #388

Scope: chore(sonar): fix CA1859 concrete types + CA1826 indexable collections
Recommendation: APPROVE

Summary

Mechanical SonarCloud smell burn-down: 29 CA1859 interface→concrete-type retypes and 11 CA1826 FirstOrDefault()→indexer rewrites across 24 Orbit.Application/Orbit.Infrastructure files plus 8 matching test-file updates. No DTOs/Controllers/routes/auth/validation touched. Every retype was traced to its call site; every FirstOrDefault()Count > 0 ? x[0] : null rewrite was verified against the source collection's actual return type (IGenericRepository<T>.FindAsync/FindTrackedIgnoringFiltersAsync return Task<IReadOnlyList<T>>, guaranteeing Count/indexer support) — semantically identical to FirstOrDefault().

A dedicated security review of the three highest-risk files independently confirmed:

  • AgentTargetOwnershipService.AllOwnedAsyncids retyped IReadOnlyCollection<Guid>List<Guid>; the ownedCount == ids.Count / ids.Count == 0 checks are untouched; CollectGuids still returns List<Guid>. No authorization-bypass risk.
  • ContentModerationServicepayload?.Results is { Count: > 0 } results ? results[0] : null is behaviorally identical to the prior FirstOrDefault(); fail-open-only-on-empty-results semantics preserved, no swallowing of a flagged result.
  • GetPublicProfileQuery, GetFriendProfileQuery, FriendGraphService lookup rewrites all filter on unique keys (Id, PublicProfileSlug, normalized Handle, ReferralCode); matches[0] selection is equivalent to the old FirstOrDefault(), no ordering-dependence introduced.

Findings

Critical

None.

High

None.

Medium

None.

Low / Info

  • [Info] src/Orbit.Infrastructure/Services/PushNotificationService.cs:72subs as List<T> ?? subs.ToList()subs is already declared List<Domain.Entities.PushSubscription>, so the cast always succeeds and the ?? subs.ToList() fallback is unreachable. Pre-existing (previously subs as IList<T>), not introduced by this diff. No action needed.

Deferred / N/A dimensions

  • Build/test (dotnet): N/A — CI runs Build/Unit Tests/SonarCloud as separate required checks; PR body states 0 build errors, 5284/5284 tests passed.
  • Contract-aligner / cross-repo parity: N/A — no DTO, Controller route, or packages/shared type touched; sibling orbit-ui-mobile repo not checked out regardless.
  • Backend hard rules (timezone/authz/validation/logging/transactions): gated in, confirmed untouched — diff surface is type retyping only.

What's good

  • Every retype verified compile-safe by checking the caller already produces the narrower concrete type, rather than trusting the "0 build errors" claim blindly.
  • FirstOrDefault()→indexer rewrites backed by the repository interface contract (IReadOnlyList<T>), provably behavior-identical.
  • No narration comments, TODO/FIXME/HACK, or dead code introduced.
  • PR description is precise about scope and states verified build/test results.

Recommendation

Approve as-is. Clean, well-scoped, behavior-preserving Sonar cleanup with no correctness, security, or contract regressions found.

@thomasluizon
thomasluizon merged commit 5a91f73 into main Jul 13, 2026
19 checks passed
@thomasluizon
thomasluizon deleted the chore/api-sonar-ca1859-ca1826 branch July 13, 2026 20:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant