From f410acd08dd1d29af5334809bb1166c6f9b0cfcb Mon Sep 17 00:00:00 2001 From: lingenj Date: Tue, 7 Jan 2025 15:03:00 +0100 Subject: [PATCH 1/3] Lombok support for java 21 --- rewrite-java-21/build.gradle.kts | 1 + .../java/isolated/ReloadableJava21Parser.java | 146 ++++++++++++++---- .../ReloadableJava21ParserVisitor.java | 122 ++++++++++++--- .../org/openrewrite/java/tree/LombokTest.java | 2 +- 4 files changed, 211 insertions(+), 60 deletions(-) diff --git a/rewrite-java-21/build.gradle.kts b/rewrite-java-21/build.gradle.kts index 143abb855fc..8ab5e091748 100644 --- a/rewrite-java-21/build.gradle.kts +++ b/rewrite-java-21/build.gradle.kts @@ -17,6 +17,7 @@ val javaTck = configurations.create("javaTck") { dependencies { api(project(":rewrite-core")) api(project(":rewrite-java")) + runtimeOnly(project(":rewrite-java-lombok")) compileOnly("org.slf4j:slf4j-api:1.7.+") diff --git a/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21Parser.java b/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21Parser.java index 823e2103f10..05b20017fd9 100644 --- a/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21Parser.java +++ b/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21Parser.java @@ -21,10 +21,12 @@ import com.sun.tools.javac.comp.Modules; import com.sun.tools.javac.file.JavacFileManager; import com.sun.tools.javac.main.JavaCompiler; +import com.sun.tools.javac.main.Option; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.Log; import com.sun.tools.javac.util.Options; +import lombok.Getter; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.objectweb.asm.ClassReader; @@ -33,7 +35,6 @@ import org.openrewrite.ExecutionContext; import org.openrewrite.InMemoryExecutionContext; import org.openrewrite.SourceFile; -import org.openrewrite.internal.StringUtils; import org.openrewrite.java.JavaParser; import org.openrewrite.java.JavaParsingException; import org.openrewrite.java.internal.JavaTypeCache; @@ -43,20 +44,27 @@ import org.openrewrite.tree.ParseError; import org.openrewrite.tree.ParsingEventListener; import org.openrewrite.tree.ParsingExecutionContextView; +import org.slf4j.LoggerFactory; +import javax.annotation.processing.Processor; import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; import javax.tools.SimpleJavaFileObject; import javax.tools.StandardLocation; import java.io.*; +import java.lang.reflect.Constructor; import java.net.URI; +import java.net.URL; import java.nio.charset.Charset; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.*; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Stream; import java.util.stream.StreamSupport; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; import static java.util.stream.Collectors.toList; /** @@ -77,6 +85,7 @@ public class ReloadableJava21Parser implements JavaParser { private final JavaCompiler compiler; private final ResettableLog compilerLog; private final Collection styles; + private final List annotationProcessors; private ReloadableJava21Parser(boolean logCompilationWarningsAndErrors, @Nullable Collection classpath, @@ -106,6 +115,70 @@ private ReloadableJava21Parser(boolean logCompilationWarningsAndErrors, Options.instance(context).put("-g", "-g"); Options.instance(context).put("-proc", "none"); + // Ensure type attribution continues despite errors in individual files or nodes. + // If an error occurs in a single file or node, type attribution should still proceed + // for all other source files and unaffected nodes within the same file. + Options.instance(context).put("should-stop.ifError", "GENERATE"); + + LOMBOK: + if (System.getenv().getOrDefault("REWRITE_LOMBOK", System.getProperty("rewrite.lombok")) != null && + classpath != null && classpath.stream().anyMatch(it -> it.toString().contains("lombok"))) { + Processor lombokProcessor = null; + try { + // https://projectlombok.org/contributing/lombok-execution-path + List overrideClasspath = new ArrayList<>(); + for (Path part : classpath) { + if (part.toString().contains("lombok")) { + overrideClasspath.add(part.toString()); + } + } + // make sure the rewrite-java-lombok dependency comes first + boolean found = false; + for (int i = 0; i < overrideClasspath.size(); i++) { + if (overrideClasspath.get(i).contains("rewrite-java-lombok")) { + overrideClasspath.add(0, overrideClasspath.remove(i)); + found = true; + } + } + if (!found) { + // try to find `rewrite-java-lombok` using class loader + URL resource = getClass().getClassLoader().getResource("org/openrewrite/java/lombok/OpenRewriteConfigurationKeysLoader.class"); + if (resource != null && resource.getProtocol().equals("jar") && resource.getPath().startsWith("file:")) { + String path = Paths.get(URI.create(resource.getPath().substring(0, resource.getPath().indexOf("!")))).toString(); + overrideClasspath.add(0, path); + } else { + break LOMBOK; + } + } + System.setProperty("shadow.override.lombok", String.join(File.pathSeparator, overrideClasspath)); + + Class shadowLoaderClass = Class.forName("lombok.launch.ShadowClassLoader", true, getClass().getClassLoader()); + Constructor shadowLoaderConstructor = shadowLoaderClass.getDeclaredConstructor( + Class.forName("java.lang.ClassLoader"), + Class.forName("java.lang.String"), + Class.forName("java.lang.String"), + Class.forName("java.util.List"), + Class.forName("java.util.List")); + shadowLoaderConstructor.setAccessible(true); + + ClassLoader lombokShadowLoader = (ClassLoader) shadowLoaderConstructor.newInstance( + getClass().getClassLoader(), + "lombok", + null, + emptyList(), + singletonList("lombok.patcher.Symbols") + ); + lombokProcessor = (Processor) lombokShadowLoader.loadClass("lombok.core.AnnotationProcessor").getDeclaredConstructor().newInstance(); + Options.instance(context).put(Option.PROCESSOR, "lombok.launch.AnnotationProcessorHider$AnnotationProcessor"); + } catch (ReflectiveOperationException ignore) { + // Lombok was not found or could not be initialized + } finally { + annotationProcessors = lombokProcessor != null ? singletonList(lombokProcessor) : emptyList(); + } + } else { + annotationProcessors = emptyList(); + } + // MUST be created (registered with the context) after pfm and compilerLog compiler = new JavaCompiler(context); @@ -124,7 +197,7 @@ public void write(char[] cbuf, int off, int len) { if (logCompilationWarningsAndErrors) { String log = new String(Arrays.copyOfRange(cbuf, off, len)); if (!log.isBlank()) { - org.slf4j.LoggerFactory.getLogger(ReloadableJava21Parser.class).warn(log); + LoggerFactory.getLogger(ReloadableJava21Parser.class).warn(log); } } } @@ -164,6 +237,7 @@ public Stream parseInputs(Iterable sourceFiles, @Nullable Pat ); J.CompilationUnit cu = (J.CompilationUnit) parser.scan(cuByPath.getValue(), Space.EMPTY); + //noinspection DataFlowIssue cuByPath.setValue(null); // allow memory used by this JCCompilationUnit to be released parsingListener.parsed(input, cu); return requirePrintEqualsInput(cu, input, relativeTo, ctx); @@ -188,39 +262,46 @@ LinkedHashMap parseInputsToCompilerAst(Iterable } LinkedHashMap cus = new LinkedHashMap<>(); - acceptedInputs(sourceFiles).forEach(input1 -> { + List inputFileObjects = acceptedInputs(sourceFiles) + .map(input -> new ReloadableJava21ParserInputFileObject(input, ctx)) + .toList(); + if (!annotationProcessors.isEmpty()) { + compiler.initProcessAnnotations(annotationProcessors, inputFileObjects, emptyList()); + } + try { + //noinspection unchecked + com.sun.tools.javac.util.List jcCompilationUnits = compiler.parseFiles((List) (List) inputFileObjects, true); + for (int i = 0; i < inputFileObjects.size(); i++) { + cus.put(inputFileObjects.get(i).getInput(), jcCompilationUnits.get(i)); + } try { - JCTree.JCCompilationUnit jcCompilationUnit = compiler.parse(new ReloadableJava21ParserInputFileObject(input1, ctx)); - cus.put(input1, jcCompilationUnit); - } catch (IllegalStateException e) { - if ("endPosTable already set".equals(e.getMessage())) { - throw new IllegalStateException( - "Call reset() on JavaParser before parsing another set of source files that " + - "have some of the same fully qualified names. Source file [" + - input1.getPath() + "]\n[\n" + StringUtils.readFully(input1.getSource(ctx), getCharset(ctx)) + "\n]", e); + initModules(cus.values()); + enterAll(cus.values()); + + // For some reason this is necessary in JDK 9+, where the internal block counter that + // annotationsBlocked() tests against remains >0 after attribution. + Annotate annotate = Annotate.instance(context); + while (annotate.annotationsBlocked()) { + annotate.unblockAnnotations(); // also flushes once unblocked } - throw e; + if (!annotationProcessors.isEmpty()) { + compiler.processAnnotations(jcCompilationUnits, emptyList()); + } + compiler.attribute(compiler.todo); + } catch (Throwable t) { + // when symbol entering fails on problems like missing types, attribution can often times proceed + // unhindered, but it sometimes cannot (so attribution is always best-effort in the presence of errors) + ctx.getOnError().accept(new JavaParsingException("Failed symbol entering or attribution", t)); } - }); - - try { - initModules(cus.values()); - enterAll(cus.values()); - - // For some reason this is necessary in JDK 9+, where the internal block counter that - // annotationsBlocked() tests against remains >0 after attribution. - Annotate annotate = Annotate.instance(context); - while (annotate.annotationsBlocked()) { - annotate.unblockAnnotations(); // also flushes once unblocked + } catch (IllegalStateException e) { + if ("endPosTable already set".equals(e.getMessage())) { + throw new IllegalStateException( + "Call reset() on JavaParser before parsing another set of source files that " + + "have some of the same fully qualified names.", e); } - - compiler.attribute(compiler.todo); - } catch ( - Throwable t) { - // when symbol entering fails on problems like missing types, attribution can often times proceed - // unhindered, but it sometimes cannot (so attribution is always best-effort in the presence of errors) - ctx.getOnError().accept(new JavaParsingException("Failed symbol entering or attribution", t)); + throw e; } + return cus; } @@ -343,6 +424,7 @@ public Iterable list(Location location, String packageName, Set< private static class PackageAwareJavaFileObject extends SimpleJavaFileObject { private final String pkg; + @Getter private final String className; private final byte[] classBytes; @@ -376,10 +458,6 @@ public String getPackage() { return pkg; } - public String getClassName() { - return className; - } - @Override public InputStream openInputStream() { return new ByteArrayInputStream(classBytes); diff --git a/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21ParserVisitor.java b/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21ParserVisitor.java index 0eeba8521e6..ac804d1d15c 100644 --- a/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21ParserVisitor.java +++ b/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21ParserVisitor.java @@ -19,6 +19,7 @@ import com.sun.source.doctree.DocCommentTree; import com.sun.source.tree.*; import com.sun.source.util.TreePathScanner; +import com.sun.tools.javac.code.Attribute; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.tree.DocCommentTable; @@ -437,7 +438,7 @@ public J visitClass(ClassTree node, Space fmt) { } JLeftPadded extendings = node.getExtendsClause() == null ? null : - padLeft(sourceBefore("extends"), convertOrNull(node.getExtendsClause())); + padLeft(sourceBefore("extends"), convert(node.getExtendsClause())); JContainer implementings = null; if (node.getImplementsClause() != null && !node.getImplementsClause().isEmpty()) { @@ -700,7 +701,7 @@ public J visitForLoop(ForLoopTree node, Space fmt) { commaDelim.apply(t) ); - JRightPadded condition = convertOrNull(node.getCondition(), semiDelim); + JRightPadded condition = convert(node.getCondition(), semiDelim); if (condition == null) { condition = padRight(new J.Empty(randomId(), sourceBefore(";"), Markers.EMPTY), EMPTY); } @@ -970,7 +971,7 @@ public J visitMethod(MethodTree node, Space fmt) { } List returnTypeAnnotations = collectAnnotations(annotationPosTable); - TypeTree returnType = convertOrNull(node.getReturnType()); + TypeTree returnType = convert(node.getReturnType()); if (returnType != null && !returnTypeAnnotations.isEmpty()) { returnType = new J.AnnotatedType(randomId(), Space.EMPTY, Markers.EMPTY, returnTypeAnnotations, returnType); @@ -1017,7 +1018,7 @@ public J visitMethod(MethodTree node, Space fmt) { JContainer.build(sourceBefore("throws"), convertAll(node.getThrows(), commaDelim, noDelim), Markers.EMPTY); - J.Block body = convertOrNull(node.getBody()); + J.Block body = convert(node.getBody()); JLeftPadded defaultValue = node.getDefaultValue() == null ? null : padLeft(sourceBefore("default"), convert(node.getDefaultValue())); @@ -1043,9 +1044,9 @@ public J visitNewArray(NewArrayTree node, Space fmt) { while (elementType instanceof JCArrayTypeTree) { elementType = ((JCArrayTypeTree) elementType).elemtype; } - typeExpr = convertOrNull(elementType); + typeExpr = convert(elementType); } else { - typeExpr = convertOrNull(jcVarType); + typeExpr = convert(jcVarType); } List nodeDimensions = node.getDimensions(); @@ -1095,7 +1096,7 @@ public J visitNewClass(NewClassTree node, Space fmt) { } // for enum definitions with anonymous class initializers, endPos of node identifier will be -1 - TypeTree clazz = endPos(node.getIdentifier()) >= 0 ? convertOrNull(node.getIdentifier()) : null; + TypeTree clazz = endPos(node.getIdentifier()) >= 0 ? convert(node.getIdentifier()) : null; JContainer args; if (positionOfNext("(", '{') > -1) { @@ -1204,7 +1205,7 @@ public J visitPrimitiveType(PrimitiveTypeTree node, Space fmt) { @Override public J visitReturn(ReturnTree node, Space fmt) { skip("return"); - Expression expression = convertOrNull(node.getExpression()); + Expression expression = convert(node.getExpression()); return new J.Return(randomId(), fmt, Markers.EMPTY, expression); } @@ -1569,8 +1570,14 @@ private J.VariableDeclarations visitVariables(List nodes, Space fm // this is a lambda parameter with an inferred type expression typeExpr = null; } else { - typeExpr = new J.Identifier(randomId(), sourceBefore("var"), Markers.EMPTY, emptyList(), "var", typeMapping.type(vartype), null); - typeExpr = typeExpr.withMarkers(typeExpr.getMarkers().add(JavaVarKeyword.build())); + boolean lombokVal = isLombokVal(node); + typeExpr = new J.Identifier(randomId(), + sourceBefore(lombokVal ? "val" : "var"), + Markers.build(singletonList(JavaVarKeyword.build())), + emptyList(), + lombokVal ? "val" : "var", + typeMapping.type(vartype), + null); } } else if (vartype instanceof JCArrayTypeTree) { JCExpression elementType = vartype; @@ -1623,7 +1630,7 @@ private J.VariableDeclarations visitVariables(List nodes, Space fm new J.VariableDeclarations.NamedVariable(randomId(), namedVarPrefix, Markers.EMPTY, name, dimensionsAfterName, - n.init != null ? padLeft(sourceBefore("="), convertOrNull(n.init)) : null, + n.init != null ? padLeft(sourceBefore("="), convert(n.init)) : null, (JavaType.Variable) typeMapping.type(n) ), i == nodes.size() - 1 ? EMPTY : sourceBefore(",") @@ -1680,7 +1687,7 @@ public J visitWildcard(WildcardTree node, Space fmt) { bound = null; } - return new J.Wildcard(randomId(), fmt, Markers.EMPTY, bound, convertOrNull(wildcard.inner)); + return new J.Wildcard(randomId(), fmt, Markers.EMPTY, bound, convert(wildcard.inner)); } /** @@ -1689,9 +1696,12 @@ public J visitWildcard(WildcardTree node, Space fmt) { * -------------- */ - private J2 convert(Tree t) { + private @Nullable J2 convert(@Nullable Tree t) { + if (t == null) { + return null; + } try { - String prefix = source.substring(cursor, max(((JCTree) t).getStartPosition(), cursor)); + String prefix = source.substring(cursor, Math.max(cursor, getActualStartPosition((JCTree) t))); 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); @@ -1707,6 +1717,14 @@ private J2 convert(Tree t) { } } + private static int getActualStartPosition(JCTree t) { + // not sure if this is a bug in Lombok, but the variable's start position is after the `val` annotation + if (t instanceof JCVariableDecl && isLombokVal((JCVariableDecl) t)) { + return ((JCVariableDecl) t).mods.annotations.get(0).getStartPosition(); + } + return t.getStartPosition(); + } + private void reportJavaParsingException(Throwable ex) { // this SHOULD never happen, but is here simply as a diagnostic measure in the event of unexpected exceptions StringBuilder message = new StringBuilder("Failed to convert for the following cursor stack:"); @@ -1731,7 +1749,10 @@ private void reportJavaParsingException(Throwable ex) { ctx.getOnError().accept(new JavaParsingException(message.toString(), ex)); } - private JRightPadded convert(Tree t, Function suffix) { + private @Nullable JRightPadded convert(@Nullable Tree t, Function suffix) { + if (t == null) { + return null; + } J2 j = convert(t); @SuppressWarnings("ConstantConditions") JRightPadded rightPadded = j == null ? null : new JRightPadded<>(j, suffix.apply(t), Markers.EMPTY); @@ -1776,14 +1797,6 @@ private long lineNumber(Tree tree) { return source.substring(0, ((JCTree) tree).getStartPosition()).chars().filter(c -> c == '\n').count() + 1; } - private @Nullable T convertOrNull(@Nullable Tree t) { - return t == null ? null : convert(t); - } - - private @Nullable JRightPadded convertOrNull(@Nullable Tree t, Function suffix) { - return t == null ? null : convert(t, suffix); - } - private List convertAll(List trees) { List converted = new ArrayList<>(trees.size()); for (Tree tree : trees) { @@ -1888,6 +1901,9 @@ private List> convertStatements(@Nullable List> treesGroupedByStartPosition = new LinkedHashMap<>(); for (Tree t : trees) { + if (isLombokGenerated(t)) { + continue; + } treesGroupedByStartPosition.computeIfAbsent(((JCTree) t).getStartPosition(), k -> new ArrayList<>(1)).add(t); } @@ -1917,6 +1933,53 @@ private List> convertStatements(@Nullable List collectAnnotations(Map annotat boolean inMultilineComment = false; for (int i = cursor; i <= maxAnnotationPosition && i < source.length(); i++) { if (annotationPosTable.containsKey(i)) { - annotations.add(convert(annotationPosTable.get(i))); + JCAnnotation jcAnnotation = annotationPosTable.get(i); + if (isLombokGenerated(jcAnnotation)) { + continue; + } + annotations.add(convert(jcAnnotation)); i = cursor; continue; } diff --git a/rewrite-java-tck/src/main/java/org/openrewrite/java/tree/LombokTest.java b/rewrite-java-tck/src/main/java/org/openrewrite/java/tree/LombokTest.java index 3a7f7850365..6a355210012 100644 --- a/rewrite-java-tck/src/main/java/org/openrewrite/java/tree/LombokTest.java +++ b/rewrite-java-tck/src/main/java/org/openrewrite/java/tree/LombokTest.java @@ -34,7 +34,7 @@ import static org.openrewrite.java.Assertions.java; @SuppressWarnings({"CaughtExceptionImmediatelyRethrown", "LombokGetterMayBeUsed", "LombokSetterMayBeUsed", "DefaultAnnotationParam", "NotNullFieldNotInitialized", "ProtectedMemberInFinalClass", "WriteOnlyObject", "ConcatenationWithEmptyString"}) -@EnabledOnJre({JRE.JAVA_11, JRE.JAVA_17}) +@EnabledOnJre({JRE.JAVA_11, JRE.JAVA_17, JRE.JAVA_21}) class LombokTest implements RewriteTest { @BeforeAll From 4ceb5824c1a2c3591d880de25678b1478c113a37 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 7 Jan 2025 15:20:26 +0100 Subject: [PATCH 2/3] Minimize diff with Java 17 to make it easier to spot intentional changes --- .../java/isolated/ReloadableJava21Parser.java | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21Parser.java b/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21Parser.java index 05b20017fd9..5b3ac22b69e 100644 --- a/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21Parser.java +++ b/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21Parser.java @@ -87,13 +87,14 @@ public class ReloadableJava21Parser implements JavaParser { private final Collection styles; private final List annotationProcessors; - private ReloadableJava21Parser(boolean logCompilationWarningsAndErrors, - @Nullable Collection classpath, - Collection classBytesClasspath, - @Nullable Collection dependsOn, - Charset charset, - Collection styles, - JavaTypeCache typeCache) { + private ReloadableJava21Parser( + boolean logCompilationWarningsAndErrors, + @Nullable Collection classpath, + Collection classBytesClasspath, + @Nullable Collection dependsOn, + Charset charset, + Collection styles, + JavaTypeCache typeCache) { this.classpath = classpath; this.dependsOn = dependsOn; this.styles = styles; @@ -116,13 +117,13 @@ private ReloadableJava21Parser(boolean logCompilationWarningsAndErrors, Options.instance(context).put("-proc", "none"); // Ensure type attribution continues despite errors in individual files or nodes. - // If an error occurs in a single file or node, type attribution should still proceed - // for all other source files and unaffected nodes within the same file. - Options.instance(context).put("should-stop.ifError", "GENERATE"); + // If an error occurs in a single file or node, type attribution should still proceed + // for all other source files and unaffected nodes within the same file. + Options.instance(context).put("should-stop.ifError", "GENERATE"); LOMBOK: if (System.getenv().getOrDefault("REWRITE_LOMBOK", System.getProperty("rewrite.lombok")) != null && - classpath != null && classpath.stream().anyMatch(it -> it.toString().contains("lombok"))) { + classpath != null && classpath.stream().anyMatch(it -> it.toString().contains("lombok"))) { Processor lombokProcessor = null; try { // https://projectlombok.org/contributing/lombok-execution-path @@ -297,7 +298,7 @@ LinkedHashMap parseInputsToCompilerAst(Iterable if ("endPosTable already set".equals(e.getMessage())) { throw new IllegalStateException( "Call reset() on JavaParser before parsing another set of source files that " + - "have some of the same fully qualified names.", e); + "have some of the same fully qualified names.", e); } throw e; } @@ -414,9 +415,9 @@ public Iterable list(Location location, String packageName, Set< Iterable listed = super.list(location, packageName, kinds, recurse); return classByteClasspath.isEmpty() ? listed : Stream.concat(classByteClasspath.stream() - .filter(jfo -> jfo.getPackage().equals(packageName)), - StreamSupport.stream(listed.spliterator(), false) - ).collect(toList()); + .filter(jfo -> jfo.getPackage().equals(packageName)), + StreamSupport.stream(listed.spliterator(), false) + ).collect(toList()); } return super.list(location, packageName, kinds, recurse); } From dd795fe5bc301c66d3253ab7e993392bc6bd5583 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 7 Jan 2025 15:24:58 +0100 Subject: [PATCH 3/3] Minimize diff some more to add missing `var` handling --- .../java/isolated/ReloadableJava17ParserVisitor.java | 1 - .../java/isolated/ReloadableJava21ParserVisitor.java | 7 ++++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/rewrite-java-17/src/main/java/org/openrewrite/java/isolated/ReloadableJava17ParserVisitor.java b/rewrite-java-17/src/main/java/org/openrewrite/java/isolated/ReloadableJava17ParserVisitor.java index 673d84fff4e..9f56bfb0b3c 100644 --- a/rewrite-java-17/src/main/java/org/openrewrite/java/isolated/ReloadableJava17ParserVisitor.java +++ b/rewrite-java-17/src/main/java/org/openrewrite/java/isolated/ReloadableJava17ParserVisitor.java @@ -544,7 +544,6 @@ public J visitCompilationUnit(CompilationUnitTree node, Space fmt) { Map annotationPosTable = mapAnnotations(node.getPackageAnnotations(), new HashMap<>(node.getPackageAnnotations().size())); - List packageAnnotations = collectAnnotations(annotationPosTable); J.Package packageDecl = null; diff --git a/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21ParserVisitor.java b/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21ParserVisitor.java index ac804d1d15c..c1c20fd3ff4 100644 --- a/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21ParserVisitor.java +++ b/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21ParserVisitor.java @@ -392,6 +392,7 @@ public J visitCatch(CatchTree node, Space fmt) { public J visitClass(ClassTree node, Space fmt) { Map annotationPosTable = mapAnnotations(node.getModifiers().getAnnotations(), new HashMap<>(node.getModifiers().getAnnotations().size())); + ReloadableJava21ModifierResults modifierResults = sortedModifiersAndAnnotations(node.getModifiers(), annotationPosTable); List kindAnnotations = collectAnnotations(annotationPosTable); @@ -956,6 +957,7 @@ public J visitMethod(MethodTree node, Space fmt) { Map annotationPosTable = mapAnnotations(node.getModifiers().getAnnotations(), new HashMap<>(node.getModifiers().getAnnotations().size())); + ReloadableJava21ModifierResults modifierResults = sortedModifiersAndAnnotations(node.getModifiers(), annotationPosTable); J.TypeParameters typeParams; @@ -1597,6 +1599,10 @@ private J.VariableDeclarations visitVariables(List nodes, Space fm typeExpr = convert(vartype); } + if (typeExpr == null && node.declaredUsingVar()) { + typeExpr = new J.Identifier(randomId(), sourceBefore("var"), Markers.build(singletonList(JavaVarKeyword.build())), emptyList(), "var", typeMapping.type(vartype), null); + } + if (typeExpr != null && !typeExprAnnotations.isEmpty()) { Space prefix = typeExprAnnotations.get(0).getPrefix(); typeExpr = new J.AnnotatedType(randomId(), prefix, Markers.EMPTY, ListUtils.mapFirst(typeExprAnnotations, a -> a.withPrefix(EMPTY)), typeExpr); @@ -1695,7 +1701,6 @@ public J visitWildcard(WildcardTree node, Space fmt) { * Conversion utilities * -------------- */ - private @Nullable J2 convert(@Nullable Tree t) { if (t == null) { return null;