Skip to content

Add Error Prone and NullAway static analysis to build#75

Open
bernardladenthin wants to merge 18 commits into
mainfrom
claude/gracious-carson-gRcSp
Open

Add Error Prone and NullAway static analysis to build#75
bernardladenthin wants to merge 18 commits into
mainfrom
claude/gracious-carson-gRcSp

Conversation

@bernardladenthin
Copy link
Copy Markdown
Owner

Summary

  • Integrate Google Error Prone and Uber NullAway as compile-time static analysis tools to catch potential bugs and null pointer issues
  • Configure Error Prone and NullAway to run during main source compilation with NullAway errors enforced for the net.ladenthin package
  • Disable NullAway for test compilation to avoid false positives in test code
  • Add JVM module exports configuration to .mvn/jvm.config to support Error Prone's compiler plugin access on Java 9+

Test plan

  • CI is green on this branch (static analysis integration verified)
  • Affected unit / integration tests pass locally (no behavioral changes to source code)

Related issues / PRs

Checklist

  • I have read CONTRIBUTING.md and CODE_OF_CONDUCT.md
  • My commits follow Conventional Commits
  • No security-sensitive changes (if there are, I have notified the maintainer privately per SECURITY.md)

https://claude.ai/code/session_01FBmnengTN8krb1Vw46gd2Z

claude added 2 commits May 24, 2026 08:18
Enables Error Prone 2.30.0 (last release supporting -source 8) and
NullAway 0.10.26 as javac plugins. NullAway is enforced as ERROR on
the main package (net.ladenthin) and disabled for test compilation
where JMH @setup fields and intentional null arguments would otherwise
trip it. .mvn/jvm.config supplies the --add-exports / --add-opens
required by Error Prone when running on JDK 16+.
Empirically verified that current Error Prone supports compiling
-source 8/-target 8 on JDK 21 when -XDaddTypeAnnotationsToSymbol=true
is passed, contrary to my earlier assumption that 2.30.0 was the last
Java-8-capable release. Only the JDK required to *run* Error Prone has
moved forward (see google/error-prone#4867).
The jvm.config format doesn't allow comments, so the SPDX header
lives in a .license sidecar per REUSE specification.
CodeQL's autobuild runs 'mvn package -DskipTests' which previously
still triggered the exec-maven-plugin jcstress execution bound to the
test phase, failing because test classes weren't compiled. Adding
<skip>${skipTests}</skip> to that execution propagates -DskipTests.

Separately, the earlier Error Prone commit set <annotationProcessorPaths>
which makes the compiler ignore classpath-based processors. That broke
jcstress and JMH annotation processing during test-compile. Adding both
processors to the test-compile path with combine.children="append"
restores them while preserving Error Prone + NullAway.
@codecov
Copy link
Copy Markdown

codecov Bot commented May 24, 2026

Codecov Report

❌ Patch coverage is 92.30769% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 97.58%. Comparing base (100ef71) to head (699abb6).

Files with missing lines Patch % Lines
.../java/net/ladenthin/streambuffer/StreamBuffer.java 92.30% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main      #75      +/-   ##
============================================
- Coverage     97.60%   97.58%   -0.02%     
  Complexity       93       93              
============================================
  Files             1        1              
  Lines           250      248       -2     
  Branches         32       32              
============================================
- Hits            244      242       -2     
  Partials          6        6              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@coveralls
Copy link
Copy Markdown

coveralls commented May 24, 2026

Coverage Status

coverage: 100.0%. remained the same — claude/gracious-carson-gRcSp into main

Enforces Palantir Java Format 2.66.0 on all main and test sources via
the spotless-maven-plugin. The spotless:check goal is bound to the
verify phase so CI fails on formatting drift; run 'mvn spotless:apply'
to fix. The initial apply touches most files and is mechanical.
Adds a Build Commands subsection covering how to run the existing JMH
benchmarks under exec-maven-plugin, with filter syntax, the gc allocation
profiler, and async-profiler (flamegraph) invocation.
Runs SpotBugs at default effort/threshold on main classes during
the verify phase, augmented with the fb-contrib and findsecbugs
detector plugins. The single initial finding - EI_EXPOSE_REP on
getOutputStream/getInputStream - is suppressed via spotbugs-exclude.xml
because those streams are the public API of StreamBuffer.
Marks the Deque buffer with @GuardedBy("bufferLock") so Error Prone
enforces that every access happens under the bufferLock monitor.
Propagates the annotation to the two methods that touch buffer outside
an explicit synchronized block - trim() and isTrimShouldBeExecuted() -
since their docstrings already require callers to hold bufferLock.
Adds error_prone_annotations as a compile dependency.
Whitebox unit tests intentionally call isTrimShouldBeExecuted() without
holding bufferLock to verify the predicate logic in isolation.
GuardedBy stays enforced for main code.
Encodes two structural invariants as JUnit tests:

  - mainCodeStaysLeaf: the published library must only depend on the
    JDK and the Error Prone GuardedBy annotation; any new runtime
    dependency will surface as a failing test.
  - dequeFieldsArePrivate: Deque fields must be private, preventing
    the internal buffer from being exposed via a future getter or
    package-private leak.
Makes both libraries available for the existing test suite:

  - Awaitility 4.2.2: replaces Thread.sleep + poll patterns with
    await().until(...) for deterministic concurrency assertions.
  - ConcurrentUnit 0.4.6: Waiter.assertEquals/assertTrue surface
    assertion failures from background threads back to the test
    thread (JUnit can't see them otherwise).

No tests refactored in this commit; libraries are added now so future
refactors are local, single-purpose changes.
Lincheck's model checker enumerates thread schedules for write, close,
available and isClosed operations. read() is excluded because blocking
reads are incompatible with the schedule enumeration model checker -
jcstress remains the test for concurrent read+write races.

The test runs 20 iterations of 500 invocations each across 2 threads
with 3 actors per thread. Local run: ~9s.
Wires com.vmlens:api as a test dependency and the vmlens-maven-plugin
inside a 'vmlens' profile. Default 'mvn test' is unchanged; running
'mvn -Pvmlens test' drives tests written against AllInterleavings
through every possible thread interleaving. Off by default because
vmlens overhead is too high for every build.
The vmlens profile now excludes StreamBufferLincheckTest because vmlens
load-time instrumentation conflicts with the bytecode Lincheck generates
for TestThreadExecution (java.lang.VerifyError, stack map mismatch).
The default test job still runs Lincheck.

Adds a vmlens job to publish.yml that runs in parallel with the regular
Test (JDK 21) job, uploads target/vmlens-report as an artifact, and
does not block coverage reporting.
  - read_parallelClose_noDeadlock: replaces Thread.sleep(500) with
    await().until(reader.getState() in WAITING/TIMED_WAITING). The
    test now blocks only as long as it actually takes the reader
    thread to park inside read(), instead of guessing 500ms.

  - concurrentReadWrite_stressTest: pipes background-thread
    IOExceptions through net.jodah.concurrentunit.Waiter so a real
    failure surfaces with its original stack trace instead of being
    re-thrown as a RuntimeException and lost to
    Thread.UncaughtExceptionHandler.
The vmlens-maven-plugin runs its own forked surefire and does not pick
up excludes configured on the standalone maven-surefire-plugin. Moving
the **/StreamBufferLincheckTest.java exclude into the vmlens plugin's
own <excludes> block (preserving its **/*$* default for inner
classes) is what actually skips the Lincheck test under vmlens.

Local: mvn -Pvmlens test now passes in ~4 min with 269/270 tests
(Lincheck skipped, runs in the default test job).
… Exception

Sonar's reliability rule java:S1181 flags methods declaring Throwable
because it lumps Errors with checked Exceptions. Waiter.resume() only
throws AssertionError (unchecked) and Thread.join() throws
InterruptedException; throws Exception is sufficient and matches the
original signature.
- Fix Reliability bug at StreamBufferTest:858: hoist os.close() out of
  the assertThrows lambda so only writeAnyValue() can produce the
  expected IOException (java:S5778).
- Add explicit assertions to six tests Sonar flagged as having none
  (assertDoesNotThrow / assertTimeoutPreemptively / state checks).
- Refactor write_nullArrayWithOffset_throwsNPE so the assertThrows
  lambda has exactly one possibly-throwing call.
- Rename WriteMethod constants to UPPER_SNAKE_CASE to satisfy the
  Java naming convention (java:S115).
- Explain why the IOException catches in ConcurrentWriteRace are
  intentionally empty (race with concurrent close()).
@sonarqubecloud
Copy link
Copy Markdown

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.

3 participants