Skip to content

Fix all analyzer warnings and enable TreatWarningsAsErrors - #223

Merged
einsteinx2 merged 11 commits into
mainfrom
fix-analyzer-warnings
Jul 27, 2026
Merged

Fix all analyzer warnings and enable TreatWarningsAsErrors#223
einsteinx2 merged 11 commits into
mainfrom
fix-analyzer-warnings

Conversation

@einsteinx2

Copy link
Copy Markdown
Owner

Summary

Eliminates all ~183 unique analyzer warnings (a full --no-incremental rebuild reported more than the original ~143 estimate — incremental builds hide warnings from up-to-date projects) and turns on TreatWarningsAsErrors in Directory.Build.props so new warnings fail the build. Fixes are grouped into 11 commits by warning category, ordered bug-risk first, intended for rebase-and-merge.

Real bugs fixed along the way

  • ArtApiHandler: a failed Int32.TryParse of the size parameter overwrote the Int32.MaxValue sentinel with 0, so an invalid size attempted a 0-pixel resize instead of skipping (CA1806)
  • FavoriteRepository.FavoritesForAlbumArtistId: threw ArgumentNullException naming "artistId" — copy-paste from the artist overload (CA2208)
  • SQLiteNet.DoSavePointExecute: ArgumentException message and paramName were swapped (CA2208)
  • TranscodeApiHandler: dead stream/length locals left over from the TranscodeStreamer refactor (CS0219)
  • 14 sites passed nullable value types to ReferenceEquals, which only worked because a null nullable boxes to a null reference (CA2013)
  • Last.fm auth requests ported from obsolete WebRequest to a shared HttpClient (SYSLIB0014)

Mechanical groups

  • CA1854: ContainsKey + indexer → TryGetValue (47 sites, API handlers + repository caches)
  • CA1859/CA1822: concrete types for locals/fields/private helpers; members that touch no instance state made static, call sites updated
  • CA1861/CA2263/CA1846/CA1866/CA1834/CA1840: constant arrays hoisted or params overloads, generic Enum.GetNames, span/char overloads
  • CA1510/CA1507: repository ctor guards → ArgumentNullException.ThrowIfNull (explicit throws kept for int? params to avoid CA1871 boxing)
  • Vendored SQLiteNet.cs: P/Invoke externs made internal (no external callers), BestFitMapping = false on string-marshaling imports (deliberately no ThrowOnUnmappableChar — would change behavior), generic GetMapping, discarded Bind* results, IDisposable declared on PreparedSqlLiteInsertCommand
  • Tests: GC.SuppressFinalize in Dispose() (CA1816), TestContext.Current.CancellationToken on E2E HTTP calls (xUnit1051), Assert.Single (xUnit2013)

Test plan

  • dotnet build WaveBox.slnx --no-incremental — 0 warnings, 0 errors (with warnings-as-errors on)
  • dotnet test — 463 passed, 0 failed (Core 193, Server 251, E2E 19), verified after each commit

🤖 Generated with Claude Code

https://claude.ai/code/session_019u4sFGNHRSAKp1Hjdr1oRF

einsteinx2 and others added 11 commits July 26, 2026 20:02
ReferenceEquals on a long?/DateTime? boxes the value, so the null checks
only worked by the accident that a null nullable boxes to a null
reference. Use plain null comparisons instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019u4sFGNHRSAKp1Hjdr1oRF
Check TryParse success explicitly at each call site instead of relying
on the out value being zeroed on failure. This also fixes a real bug in
ArtApiHandler: a failed parse of the size parameter overwrote the
Int32.MaxValue sentinel with 0, so an invalid size attempted a 0-pixel
resize instead of skipping the resize.

The stream/length locals in TranscodeApiHandler were leftovers from
before the TranscodeStreamer refactor and are now deleted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019u4sFGNHRSAKp1Hjdr1oRF
WebRequest.Create is obsolete (SYSLIB0014); the two last.fm token/session
GETs now go through a shared static HttpClient. Error semantics are
unchanged: both APIs throw on a failed request.

ServerInfo.TempFolder (never reassigned) becomes a get-only property and
UserPurge.Queue becomes a property, resolving CA2211.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019u4sFGNHRSAKp1Hjdr1oRF
Convert dictionary reads guarded by ContainsKey to single TryGetValue
lookups across the API handlers and the session/user repository caches.
No behavior change; short-circuit ordering of compound conditions is
preserved at every site.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019u4sFGNHRSAKp1Hjdr1oRF
Declare locals, fields, and private helpers with their concrete types
(List, Dictionary, HashSet, FileStream) instead of interfaces, and mark
members that touch no instance state as static, updating call sites.
The 3-arg IApiHandler.Process implementations stay instance methods;
only the non-interface overloads and helpers changed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019u4sFGNHRSAKp1Hjdr1oRF
A full rebuild surfaced analyzer warnings that incremental builds had
hidden. The behavior-relevant ones:

- CA2013: more nullable value types passed to ReferenceEquals in
  Playlist, User, and UserRepository; use plain null comparisons.
- CA2208: FavoritesForAlbumArtistId threw ArgumentNullException with
  the wrong parameter name ("artistId", a copy-paste from the artist
  overload), and InsertTypeExtensions.QueryText passed the type name
  instead of the parameter name; both now use nameof.
- CA1873: Log.Write now checks ILogger.IsEnabled before logging so
  disabled levels skip message formatting.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019u4sFGNHRSAKp1Hjdr1oRF
CA1861 constant array arguments become params Split/Trim calls or
static readonly fields; CA2263 Enum.GetNames uses the generic overload;
CA1846/CA1866/CA1834 switch to span, char, and Append(char) overloads;
CA1840 uses Environment.CurrentManagedThreadId.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019u4sFGNHRSAKp1Hjdr1oRF
Constructor guards use ArgumentNullException.ThrowIfNull; the nullable
int? parameter guards in FavoriteRepository keep explicit throws (to
avoid CA1871 boxing) but use nameof for the parameter names.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019u4sFGNHRSAKp1Hjdr1oRF
P/Invoke externs become internal (no callers outside the assembly) with
BestFitMapping disabled on the string-marshaling imports; GetMapping
calls with compile-time types use the generic overload; Bind* results
are explicitly discarded; PreparedSqlLiteInsertCommand now declares
IDisposable so its existing dispose pattern is recognized; plus small
span/char-overload and Length-over-LINQ cleanups. The ArgumentException
in DoSavePointExecute had its message passed as paramName; the
arguments are now in the right order.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019u4sFGNHRSAKp1Hjdr1oRF
Test-class Dispose() methods call GC.SuppressFinalize (CA1816), E2E
HTTP calls pass TestContext.Current.CancellationToken so test
cancellation stays responsive (xUnit1051), and the starred-songs count
assertion uses Assert.Single (xUnit2013).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019u4sFGNHRSAKp1Hjdr1oRF
@einsteinx2
einsteinx2 merged commit ca04d81 into main Jul 27, 2026
9 checks passed
@einsteinx2
einsteinx2 deleted the fix-analyzer-warnings branch July 27, 2026 02:40
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