Skip to content

spotbugs_ignore: suppress SE_BAD_FIELD for cdom facet events#7632

Open
Vest wants to merge 1 commit into
PCGen:masterfrom
Vest:spotbugs-se-bad-field
Open

spotbugs_ignore: suppress SE_BAD_FIELD for cdom facet events#7632
Vest wants to merge 1 commit into
PCGen:masterfrom
Vest:spotbugs-se-bad-field

Conversation

@Vest

@Vest Vest commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

All 10 SE_BAD_FIELD findings under pcgen.cdom.* are on EventObject subtypes — facet change events that inherit Serializable from java.util.EventObject but hold runtime-only identifiers (CharID, VariableID, Skill, PCClassLevel, references to mutable domain objects). The Serializable contract is inherited from EventObject, not exercised by pcgen — grep -r ObjectOutputStream\|writeObject code/src/java returns nothing.

Affected classes

Class Field(s)
pcgen.cdom.facet.CategorizedDataFacetChangeEvent category
pcgen.cdom.facet.SkillRankFacet$SkillRankChangeEvent charID
pcgen.cdom.facet.analysis.LevelFacet$LevelChangeEvent charID
pcgen.cdom.facet.event.SubScopeFacetChangeEvent charID
pcgen.cdom.facet.model.ClassFacet$ClassLevelChangeEvent charID
pcgen.cdom.facet.model.ClassFacet$ClassLevelObjectChangeEvent charID, newLvl, oldLvl
pcgen.cdom.formula.VariableChangeEvent varID
pcgen.cdom.reference.UnconstructedEvent reference

Why suppress rather than mark transient

Marking each non-Serializable field transient (Option A in the analysis) would be 10 field annotations + adding serialVersionUID to each class — ceremony for a code path that never executes. The field types (CharID etc.) are runtime identifiers whose values would be meaningless after deserialization onto a different JVM run anyway, so transient would be the correct annotation if anyone ever did try to serialize an event, but the practical result is the same as suppressing: the field doesn't make the journey.

Dropping Serializable entirely (Option B — replacing EventObject with a project-local non-Serializable event base) is more architecturally honest but costs ~50–80 LOC and a project-local convention where the JDK already has one (EventObject is the event-object idiom in Java). Net result is the same 10 findings cleared.

Why not just fix it

Same shape as PR #7625 (EI_EXPOSE_REP) and PR #7626 (IMPROPER_UNICODE): the SpotBugs rule is technically correct, but it does not apply to PCGen's design. The events are deliberately runtime-only, inheriting Serializable accidentally from EventObject. The honest answer is "the contract is inherited, not used" — captured in the XML comment.

Filter rule

<Match>
  <!-- Facet events extend java.util.EventObject (Serializable) but hold
       runtime identifiers (CharID, VariableID, references to mutable
       domain objects). Nothing in pcgen actually serializes these events
       - the contract is inherited from EventObject, not used. Marking
       each field transient would be ceremony for a code path that never
       executes; suppressing keeps the events honest about being runtime
       constructs. See PR #7625's comment for the broader rationale: the
       cdom layer is wired by shared reference, not by value-copy. -->
  <Or>
    <Package name="~pcgen[.]cdom[.]facet.*"/>
    <Package name="~pcgen[.]cdom[.]formula.*"/>
    <Package name="~pcgen[.]cdom[.]reference.*"/>
  </Or>
  <Bug pattern="SE_BAD_FIELD"/>
</Match>

Scope is narrowed to facet/formula/reference subpackages rather than blanket pcgen.cdom.*. A survey confirmed zero non-event Serializable classes exist in these packages today; if one is added in future the narrower scope makes it easier to spot.

This mirrors the existing SE_NO_SERIALVERSIONID suppression at spotbugs_ignore.xml:24 ("not relevant to pcgen"), which is the same family of decision.

SpotBugs delta

Before After
Total findings 77 67
SE_BAD_FIELD 10 0

Verification

  • ./gradlew spotbugsMain — 10 SE_BAD_FIELD findings cleared, 0 new findings introduced.
  • ./gradlew :test --tests "pcgen.cdom.facet.*" — clean (filter-only change, no source modifications).

Scope note

Standalone — independent of the still-open #7627 (mark CDOM classes final), #7631 (concurrency hygiene), and the merged #7624#7628. Branched off fresh origin/master.

All 10 SE_BAD_FIELD findings are on EventObject subtypes in pcgen.cdom.*:

  - pcgen.cdom.facet.CategorizedDataFacetChangeEvent #category
  - pcgen.cdom.facet.SkillRankFacet$SkillRankChangeEvent #charID
  - pcgen.cdom.facet.analysis.LevelFacet$LevelChangeEvent #charID
  - pcgen.cdom.facet.event.SubScopeFacetChangeEvent #charID
  - pcgen.cdom.facet.model.ClassFacet$ClassLevelChangeEvent #charID
  - pcgen.cdom.facet.model.ClassFacet$ClassLevelObjectChangeEvent
      #charID, #newLvl, #oldLvl
  - pcgen.cdom.formula.VariableChangeEvent #varID
  - pcgen.cdom.reference.UnconstructedEvent #reference

These classes extend java.util.EventObject, which carries the
Serializable marker. The flagged fields (CharID, VariableID, Skill,
PCClassLevel, references) hold runtime identifiers that would be
meaningless after deserialization on a different JVM run.

Verified: zero ObjectOutputStream/writeObject references anywhere in
the codebase. The Serializable contract is inherited from EventObject,
not exercised by pcgen. Marking each field 'transient' would be 10
field annotations + serialVersionUIDs of ceremony for a code path that
never runs.

Same shape as the existing SE_NO_SERIALVERSIONID suppression (line 24,
'not relevant to pcgen') and consistent with the EI_EXPOSE_REP rule
added in PR PCGen#7625 - the cdom layer is wired by shared reference, not
by value-copy.

The scope is narrowed to facet/formula/reference subpackages of
pcgen.cdom rather than all of pcgen.cdom.*; a survey confirmed no
non-event Serializable classes exist in these packages today, and if
one is added in future the narrower scope makes it easier to spot.

Drops spotbugs findings from 77 -> 67.
@Vest

Vest commented Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

We have decided not to use transient in #7593. That's why I'd better skip the check for some (but not all) packages only.

@Vest Vest marked this pull request as ready for review June 25, 2026 11:54
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