Skip to content

Commit a8e4ae3

Browse files
authored
Merge pull request #30 from openjavaformat/upstream-prs-1707-1786
Bring over upstream palantir#1707 (Java 25 syntax) and palantir#1786 (JDK 27 end positions)
2 parents 43f5d3d + ecc0647 commit a8e4ae3

29 files changed

Lines changed: 1104 additions & 81 deletions

‎.github/workflows/ci.yml‎

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,68 @@ jobs:
3737
annotate_only: true
3838
job_summary: true
3939

40+
# The tests of `build` on the newer JDKs, in parallel with everything else: nothing waits for these
41+
# jobs and nothing they build is kept. The code is still compiled for Java 21 by JDK 21, as in the
42+
# release; -PjavaRuntime moves only the test JVMs, and with them the javac whose internals the
43+
# formatter parses with. The IntelliJ plugin's tests stay on the runtime of the IDE they start.
44+
# JDK 21 itself is `build`, the check the main branch requires.
45+
jdk:
46+
name: build (JDK ${{ matrix.jdk }})
47+
runs-on: ubuntu-latest
48+
strategy:
49+
fail-fast: false
50+
matrix:
51+
include:
52+
- jdk: 25
53+
gradle_args: ''
54+
gradle_args_reason: ''
55+
- jdk: 26
56+
gradle_args: ''
57+
gradle_args_reason: ''
58+
- jdk: 27
59+
gradle_args: -x :gradle-open-java-format:test
60+
gradle_args_reason: >-
61+
Gradle 9.7.1 does not run on Java 27 yet, and the Gradle plugin's tests run their TestKit builds on
62+
the test JVM, where they fail with "Unsupported class file major version 71". Those tests are left
63+
out until the wrapper's Gradle supports Java 27.
64+
steps:
65+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
66+
67+
- name: Install JDK ${{ matrix.jdk }} for the tests
68+
id: test-jdk
69+
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
70+
with:
71+
distribution: temurin
72+
java-version: ${{ matrix.jdk }}
73+
74+
# Installed last, so that it is the JAVA_HOME Gradle runs and compiles on.
75+
- name: Install JDK 21
76+
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
77+
with:
78+
distribution: temurin
79+
java-version: '21'
80+
81+
- name: Explain the extra Gradle arguments
82+
if: ${{ matrix.gradle_args != '' }}
83+
env:
84+
GRADLE_ARGS: ${{ matrix.gradle_args }}
85+
REASON: ${{ matrix.gradle_args_reason }}
86+
run: echo "::notice title=JDK ${{ matrix.jdk }} runs with $GRADLE_ARGS::$REASON"
87+
88+
# Gradle does not look into the runner's tool cache, so it is told where the test JDK is.
89+
- name: Build
90+
run: >-
91+
./gradlew test -PjavaRuntime=${{ matrix.jdk }} ${{ matrix.gradle_args }}
92+
-Porg.gradle.java.installations.paths=${{ steps.test-jdk.outputs.path }}
93+
94+
- name: Publish Test Report
95+
uses: mikepenz/action-junit-report@a9170d5795813c01ab4901ffb045b52bab4ab09d # v6.5.0
96+
if: ${{ !cancelled() }}
97+
with:
98+
report_paths: '**/build/test-results/test/*.xml'
99+
annotate_only: true
100+
job_summary: true
101+
40102
# One explicit job per target, so every binary we ship is traceable to a named run.
41103
native:
42104
name: native (${{ matrix.platform }})
@@ -87,7 +149,8 @@ jobs:
87149
env:
88150
JDK21_HOME: ${{ steps.jdk21.outputs.path }}
89151

90-
# The binary itself on a file that needs formatting, a formatted one and one that does not parse.
152+
# The binary itself on a file that needs formatting, a formatted one, one that does not parse and
153+
# one in Java 25 syntax.
91154
- name: Smoke-test the binary
92155
run: |
93156
binary="$PWD/$(ls open-java-format-native/build/native/nativeCompile/open-java-format-* | grep -v '\.txt$')"
@@ -100,6 +163,10 @@ jobs:
100163
printf 'class B {\n' > B.java
101164
set +e; "$binary" B.java; status=$?; set -e
102165
test "$status" -eq 2
166+
# Java 25 syntax: a compact source file with a module import and an unnamed pattern.
167+
printf 'import module java.base;\nrecord Box(int a,int b){}\nvoid main(){Object o=new Box(1,2);if(o instanceof Box(_,_)){IO.println(List.of(1));}}\n' > C.java
168+
"$binary" --replace C.java
169+
printf 'import module java.base;\n\nrecord Box(int a, int b) {}\n\nvoid main() {\n Object o = new Box(1, 2);\n if (o instanceof Box(_, _)) {\n IO.println(List.of(1));\n }\n}\n' | diff - C.java
103170
104171
- name: Test the plugins against the image
105172
run: ./gradlew -PnativeImage=true :open-java-format-jdk-bootstrap:test :gradle-open-java-format:test

‎README.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ mise trust && mise install
119119
./gradlew test # what the CI build job runs
120120
```
121121

122+
`-PjavaRuntime=25` runs the same tests on JDK 25, as CI's `jdk` jobs do for 25, 26 and 27. The code
123+
is compiled for Java 21 either way. Gradle has to find that JDK: installed with mise, or named with
124+
`-Porg.gradle.java.installations.paths=/path/to/jdk`.
125+
122126
Nothing inside the build downloads a JDK. `gradle.properties` turns toolchain auto-download off and
123127
reads the installations from `JDK21_HOME` and `GRAALVM_HOME`, so a missing JDK is an error you can
124128
read rather than a silent download.

‎build.gradle‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ allprojects {
8787

8888
tasks.withType(Test).configureEach {
8989
jvmArgs(javacInternalExports.collect { "--add-exports=${it}=ALL-UNNAMED".toString() })
90+
// The short form gave CI logs only "FormatterException at FormatterIntegrationTest.java:76", never what
91+
// the formatter complained about, nor the output of a TestKit build that failed.
92+
testLogging.exceptionFormat = 'full'
9093
}
9194

9295
tasks.withType(Javadoc).configureEach {
@@ -127,7 +130,11 @@ subprojects {
127130
}
128131
}
129132

133+
// The JDK the tests run on, and with it the javac whose internals the formatter parses with. The code is
134+
// compiled for 21 whatever this says. CI's `jdk` jobs pass -PjavaRuntime=25 and so on.
135+
ext.javaRuntime = providers.gradleProperty('javaRuntime').getOrElse('21')
136+
130137
javaVersions {
131138
libraryTarget = 21
132-
runtime = 21
139+
runtime = javaRuntime
133140
}

‎open-java-format-native/src/main/resources/META-INF/native-image/reachability-metadata.json‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,15 @@
131131
}
132132
]
133133
},
134+
{
135+
"type": "com.sun.source.tree.ImportTree",
136+
"methods": [
137+
{
138+
"name": "isModule",
139+
"parameterTypes": []
140+
}
141+
]
142+
},
134143
{
135144
"type": "com.sun.tools.javac.parser.JavaTokenizer",
136145
"fields": [
@@ -151,6 +160,28 @@
151160
}
152161
]
153162
},
163+
{
164+
"type": "com.sun.tools.javac.tree.EndPosTable"
165+
},
166+
{
167+
"type": "com.sun.tools.javac.tree.JCTree",
168+
"methods": [
169+
{
170+
"name": "getEndPosition",
171+
"parameterTypes": [
172+
"com.sun.tools.javac.tree.EndPosTable"
173+
]
174+
}
175+
]
176+
},
177+
{
178+
"type": "com.sun.tools.javac.tree.JCTree$JCCompilationUnit",
179+
"fields": [
180+
{
181+
"name": "endPositions"
182+
}
183+
]
184+
},
154185
{
155186
"type": "java.io.Serializable"
156187
},

‎open-java-format/build.gradle‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ tasks.named("test") {
7676
// Run all classes and tests in parallel
7777
// https://junit.org/junit5/docs/current/user-guide/#writing-tests-parallel-execution
7878
systemProperty 'junit.jupiter.execution.parallel.mode.default', 'concurrent'
79+
// FormatterVersionTest fails when the tests run on another JDK than the one asked for, so a CI leg
80+
// cannot pass on the wrong JDK and skip the tests that need a newer parser.
81+
systemProperty 'expectedJavaVersion', rootProject.ext.javaRuntime
7982
}
8083

8184
javaVersion {

‎open-java-format/src/main/java/com/palantir/javaformat/java/Formatter.java‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,8 @@ public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOExcept
184184
};
185185
Log.instance(context).useSource(source);
186186
ParserFactory parserFactory = ParserFactory.instance(context);
187-
JavacParser parser = parserFactory.newParser(
188-
sourceText, /*keepDocComments=*/ true, /*keepEndPos=*/ true, /*keepLineMap=*/ true);
187+
JavacParser parser =
188+
Trees.newParser(parserFactory, sourceText, /*keepDocComments=*/ true, /*keepLineMap=*/ true);
189189
unit = parser.parseCompilationUnit();
190190
unit.sourcefile = source;
191191

0 commit comments

Comments
 (0)