Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
061fdca
Init setup of Java 25 parser
Laurens-W Aug 21, 2025
804235f
Update copyright headers
Laurens-W Aug 21, 2025
90e99c1
Use specific early access jdk25
Laurens-W Aug 21, 2025
e525a39
Temporary Java 25 setup
Laurens-W Aug 21, 2025
a484e81
Stick to Java 24 for CI for now
Laurens-W Aug 21, 2025
fe161de
Adjust parser
Laurens-W Aug 21, 2025
8375633
Try Jdk 25
Laurens-W Aug 21, 2025
71a53e0
Remove copied tests
Laurens-W Aug 22, 2025
aa1d84f
Use `java_version: 25-ea` in receive-pr.yml
timtebeek Aug 22, 2025
cf8073e
Comment out rewrite-kotlin for now
timtebeek Aug 22, 2025
96b31c1
Restore rewrite-kotlin; problem is in .kts
timtebeek Aug 22, 2025
54abcdf
Upgrade to Gradle 9.0.0 for Java 25 support
timtebeek Aug 22, 2025
cd90994
Improve temp file handling for windows
Laurens-W Aug 25, 2025
e55442c
Fix test
Laurens-W Aug 25, 2025
8cdec25
Use Java 25-ea
Laurens-W Aug 25, 2025
b6712bf
Fix java-version key in CI workflow
timtebeek Aug 25, 2025
39067d5
With Java 25-ea
Laurens-W Aug 25, 2025
d7565c0
Fix JavaDoc parsing the ugly way
Laurens-W Aug 25, 2025
d9a3bec
Fix more test issues
Laurens-W Aug 26, 2025
1683bc8
Fix more test issues
Laurens-W Aug 26, 2025
f8f170a
Remove comment that no longer makes sense
Laurens-W Aug 27, 2025
389cf6a
Merge branch 'main' into java25-parser
timtebeek Aug 27, 2025
dd4c973
Fix npmTest for windows
Laurens-W Aug 27, 2025
d2b4e8b
Use java25 CI branch
Laurens-W Aug 27, 2025
594d1d6
Testing
Laurens-W Aug 28, 2025
75dbd12
Testing
Laurens-W Aug 28, 2025
948b5b3
Testing
Laurens-W Aug 28, 2025
59c1718
Polish
Laurens-W Aug 28, 2025
206d03c
Remove old backwards compatibility stuff
Laurens-W Aug 28, 2025
aedf5d7
Do not apply rewrite-plugin to rewrite-java-25 for now
Laurens-W Aug 28, 2025
f2a6738
Prevent licenseFormat and rewriteRun from running on java 25 parser
Laurens-W Aug 28, 2025
72a0e3a
Conditionally include rewrite-java-25
Laurens-W Aug 28, 2025
957cd29
Try something else
Laurens-W Aug 28, 2025
78e0070
Dynamically switch toolchain
Laurens-W Aug 28, 2025
72336b0
Revert for now
Laurens-W Aug 28, 2025
e67d92b
Testing
Laurens-W Aug 28, 2025
78cc934
Testing
Laurens-W Aug 28, 2025
8e1232f
Testing
Laurens-W Aug 28, 2025
7f993fc
Merge branch 'main' into java25-parser
timtebeek Sep 1, 2025
0d80476
Temporarily disable workflow
Laurens-W Sep 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ concurrency:
jobs:
build:
uses: openrewrite/gh-automation/.github/workflows/ci-gradle.yml@main
with:
java_version: |
25-ea
21
secrets:
gradle_enterprise_access_key: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }}
sonatype_username: ${{ secrets.SONATYPE_USERNAME }}
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/receive-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ concurrency:
# Since this pull request receives untrusted code, we should **NOT** have any secrets in the environment.
jobs:
upload-patch:
if: false # Temporarily disable to avoid excessive suggestion spam
uses: openrewrite/gh-automation/.github/workflows/receive-pr.yml@main
with:
java_version: |
25-ea
21
recipe: 'org.openrewrite.recipes.rewrite.OpenRewriteRecipeBestPracticesSubset'

1 change: 1 addition & 0 deletions IDE.properties.tmp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ rewrite-java-test
rewrite-java-tck
rewrite-java-17
rewrite-java-21
rewrite-java-25

# Other language modules

Expand Down
4 changes: 2 additions & 2 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionSha256Sum=bd71102213493060956ec229d946beee57158dbd89d0e62b91bca0fa2c5f3531
distributionSha256Sum=8fad3d78296ca518113f3d29016617c7f9367dc005f932bd9d93bf45ba46072b
6 changes: 3 additions & 3 deletions gradlew
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac

CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
CLASSPATH="\\\"\\\""


# Determine the Java command to use to start the JVM.
Expand Down Expand Up @@ -205,15 +205,15 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'

# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.

set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"

# Stop when "xargs" is not available.
Expand Down
4 changes: 2 additions & 2 deletions gradlew.bat
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,11 @@ goto fail
:execute
@rem Setup the command line

set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
set CLASSPATH=


@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*

:end
@rem End local scope for the variables with windows NT shell
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1761,15 +1761,8 @@ public J visitWildcard(WildcardTree node, Space fmt) {
// The spacing of initialized enums such as `ONE (1)` is handled in the `visitNewClass` method, so set it explicitly to “” here.
String prefix = isEnum(t) ? "" : source.substring(cursor, indexOfNextNonWhitespace(cursor, source));
cursor += prefix.length();
// Java 21 and 23 have a different return type from getCommentTree; with reflection we can support both
// Though this is the Java 17 parser, we still need to support it if people use this parser with newer Java versions (e.g. https://github.com/PicnicSupermarket/error-prone-support/pull/1557)
Method getCommentTreeMethod = DocCommentTable.class.getMethod("getCommentTree", JCTree.class);
DocCommentTree commentTree = (DocCommentTree) getCommentTreeMethod.invoke(docCommentTable, t);
@SuppressWarnings("unchecked") J2 j = (J2) scan(t, formatWithCommentTree(prefix, (JCTree) t, commentTree));
@SuppressWarnings("unchecked") J2 j = (J2) scan(t, formatWithCommentTree(prefix, (JCTree) t, docCommentTable.getCommentTree((JCTree) t)));
return j;
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException ex) {
reportJavaParsingException(ex);
throw new IllegalStateException("Failed to invoke getCommentTree method", ex);
} catch (Throwable ex) {
reportJavaParsingException(ex);
throw ex;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1788,14 +1788,8 @@ public J visitWildcard(WildcardTree node, Space fmt) {
// The spacing of initialized enums such as `ONE (1)` is handled in the `visitNewClass` method, so set it explicitly to “” here.
String prefix = isEnum(t) ? "" : source.substring(cursor, indexOfNextNonWhitespace(cursor, source));
cursor += prefix.length();
// Java 21 and 23 have a different return type from getCommentTree; with reflection we can support both
Method getCommentTreeMethod = DocCommentTable.class.getMethod("getCommentTree", JCTree.class);
DocCommentTree commentTree = (DocCommentTree) getCommentTreeMethod.invoke(docCommentTable, t);
@SuppressWarnings("unchecked") J2 j = (J2) scan(t, formatWithCommentTree(prefix, (JCTree) t, commentTree));
@SuppressWarnings("unchecked") J2 j = (J2) scan(t, formatWithCommentTree(prefix, (JCTree) t, docCommentTable.getCommentTree((JCTree) t)));
return j;
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException ex) {
reportJavaParsingException(ex);
throw new IllegalStateException("Failed to invoke getCommentTree method", ex);
} catch (Throwable ex) {
reportJavaParsingException(ex);
throw ex;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class ReloadableJava21TypeMapping implements JavaTypeMapping<Tree> {
private final JavaTypeCache typeCache;

public JavaType type(com.sun.tools.javac.code.@Nullable Type type) {
if (type == null || type instanceof Type.ErrorType || type instanceof Type.PackageType || isUnknownType(type) ||
if (type == null || type instanceof Type.ErrorType || type instanceof Type.PackageType || type instanceof Type.UnknownType ||
type instanceof NullType) {
return JavaType.Class.Unknown.getInstance();
}
Expand Down Expand Up @@ -488,7 +488,7 @@ public JavaType.Primitive primitive(TypeTag tag) {
}

if (selectType == null || selectType instanceof Type.ErrorType || symbol == null || symbol.kind == Kinds.Kind.ERR ||
isUnknownType(symbol.type)) {
symbol.type instanceof Type.UnknownType) {
return null;
}

Expand Down Expand Up @@ -553,7 +553,7 @@ public JavaType.Primitive primitive(TypeTag tag) {
exceptionTypes.add(javaType);
}
}
} else if (isUnknownType(selectType)) {
} else if (selectType instanceof Type.UnknownType) {
returnType = JavaType.Unknown.getInstance();
}

Expand Down Expand Up @@ -776,11 +776,4 @@ private Object annotationElementValue(Object value) {
}
return JavaType.Unknown.getInstance();
}

/**
* Check for the `UnknownType` which existed up until JDK 22; starting with JDK 23 only the `ErrorType` is used
*/
public static boolean isUnknownType(@Nullable Type type) {
return type != null && type.getClass().getName().equals("com.sun.tools.javac.code.Type$UnknownType");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
import java.util.Set;
import java.util.StringJoiner;

import static org.openrewrite.java.isolated.ReloadableJava21TypeMapping.isUnknownType;

class ReloadableJava21TypeSignatureBuilder implements JavaTypeSignatureBuilder {
@Nullable
Expand All @@ -42,7 +41,7 @@ public String signature(@Nullable Object t) {
}

private String signature(@Nullable Type type) {
if (type == null || isUnknownType(type) || type instanceof NullType) {
if (type == null || type instanceof Type.UnknownType || type instanceof NullType) {
return "{undefined}";
} else if (type instanceof Type.IntersectionClassType) {
return intersectionSignature(type);
Expand Down Expand Up @@ -300,7 +299,7 @@ private String methodArgumentSignature(Type selectType) {
return resolvedArgumentTypes.toString();
} else if (selectType instanceof Type.ForAll) {
return methodArgumentSignature(((Type.ForAll) selectType).qtype);
} else if (selectType instanceof Type.JCNoType || isUnknownType(selectType)) {
} else if (selectType instanceof Type.JCNoType || selectType instanceof Type.UnknownType) {
return "{undefined}";
}

Expand Down
94 changes: 94 additions & 0 deletions rewrite-java-25/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
@file:Suppress("UnstableApiUsage")

plugins {
id("org.openrewrite.build.language-library")
id("jvm-test-suite")
}

val javaTck = configurations.create("javaTck") {
isTransitive = false
}

dependencies {
api(project(":rewrite-core"))
api(project(":rewrite-java"))
implementation(project(":rewrite-java-lombok"))

compileOnly("org.slf4j:slf4j-api:1.7.+")

implementation("io.micrometer:micrometer-core:1.9.+")
implementation("io.github.classgraph:classgraph:latest.release")
implementation("org.ow2.asm:asm:latest.release")

testImplementation(project(":rewrite-test"))
testImplementation("org.antlr:antlr4-runtime:4.13.2")
"javaTck"(project(":rewrite-java-tck"))
}

java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(25))
}
}

tasks.named("licenseFormat") { enabled = false }

tasks.withType<JavaCompile>().configureEach {
// allows --add-exports to in spite of the JDK's restrictions on this
sourceCompatibility = JavaVersion.VERSION_25.toString()
targetCompatibility = JavaVersion.VERSION_25.toString()

options.release.set(null as Int?) // remove `--release 8` set in `org.openrewrite.java-base`
options.compilerArgs.addAll(
listOf(
"--add-exports", "jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
"--add-exports", "jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED",
"--add-exports", "jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED",
"--add-exports", "jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED",
"--add-exports", "jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
"--add-exports", "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED"
)
)
}

//Javadoc compiler will complain about the use of the internal types.
tasks.withType<Javadoc>().configureEach {
exclude(
"**/ReloadableJava25JavadocVisitor**",
"**/ReloadableJava25Parser**",
"**/ReloadableJava25ParserVisitor**",
"**/ReloadableJava25TypeMapping**",
"**/ReloadableJava25TypeSignatureBuilder**"
)
}

testing {
suites {
val test by getting(JvmTestSuite::class)

register("compatibilityTest", JvmTestSuite::class) {
dependencies {
implementation(project())
implementation(project(":rewrite-test"))
implementation(project(":rewrite-java-tck"))
implementation(project(":rewrite-java-test"))
implementation("org.assertj:assertj-core:latest.release")
}

targets {
all {
testTask.configure {
useJUnitPlatform()
testClassesDirs += files(javaTck.files.map { zipTree(it) })
jvmArgs = listOf("-XX:+UnlockDiagnosticVMOptions", "-XX:+ShowHiddenFrames")
shouldRunAfter(test)
}
}
}
}
}
}

tasks.named("check") {
dependsOn(testing.suites.named("compatibilityTest"))
}
100 changes: 100 additions & 0 deletions rewrite-java-25/src/main/java/org/openrewrite/java/Java25Parser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright 2025 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.java;

import org.jspecify.annotations.Nullable;
import org.openrewrite.ExecutionContext;
import org.openrewrite.SourceFile;
import org.openrewrite.java.internal.JavaTypeCache;

import java.lang.reflect.Constructor;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.util.Collection;
import java.util.stream.Stream;

public class Java25Parser implements JavaParser {
private final JavaParser delegate;

Java25Parser(JavaParser delegate) {
this.delegate = delegate;
}

@Override
public Stream<SourceFile> parseInputs(Iterable<Input> sourceFiles, @Nullable Path relativeTo, ExecutionContext ctx) {
return delegate.parseInputs(sourceFiles, relativeTo, ctx);
}

@Override
public JavaParser reset() {
delegate.reset();
return this;
}

@Override
public JavaParser reset(Collection<URI> cus) {
delegate.reset(cus);
return this;
}

@Override
public void setClasspath(Collection<Path> classpath) {
delegate.setClasspath(classpath);
}

public static Builder builder() {
return new Builder();
}

public static class Builder extends JavaParser.Builder<Java25Parser, Builder> {

@Nullable
private static ClassLoader moduleClassLoader;

static synchronized void lazyInitClassLoaders() {
if (moduleClassLoader != null) {
return;
}

ClassLoader appClassLoader = Java25Parser.class.getClassLoader();
moduleClassLoader = new JavaUnrestrictedClassLoader(appClassLoader);
}

@Override
public Java25Parser build() {
lazyInitClassLoaders();

try {
//Load the parser implementation use the unrestricted module classloader.
Class<?> parserImplementation = Class.forName("org.openrewrite.java.isolated.ReloadableJava25Parser", true, moduleClassLoader);

Constructor<?> parserConstructor = parserImplementation
.getDeclaredConstructor(Boolean.TYPE, Collection.class, Collection.class, Collection.class, Charset.class,
Collection.class, JavaTypeCache.class);

parserConstructor.setAccessible(true);

JavaParser delegate = (JavaParser) parserConstructor
.newInstance(logCompilationWarningsAndErrors, resolvedClasspath(), classBytesClasspath, dependsOn, charset, styles, javaTypeCache);

return new Java25Parser(delegate);
} catch (Exception e) {
throw new IllegalStateException("Unable to construct Java25Parser.", e);
}
}
}
}
Loading