Bring over upstream #1707 (Java 25 syntax) and #1786 (JDK 27 end positions) - #30
Merged
Merged
Conversation
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
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
EndPosTableandJCCompilationUnit.endPositions.Treesnow resolves the end-position API through a method handle, andTrees.newParserpasses the parser flags in the order each JDK expects. The changelog commit is left out.Co-authored-bytrailer.import module …;(JEP 511), compact source files (JEP 512) and unnamed patterns such ascase Box(_, _)(JEP 456).ImportOrdererrenders each import from its tokens.open-java-format/.gradle/jdks/25/**, the gradle-jdks build changes and theirtestJdk23/testJdk25tasks, and the README changes.Added here
varon JDK 27. After Support JDK 27 end positions (JDK-8372948) palantir/palantir-java-format#1786, everyvardeclaration still failed on JDK 27: 24 golden tests. JDK 27 (JDK-8268850) givesvaraVarTypeTreewhere older JDKs have no type, and this visitor has novisitVarType, because it compiles against JDK 21. Two changes fix it:declareOnechecks thevartoken first, the same reordering as google/google-java-format@075e025c94;VAR_TYPEnode asvar.--exact-reachability-metadata. Built without these entries, it failed on every file, evenclass A{int x;}, with "Cannot reflectively access methodJCTree#getEndPosition(EndPosTable)".FormatterVersionTestwired to-PjavaRuntime. Ajdkleg that runs on the wrong JDK fails.import moduleandBox(_, _).-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
mainmainhas since gained #43 and #44, which change the same import code. The merge, ecc0647, keeps both:;is no longer an error. Format module imports, compact source files, and unnamed patterns palantir/palantir-java-format#1707 rejected it because the formatter moves it onto a line of its own, which used to separate the imports. Since Keep comments between imports with the import after them #44, a comment between imports goes with the import after it, and so does this one.RemoveUnusedImportswalks module imports as Format module imports, compact source files, and unnamed patterns palantir/palantir-java-format#1707 does, and cleans up blank lines around deleted imports as Stop an unused import from leaving two blank lines behind #43 does.Checked
CI: all 9 jobs are green on the merge commit ecc0647. The JDK 27 leg ran 1502 tests and all of them passed,
FormatterVersionTestincluded, 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 themainjar (43f5d3d) and the merged branch's jar were run over two corpora:mainand this branchNeither 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: