Skip to content

chore: Checker Framework adoption - part 1#1981

Merged
sergiyvamz merged 3 commits into
mainfrom
checker-framework-pilot
Jun 19, 2026
Merged

chore: Checker Framework adoption - part 1#1981
sergiyvamz merged 3 commits into
mainfrom
checker-framework-pilot

Conversation

@sergiyvamz

@sergiyvamz sergiyvamz commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Checker Framework adoption — Part 1 (core connection/plugin path)

This is part 1 of a planned series to incrementally adopt static nullness checking
in the wrapper, inspired by #373 ("Use checker framework to check for nulls"). That PR
demonstrated the value of the idea but tried to flip on three checkers across 20 files at
once and became an unmergeable WIP. This series takes the opposite approach: enable one
checker, in warning mode, scoped to one cohesive area at a time, and actually fix the
findings
before widening scope.

Context

The codebase already uses @Nullable/@NonNull annotations heavily (~420 @Nullable,
~670 @NonNull) and depends on checker-qual, but nothing ever verified them — the
org.checkerframework Gradle plugin was declared in settings.gradle.kts but never
applied. So the annotations were documentation, not guarantees.

What this PR does

  1. Wires up the NullnessChecker behind a flag. A guarded block in
    wrapper/build.gradle.kts (active only with -PenableCheckerFramework) applies the
    plugin with:

    • the NullnessChecker only (Optional/Regex deferred — noise for little gain here),
    • warning mode (-Awarns, build does not fail), and
    • scope limited via -AonlyDefs to the core path: ConnectionPluginManager,
      PluginServiceImpl, wrapper.ConnectionWrapper.

    Normal Java 8 builds are completely unaffected. Because the checker needs JDK 11+ to
    run (the main source targets Java 8), it is intended to run on a JDK 17 lane; the build
    block handles the JDK-17 plumbing (--add-exports/--add-opens for jdk.compiler,
    and stripping the unsupported -Xbootclasspath/p arg the plugin injects).

  2. Adds an on-demand GitHub Action (.github/workflows/run-checker-framework.yml,
    workflow_dispatch only). On GitHub's runners JDK 17 is available natively, so the
    workflow is simple: check out, set up JDK 8 + 17, run
    :aws-advanced-jdbc-wrapper:compileJava -PenableCheckerFramework. It posts a findings
    summary (total / style-only / real / crashes, with a by-category breakdown) to the job
    summary and uploads the full report as an artifact. It is not wired into CI gating —
    it is triggered manually to measure nullness findings.

  3. Fixes the findings in the scoped files. Running the checker surfaced 25 real
    (non-style) findings
    ; all are resolved, taking the scoped files to 0 real findings,
    0 checker crashes
    . Highlights:

    • 1 latent NPE fixed in PluginServiceImpl.getCurrentHostSpec() — it dereferenced
      the result of Utils.getWriter(...) (which is @Nullable) before null-checking it,
      so a topology with no writer would NPE. Restructured to resolve into a local,
      null-check, then use; dead fallback code removed.
    • A structural refactor of the plugin pipeline: PluginPipeline was overloaded for
      both the execute path (always has a continuation func) and the notify path (ignores
      it), forcing func to be @Nullable and making the execute path look unsafe. Split
      out a dedicated NotifyPluginPipeline so each contract is honest.
    • Genuine contract corrections: @Nullable applied to fields, parameters, and
      returns that were already null in practice (effectiveConnProvider, hosts,
      currentHostSpec, routedHostSpec, pooledConnection, getPlugin, ExceptionManager
      dialect params, Utils.isNullOrEmpty, Pair type bounds, etc.).
    • A small number of justified, documented @SuppressWarnings where the honest fix
      is unavailable (overriding the non-null JDK Wrapper.unwrap), an upstream tool bug
      (generic varargs inference crash), or out of scope (widening StateSnapshotProvider
      across ~25 implementers).

    Utils.isNullOrEmpty also gained @EnsuresNonNullIf so its guard semantics are
    understood at every call site.

Why warning mode / why scoped

This is intentionally non-blocking. The goal of part 1 is to prove the workflow,
clean up the most safety-critical path, and gather real data — not to gate CI yet. Once a
package is clean, a later part can flip it from -Awarns to build-failing.

Verification

  • Normal Java 8 build compiles; test sources compile.
  • PluginServiceImplTests + ConnectionPluginManagerTests (44 tests) pass —
    confirming the pipeline split and getCurrentHostSpec refactor preserve behavior.
  • Checker (NullnessChecker, scoped): 0 real findings, 0 crashes; the only remaining
    output is 80 type.anno.before.modifier style warnings (annotation placement — not
    bugs, mechanically auto-fixable, deferred).

A methodology + findings writeup is included under docs/checker-framework-pilot/.

Planned next parts

  • Auto-fix the type.anno.before.modifier placement warnings.
  • Expand -AonlyDefs package-by-package; flip cleaned packages to build-failing.
  • Decide policy for initialization findings and the StateSnapshotProvider interface
    widening.

How to run the checker

On GitHub: Actions → Run Checker Framework (Nullness) → Run workflow (manual dispatch).


By submitting this pull request, I confirm that my contribution is made under the terms
of the Apache 2.0 license.

@sergiyvamz sergiyvamz changed the title Checker Framework adoption part 1: enable NullnessChecker (scoped, warn-only) and fix core path chore: Checker Framework adoption part 1: enable NullnessChecker (scoped, warn-only) and fix core path Jun 18, 2026
@sergiyvamz sergiyvamz changed the title chore: Checker Framework adoption part 1: enable NullnessChecker (scoped, warn-only) and fix core path chore: Checker Framework adoption - part 1 Jun 18, 2026
@sergiyvamz sergiyvamz force-pushed the checker-framework-pilot branch from 1a9dab7 to 2d63054 Compare June 19, 2026 18:33
@sergiyvamz sergiyvamz enabled auto-merge (squash) June 19, 2026 18:34
@sergiyvamz sergiyvamz merged commit f8a20fa into main Jun 19, 2026
22 checks passed
@sergiyvamz sergiyvamz deleted the checker-framework-pilot branch June 19, 2026 18:39
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.

2 participants