Skip to content

Commit 14c5f8e

Browse files
Merge pull request #263 from vaiju1981/chore/spotbugs-effort-max
chore: make SpotBugs (effort=Max, threshold=Low) clean again
2 parents 922d82c + 347bdd1 commit 14c5f8e

4 files changed

Lines changed: 47 additions & 9 deletions

File tree

TODO.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,15 @@ Feel free to contribute fixes — PRs welcome.
442442
443443
- **Null-safety refinement.** JSpecify + NullAway are now enforced at compile time in **strict JSpecify mode** with the extra options `CheckOptionalEmptiness`, `AcknowledgeRestrictiveAnnotations`, `AcknowledgeAndroidRecent`, `AssertsEnabled` (see `pom.xml`); `@NullMarked` on the three packages via `package-info.java`; JDK module exports in `.mvn/jvm.config`. The legacy `org.jetbrains.annotations` dep has been removed; all nullability annotations are JSpecify. Public-API methods that may legitimately have no value use `Optional<T>` rather than `@Nullable T` (`ChatResponse.getFirstMessage`, `ChatMessage.getParts`, `ChatRequest.buildToolsJson`). Open follow-up: review remaining unannotated public API surfaces for places where `@Nullable` would be more precise than the implicit non-null default.
444444
445-
- **SpotBugs `effort=Max` + `threshold=Low`** — currently default effort/threshold. Raising both surfaces ~65 remaining findings (was 90; the cross-repo `OPM_OVERLY_PERMISSIVE_METHOD` suppression in `07109cc` silenced 25 of them pending the package refactor — see below). Top remaining patterns: `DRE_DECLARED_RUNTIME_EXCEPTION` 20, `WEM_WEAK_EXCEPTION_MESSAGING` 14. The BAF/sb/plugin playbook applies: flip pom, run `spotbugs:check`, fix at source where reasonable + narrow `<Match>` with rationale for structural false positives. Cross-cutting (tracked in [`../workspace/crossrepostatus.md`](../workspace/crossrepostatus.md)).
445+
- **SpotBugs `effort=Max` + `threshold=Low`** — **DONE (already enabled in `pom.xml`)**, with fb-contrib +
446+
findsecbugs, bound to `verify`. The legacy "flip the pom / ~65 findings" note is stale: only a handful
447+
of unexcluded findings remain at any time, and `spotbugs:check` is kept green. Most recent pass fixed the
448+
6 introduced by the audit Tier-1–3 fixes — `withScalar` uses a single `instanceof Number` (no
449+
`ITC_INHERITANCE_TYPE_CHECKING`); `ChatMessage.getToolCalls` returns a fresh unmodifiable view (no
450+
`EI_EXPOSE_REP`); the `LlamaModel` batch methods' deliberate re-throw and the `ChatMessage` public
451+
constructor's `List` param carry narrow `<Match>` rationale suppressions.
452+
**Note:** `spotbugs:check` is bound to the `verify` phase, which the model-backed CI test jobs
453+
(`mvn test` / `mvn package`) do not reach — run `mvn verify` (or a dedicated job) to gate it in CI.
446454
447455
- **Drop the project-wide `OPM_OVERLY_PERMISSIVE_METHOD` suppression in
448456
`spotbugs-exclude.xml`** once the package-architecture refactor lands

spotbugs-exclude.xml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,36 @@ SPDX-License-Identifier: MIT
347347
<Method name="invoke"/>
348348
</Match>
349349

350+
<!--
351+
LlamaModel.completeBatch / completeBatchWithStats / chatBatch dispatch N async requests, then
352+
deliberately join EVERY future before re-throwing the first captured failure (a
353+
CompletionException) — so a partial failure never abandons sibling requests running unobserved.
354+
Re-throwing the captured RuntimeException is the intended behaviour; fb-contrib flags any
355+
`throw <RuntimeException>` as THROWS_METHOD_THROWS_RUNTIMEEXCEPTION.
356+
-->
357+
<Match>
358+
<Class name="net.ladenthin.llama.LlamaModel"/>
359+
<Bug pattern="THROWS_METHOD_THROWS_RUNTIMEEXCEPTION"/>
360+
<Or>
361+
<Method name="completeBatch"/>
362+
<Method name="completeBatchWithStats"/>
363+
<Method name="chatBatch"/>
364+
</Or>
365+
</Match>
366+
367+
<!--
368+
ChatMessage's public constructor intentionally accepts List<ToolCall> (the natural,
369+
ergonomic public-API type, matching the parts constructor). fb-contrib notes the parameter
370+
is only consumed as a Collection (defensively copied into an ArrayList) and suggests widening
371+
it; doing so on a public constructor would be a binary-incompatible API change for no caller
372+
benefit, so the concrete List type is kept deliberately.
373+
-->
374+
<Match>
375+
<Class name="net.ladenthin.llama.value.ChatMessage"/>
376+
<Bug pattern="OCP_OVERLY_CONCRETE_COLLECTION_PARAMETER"/>
377+
<Method name="&lt;init&gt;"/>
378+
</Match>
379+
350380
<!--
351381
ChatMessage.requireNonNull is a precondition guard whose only
352382
meaningful state to report is the parameter name itself (the value

src/main/java/net/ladenthin/llama/parameters/JsonParameters.java

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -139,13 +139,11 @@ protected final <T extends JsonParameters> T withRaw(String key, String value) {
139139
*/
140140
@SuppressWarnings("TypeParameterUnusedInFormals")
141141
protected final <T extends JsonParameters> T withScalar(String key, Object value) {
142-
// String.valueOf(Float.NaN)/Infinity produce "NaN"/"Infinity", which are NOT valid JSON
143-
// tokens and would make the whole request body unparseable by the native nlohmann parser.
144-
// Reject at the source so the caller gets a clear error instead of an opaque downstream failure.
145-
if (value instanceof Float && !Float.isFinite((Float) value)) {
146-
throw new IllegalArgumentException(key + " must be a finite number but was " + value);
147-
}
148-
if (value instanceof Double && !Double.isFinite((Double) value)) {
142+
// String.valueOf on a non-finite float/double yields "NaN"/"Infinity" — invalid JSON tokens
143+
// the native nlohmann parser rejects. Reject at the source so the caller gets a clear error
144+
// instead of an opaque downstream failure. Integer/Long are always finite (a no-op here);
145+
// Boolean is not a Number and is skipped.
146+
if (value instanceof Number && !Double.isFinite(((Number) value).doubleValue())) {
149147
throw new IllegalArgumentException(key + " must be a finite number but was " + value);
150148
}
151149
return withPut(key, String.valueOf(value));

src/main/java/net/ladenthin/llama/value/ChatMessage.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,9 @@ public Optional<String> getToolCallId() {
186186
* @return the calls list, never {@code null}; empty when the message is not a tool-call turn
187187
*/
188188
public List<ToolCall> getToolCalls() {
189-
return toolCalls;
189+
// Return a fresh unmodifiable view (mirrors getParts) so the stored list is never
190+
// exposed directly — the caller cannot mutate this value type's internal state.
191+
return Collections.unmodifiableList(toolCalls);
190192
}
191193

192194
/**

0 commit comments

Comments
 (0)