Skip to content

Use records instead of Immutables for plain value types - #98

Merged
abashev merged 4 commits into
mainfrom
records-instead-of-immutables
Oct 2, 2026
Merged

abashev merged 4 commits into
mainfrom
records-instead-of-immutables

Conversation

@abashev

@abashev abashev commented Oct 2, 2026

Copy link
Copy Markdown
Collaborator

Follows #97: it builds on that PR's conventions.

Immutables generated eleven types. This turns the eight that are plain values into records and leaves the three that cannot be records as they stand.

What changes

  • open-java-format-jdk-bootstrap. FormatterCliArgs and FormatterNativeImageArgs, the arguments of a formatter process, are records built through their constructors. The module no longer runs the Immutables processor.
  • open-java-format. State's BreakState, LevelState and TokState, Level's SplitsBreaks, OpsBuilder's OpsOutput and InputMetadata are records. The call sites use constructors instead of builders and of(...); the accessors keep their names.

What stays on Immutables

  • OpenOp and Break extend HasUniqueId, whose per-instance id orders the formatter's persistent collections, and a record cannot extend a class. They need the Doc hierarchy reworked first.
  • State copies itself through its builder in about a dozen places. As a record each copy would list all ten components, or it would need a hand-written builder. It is created in the layout's hot loop, so that change wants a JMH measurement of its own.

Worth a look

  • InputMetadata keeps @Immutable, which Error Prone now checks against the record's components; before, the generated class was excluded from analysis. That needed @Immutable on OpsBuilder.BlankLineWanted as well. Its two subclasses hold only an Optional<Boolean> and an ImmutableList<BreakTag>, and Error Prone verifies them.

Checked

  • The 15,747 files of the JDK 21 sources format byte for byte as before: diff -rq of the trees formatted by the allJar of Replace palantir's Gradle plugins with plain Gradle #97's head, which main now holds, and of this branch.
  • ./gradlew test: 1597 tests in :open-java-format and 10 in the IDEA plugin. With -PnativeImage=true, 60 in the Gradle plugin and 8 in jdk-bootstrap, whose FormatterServicesTest runs both changed services against a JDK and the native image.
  • formatDiff through the released plugin.

FormatterCliArgs and FormatterNativeImageArgs only gathered the
arguments of a formatter process and turned them into its command
line; Immutables generated a builder for each. They are records now,
built through their constructors, and jvmArgsForVersion does what the
builder's withJvmArgsForVersion did. open-java-format-jdk-bootstrap no
longer runs the Immutables processor.

FormatterServicesTest, which runs both services against a JDK and the
native image, passes with -PnativeImage=true.
Six of the types Immutables generated in open-java-format were plain
values: State's BreakState, LevelState and TokState, Level's
SplitsBreaks, OpsBuilder's OpsOutput and InputMetadata. They are
records now. The call sites use their constructors instead of builders
and of(...) factories, and the accessors keep their names.

InputMetadata keeps @immutable, which Error Prone now checks against the
record's components instead of skipping a generated class. That needed
BlankLineWanted to say it is immutable as well, and it is: its two
subclasses hold an Optional<Boolean> and an ImmutableList<BreakTag>.

OpenOp, Break and State stay on Immutables. OpenOp and Break extend
HasUniqueId, whose per-instance id orders the formatter's persistent
collections, and a record cannot extend a class. State copies itself
through its builder in a dozen places.

The 15,747 files of the JDK 21 sources format exactly as before.
@abashev
abashev merged commit 26f6755 into main Oct 2, 2026
16 checks passed
@abashev
abashev deleted the records-instead-of-immutables branch October 2, 2026 21:30
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