Skip to content

Bring over upstream #1707 (Java 25 syntax) and #1786 (JDK 27 end positions) - #30

Merged
abashev merged 10 commits into
mainfrom
upstream-prs-1707-1786
Sep 23, 2026
Merged

abashev merged 10 commits into
mainfrom
upstream-prs-1707-1786

Conversation

@abashev

@abashev abashev commented Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #22. It also carries the JDK matrix from #29, which was closed without merging, so the tests run on JDK 25, 26 and 27 as well.

From upstream, with the authors kept

  • Support JDK 27 end positions (JDK-8372948) palantir/palantir-java-format#1786 by Sylwester Lachiewicz, commit authored by him. JDK 27 (JDK-8372948) removed EndPosTable and JCCompilationUnit.endPositions. Trees now resolves the end-position API through a method handle, and Trees.newParser passes the parser flags in the order each JDK expects. The changelog commit is left out.
  • Format module imports, compact source files, and unnamed patterns palantir/palantir-java-format#1707 by Pavel Finkelshtein (asm0dey), 15 commits squashed into one, authored by him and keeping upstream's Co-authored-by trailer.
    • Formats import module …; (JEP 511), compact source files (JEP 512) and unnamed patterns such as case Box(_, _) (JEP 456).
    • ImportOrderer renders each import from its tokens.
    • Paths moved to open-java-format/.
    • Left out: the changelog, gradle/jdks/25/**, the gradle-jdks build changes and their testJdk23/testJdk25 tasks, and the README changes.

Added here

  • var on JDK 27. After Support JDK 27 end positions (JDK-8372948) palantir/palantir-java-format#1786, every var declaration still failed on JDK 27: 24 golden tests. JDK 27 (JDK-8268850) gives var a VarTypeTree where older JDKs have no type, and this visitor has no visitVarType, because it compiles against JDK 21. Two changes fix it:
  • Native metadata for the Support JDK 27 end positions (JDK-8372948) palantir/palantir-java-format#1786 method handles. The image is built with --exact-reachability-metadata. Built without these entries, it failed on every file, even class A{int x;}, with "Cannot reflectively access method JCTree#getEndPosition(EndPosTable)".
  • FormatterVersionTest wired to -PjavaRuntime. A jdk leg that runs on the wrong JDK fails.
  • Native smoke test. It now also formats a Java 25 compact source file with import module and Box(_, _).
  • Full exceptions in test logs. The short form hid the formatter's message and TestKit build output.
  • JDK 27 leg skips the Gradle plugin's tests. The matrix names each leg's extra Gradle arguments and the reason for them, shown as a notice. Only JDK 27 has any: -x :gradle-open-java-format:test. Gradle 9.7.1 does not run on Java 27 yet, so TestKit builds there fail with "Unsupported class file major version 71".

Merged with main

main has since gained #43 and #44, which change the same import code. The merge, ecc0647, keeps both:

Checked

  • CI: all 9 jobs are green on the merge commit ecc0647. The JDK 27 leg ran 1502 tests and all of them passed, FormatterVersionTest included, which confirms that the run used JDK 27. The native smoke test passed on all five platforms.

  • Tests: after the merge, all 1493 formatter tests pass on JDK 21.

  • Same output as main. Both the main jar (43f5d3d) and the merged branch's jar were run over two corpora:

    Corpus Files Changed by the formatter Differ between main and this branch
    JDK 21 sources, on Temurin 21 15,747 14,090 0
    JDK 25 sources, on Corretto 25 15,368 13,635 0

    Neither jar reports an error. The "Imports not contiguous" failures from the earlier check are gone, because Keep comments between imports with the import after them #44 handles comments between imports.

  • The merge's edge cases, end to end. Each case formats, and a second run leaves the output unchanged:

    • a javadoc after an import;
    • a javadoc after the last import;
    • comments inside, before and after declarations, next to a static import;
    • a duplicate with a comment before it;
    • unused imports around a comment.

abashev and others added 9 commits September 22, 2026 18:00
The formatter parses with the javac of the JDK it runs on, so a change can pass
on 21 and break on a newer JDK. A new `jdk` job runs the tests of `build` on
25, 26 and 27, in parallel with the other jobs; nothing waits for it.

-PjavaRuntime sets baseline's javaVersions.runtime, which moves only the test
JVMs: the code is still compiled for Java 21 by JDK 21. The IntelliJ plugin's
tests keep the runtime of the IDE they start. `build` keeps JDK 21 and its
name, which the ruleset on main requires.
JDK-8372948 removed EndPosTable and JCCompilationUnit.endPositions and
re-ordered the four-argument ParserFactory.newParser. Trees now resolves
the end-position API once through a method handle and all three call
sites go through it; Trees.newParser passes the parser flags in the order
each JDK generation expects.

From palantir#1786 by Sylwester Lachiewicz, commit
81e3fff, without its changelog entry.
From palantir#1707 by Pavel Finkelshtein (asm0dey),
its 15 commits up to 53bea7f squashed into one. The formatter failed on
`import module ...;` (JEP 511), on compact source files with an instance
main (JEP 512) and on unnamed patterns such as `case Box(_, _)` (JEP 456).
ImportOrderer now renders each import from its tokens instead of rebuilding
it, so whitespace and comments inside an import declaration are accepted.

Adapted while bringing it over:
- paths moved from palantir-java-format/ to open-java-format/;
- left out: the changelog entry, the gradle/jdks/25 files, and the build
  changes for palantir's gradle-jdks with the testJdk23 and testJdk25
  tasks. Here the JDK 25 and later runs come from CI's jdk jobs;
- left out: the README changes, written for upstream's README;
- RemoveUnusedImports takes the end position through Trees.getEndPosition,
  from palantir#1786.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Since palantir#1786, Trees reaches
JCTree.getEndPosition(EndPosTable) and JCCompilationUnit.endPositions
through method handles, so that the same code runs on JDK 27, where
neither exists. The image is built with --exact-reachability-metadata, so
without these entries every file failed, even `class A{int x;}`:
"Cannot reflectively access method 'com.sun.tools.javac.tree.JCTree#getEndPosition(com.sun.tools.javac.tree.EndPosTable)'".
FormatterVersionTest, from palantir#1707, compares the
test JVM with the expectedJavaVersion system property, which upstream's
testJdkNN tasks set. Here the build passes the -PjavaRuntime value, so a CI
leg that runs on another JDK fails instead of skipping the tests that need
a newer parser and still passing.
A compact source file with a module import and an unnamed pattern, formatted
by the image itself: this is the syntax palantir#1707
brings, and the image also needs its ImportTree#isModule metadata for it.
JDK 27 (JDK-8268850) gives a variable declared with `var` a VarTypeTree as
its type, where earlier JDKs have none. This visitor compiles against JDK 21
and has no visitVarType, so it scanned the new node to nothing and every
`var` declaration failed on JDK 27: six golden files in CI, among them
FlexibleConstructor, I959 and B380299722.

declareOne now looks for the `var` token before the type node, the same
reordering google-java-format made in google/google-java-format@075e025c94.
A binding pattern such as `Nested(var i)` has a method of its own, which
now treats a VAR_TYPE node like the missing type of the older JDKs.
In CI the short form gave only "FormatterException at
FormatterIntegrationTest.java:76", not what the formatter complained about,
and nothing of the output of a TestKit build that failed.
On JDK 27 every TestKit build failed with "BUG! exception in phase 'semantic
analysis' in source unit '_BuildScript_' Unsupported class file major
version 71": Gradle 9.7.1 does not run on Java 27, and TestKit runs the
builds on the test JVM. The formatter's own tests all pass there.

Each leg of the matrix now names its extra Gradle arguments and the reason
for them, empty for 25 and 26. The 27 leg passes -x
:gradle-open-java-format:test, and the job shows the reason as a notice,
so it is visible in the run and not only in a YAML comment.
abashev added a commit that referenced this pull request Sep 22, 2026
buildSrc listed only the Gradle Plugin Portal, which serves JReleaser's
plugin itself but only proxies the rest from Maven Central. On 2026-09-22
that proxy answered 404 for jreleaser-engine 1.26.0 and failed the
linux-x86-64 native job of PR #30, while the same commit built on the other
four runners. With Maven Central listed first, as in the root buildscript,
those modules come from Central directly.
@abashev
abashev enabled auto-merge September 22, 2026 17:34
Conflicts with #43 and #44 in ImportOrderer, RemoveUnusedImports and
their tests.

ImportOrderer: an import now carries both the comments on the lines
before it, from #44, and its declaration rendered from its toks, from
this branch (palantir#1707). This branch's loop
over same-line block comments already covers #44's single block comment
after the `;`, so #44's copy of that step goes. Two interactions are
decided here:

- A javadoc comment right after an import's `;` is no longer an error.
  This branch rejected it because the formatter moves it onto a line of
  its own, which used to separate the imports. Since #44 a comment
  between imports goes with the import after it, and so does this one.
- Two copies of an import that differ only in the comment on the lines
  before them no longer collapse into one, so that comment stays. This
  branch already kept copies that differ in a comment inside the
  declaration.

RemoveUnusedImports: this branch's loop over JCTree with
Trees.getEndPosition, and #43's coalesced ranges and blank-line cleanup.

Tests: every case from both sides. This branch's case for a same-line
block comment repeated #44's and is folded into it. Its javadoc case now
expects the comment to move with the next import. A new case covers the
duplicate.

Checked: all 1493 formatter tests pass. On the JDK 21 sources (15,747
files, Temurin 21) and the JDK 25 sources (15,368 files, Corretto 25),
the merge formats every file exactly as main does, with no errors.
@abashev
abashev disabled auto-merge September 23, 2026 14:53
@abashev
abashev merged commit a8e4ae3 into main Sep 23, 2026
13 checks passed
@abashev
abashev deleted the upstream-prs-1707-1786 branch September 23, 2026 14:53
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.

Bring over upstream PRs #1707 (Java 25 syntax) and #1786 (JDK 27 end positions)

3 participants