diff --git a/rewrite-java-11/build.gradle.kts b/rewrite-java-11/build.gradle.kts index e897cf9f491..091f2016502 100644 --- a/rewrite-java-11/build.gradle.kts +++ b/rewrite-java-11/build.gradle.kts @@ -9,6 +9,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-11/src/main/java/org/openrewrite/java/isolated/ReloadableJava11Parser.java b/rewrite-java-11/src/main/java/org/openrewrite/java/isolated/ReloadableJava11Parser.java index 75a6e970757..43eeb24d8b8 100644 --- a/rewrite-java-11/src/main/java/org/openrewrite/java/isolated/ReloadableJava11Parser.java +++ b/rewrite-java-11/src/main/java/org/openrewrite/java/isolated/ReloadableJava11Parser.java @@ -18,12 +18,14 @@ import com.sun.tools.javac.comp.*; 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 io.micrometer.core.instrument.Metrics; import io.micrometer.core.instrument.Timer; +import lombok.Getter; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.objectweb.asm.ClassReader; @@ -43,20 +45,28 @@ 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.Collectors; 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,14 +87,16 @@ public class ReloadableJava11Parser implements JavaParser { private final JavaCompiler compiler; private final ResettableLog compilerLog; private final Collection styles; - - private ReloadableJava11Parser(boolean logCompilationWarningsAndErrors, - @Nullable Collection classpath, - Collection classBytesClasspath, - @Nullable Collection dependsOn, - Charset charset, - Collection styles, - JavaTypeCache typeCache) { + private final List annotationProcessors; + + private ReloadableJava11Parser( + 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; @@ -106,6 +118,65 @@ private ReloadableJava11Parser(boolean logCompilationWarningsAndErrors, Options.instance(context).put("-g", "-g"); Options.instance(context).put("-proc", "none"); + 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 ahead of compiler construction new TimedTodo(context); @@ -127,7 +198,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(ReloadableJava11Parser.class).warn(log); + LoggerFactory.getLogger(ReloadableJava11Parser.class).warn(log); } } } @@ -191,37 +262,44 @@ LinkedHashMap parseInputsToCompilerAst(Iterable } LinkedHashMap cus = new LinkedHashMap<>(); - acceptedInputs(sourceFiles).forEach(input1 -> { + List inputFileObjects = acceptedInputs(sourceFiles) + .map(input -> new ReloadableJava11ParserInputFileObject(input, ctx)) + .collect(Collectors.toList()); + if (!annotationProcessors.isEmpty()) { + compiler.initProcessAnnotations(annotationProcessors, inputFileObjects, emptyList()); + } + try { + //noinspection unchecked + com.sun.tools.javac.util.List jcCompilationUnits = compiler.parseFiles((List) (List) inputFileObjects); + for (int i = 0; i < inputFileObjects.size(); i++) { + cus.put(inputFileObjects.get(i).getInput(), jcCompilationUnits.get(i)); + } try { - JCTree.JCCompilationUnit jcCompilationUnit = compiler.parse(new ReloadableJava11ParserInputFileObject(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 + } + if (!annotationProcessors.isEmpty()) { + compiler.processAnnotations(jcCompilationUnits, emptyList()); } - throw 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)); } - }); - - 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 a BEST EFFORT in the presence of errors) - ctx.getOnError().accept(new JavaParsingException("Failed symbol entering or attribution", t)); + throw e; } return cus; } @@ -361,8 +439,7 @@ public String inferBinaryName(Location location, JavaFileObject file) { public Iterable list(Location location, String packageName, Set kinds, boolean recurse) throws IOException { if (StandardLocation.CLASS_PATH.equals(location)) { Iterable listed = super.list(location, packageName, kinds, recurse); - return Stream.concat( - classByteClasspath.stream() + return Stream.concat(classByteClasspath.stream() .filter(jfo -> jfo.getPackage().equals(packageName)), StreamSupport.stream(listed.spliterator(), false) ).collect(toList()); @@ -373,6 +450,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; @@ -406,10 +484,6 @@ public String getPackage() { return pkg; } - public String getClassName() { - return className; - } - @Override public InputStream openInputStream() { return new ByteArrayInputStream(classBytes); diff --git a/rewrite-java-11/src/main/java/org/openrewrite/java/isolated/ReloadableJava11ParserVisitor.java b/rewrite-java-11/src/main/java/org/openrewrite/java/isolated/ReloadableJava11ParserVisitor.java index ac0714e55af..b8dd7a5d14f 100644 --- a/rewrite-java-11/src/main/java/org/openrewrite/java/isolated/ReloadableJava11ParserVisitor.java +++ b/rewrite-java-11/src/main/java/org/openrewrite/java/isolated/ReloadableJava11ParserVisitor.java @@ -18,6 +18,7 @@ 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.DCTree; @@ -340,7 +341,7 @@ public J visitCase(CaseTree node, Space fmt) { node.getExpression() == null ? EMPTY : sourceBefore("case"), singletonList(node.getExpression() == null ? JRightPadded.build(new J.Identifier(randomId(), Space.EMPTY, Markers.EMPTY, emptyList(), skip("default"), null, null)) : - JRightPadded.build(convertOrNull(node.getExpression())) + JRightPadded.build(convert(node.getExpression())) ), Markers.EMPTY ), @@ -391,7 +392,7 @@ public J visitClass(ClassTree node, Space fmt) { Markers.EMPTY); 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()) { @@ -640,7 +641,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); } @@ -909,7 +910,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); @@ -952,7 +953,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())); @@ -976,9 +977,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(); @@ -991,7 +992,7 @@ public J visitNewArray(NewArrayTree node, Space fmt) { convert(dim, t -> sourceBefore("]")))); } - while(true) { + while (true) { int beginBracket = indexOfNextNonWhitespace(cursor, source); if (source.charAt(beginBracket) == '[') { int endBracket = indexOfNextNonWhitespace(beginBracket + 1, source); @@ -1028,7 +1029,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) { @@ -1037,8 +1038,8 @@ public J visitNewClass(NewClassTree node, Space fmt) { singletonList(padRight(new J.Empty(randomId(), sourceBefore(")"), Markers.EMPTY), EMPTY)) : convertAll(node.getArguments(), commaDelim, t -> sourceBefore(")")), Markers.EMPTY); } else { - args = JContainer.empty(); - args = args.withMarkers(args.getMarkers().add(new OmitParentheses(randomId()))); + args = JContainer.empty() + .withMarkers(Markers.build(singletonList(new OmitParentheses(randomId())))); } J.Block body = null; @@ -1136,7 +1137,8 @@ public J visitPrimitiveType(PrimitiveTypeTree node, Space fmt) { @Override public J visitReturn(ReturnTree node, Space fmt) { skip("return"); - return new J.Return(randomId(), fmt, Markers.EMPTY, convertOrNull(node.getExpression())); + Expression expression = convert(node.getExpression()); + return new J.Return(randomId(), fmt, Markers.EMPTY, expression); } @Override @@ -1366,7 +1368,7 @@ private T buildName(String fullyQualifiedName) fullName += "." + part; int endOfPrefix = indexOfNextNonWhitespace(0, part); - Space identFmt = endOfPrefix > 0 ? format(part.substring(0, endOfPrefix)) : EMPTY; + Space identFmt = endOfPrefix > 0 ? format(part, 0, endOfPrefix) : EMPTY; Matcher whitespaceSuffix = whitespaceSuffixPattern.matcher(part); //noinspection ResultOfMethodCallIgnored @@ -1457,7 +1459,7 @@ public J visitVariable(VariableTree node, Space fmt) { int endPos = jcVariableDecl.getEndPosition(endPosTable); if (startPos == endPos) { - endPos = startPos + 1; + endPos = startPos + 1; // For cases where the error node is a single character like "/" } String erroneousNode = source.substring(startPos, endPos); return new J.Erroneous( @@ -1491,8 +1493,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; @@ -1512,6 +1520,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); @@ -1545,7 +1557,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(",") @@ -1558,15 +1570,15 @@ private J.VariableDeclarations visitVariables(List nodes, Space fm private List> arrayDimensions() { List> dims = null; - while(true) { + while (true) { int beginBracket = indexOfNextNonWhitespace(cursor, source); if (source.charAt(beginBracket) == '[') { int endBracket = indexOfNextNonWhitespace(beginBracket + 1, source); - if(dims == null) { + if (dims == null) { dims = new ArrayList<>(2); } - dims.add(padLeft(format(source.substring(cursor, beginBracket)), - format(source.substring(beginBracket + 1, endBracket)))); + dims.add(padLeft(format(source, cursor, beginBracket), + format(source, beginBracket + 1, endBracket))); cursor = endBracket + 1; } else { break; @@ -1602,7 +1614,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)); } /** @@ -1611,9 +1623,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, max(cursor, getActualStartPosition((JCTree) t))); cursor += prefix.length(); @SuppressWarnings("unchecked") J2 j = (J2) scan(t, formatWithCommentTree(prefix, (JCTree) t, docCommentTable.getCommentTree((JCTree) t))); return j; @@ -1643,7 +1658,18 @@ private J2 convert(Tree t) { } } - private JRightPadded convert(Tree t, Function suffix) { + 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 @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); @@ -1688,14 +1714,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) { @@ -1759,7 +1777,7 @@ private Space statementDelim(@Nullable Tree t) { if (t instanceof JCExpressionStatement) { ExpressionTree expTree = ((ExpressionStatementTree) t).getExpression(); if (expTree instanceof ErroneousTree) { - return Space.build(source.substring(((JCTree) expTree).getEndPosition(endPosTable),((JCTree) t).getEndPosition(endPosTable)), Collections.emptyList()); + return Space.build(source.substring(((JCTree) expTree).getEndPosition(endPosTable), ((JCTree) t).getEndPosition(endPosTable)), Collections.emptyList()); } else { return sourceBefore(";"); } @@ -1807,6 +1825,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); } @@ -1836,6 +1857,53 @@ private List> convertStatements(@Nullable List JRightPadded padRight(T tree, Space right) { @@ -1894,7 +1962,7 @@ private int positionOfNext(String untilDelim, @Nullable Character stop) { char c2 = source.charAt(delimIndex + 1); switch (c1) { case '/': - switch(c2) { + switch (c2) { case '/': inSingleLineComment = true; delimIndex++; @@ -1906,7 +1974,7 @@ private int positionOfNext(String untilDelim, @Nullable Character stop) { } break; case '*': - if(c2 == '/') { + if (c2 == '/') { inMultiLineComment = false; delimIndex++; continue; @@ -1938,9 +2006,9 @@ private Space whitespace() { if (nextNonWhitespace == cursor) { return EMPTY; } - String prefix = source.substring(cursor, nextNonWhitespace); - cursor += prefix.length(); - return format(prefix); + Space space = format(source, cursor, nextNonWhitespace); + cursor = nextNonWhitespace; + return space; } private String skip(@Nullable String token) { @@ -2010,13 +2078,18 @@ private ReloadableJava11ModifierResults sortedModifiersAndAnnotations(ModifiersT int lastAnnotationPosition = cursor; for (int i = cursor; i < source.length(); i++) { if (annotationPosTable.containsKey(i)) { - J.Annotation annotation = convert(annotationPosTable.get(i)); + JCAnnotation jcAnnotation = annotationPosTable.get(i); + // Skip over lombok's "@val" annotation which does not actually appear in source + if (isLombokGenerated(jcAnnotation.getAnnotationType())) { + continue; + } + J.Annotation annotation = convert(jcAnnotation); if (afterFirstModifier) { currentAnnotations.add(annotation); } else { leadingAnnotations.add(annotation); } - i = cursor -1; + i = cursor - 1; lastAnnotationPosition = cursor; continue; } @@ -2130,7 +2203,11 @@ private 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-11/src/main/java/org/openrewrite/java/isolated/ReloadableJava11TypeMapping.java b/rewrite-java-11/src/main/java/org/openrewrite/java/isolated/ReloadableJava11TypeMapping.java index a37d73adf6e..0fb15f638be 100644 --- a/rewrite-java-11/src/main/java/org/openrewrite/java/isolated/ReloadableJava11TypeMapping.java +++ b/rewrite-java-11/src/main/java/org/openrewrite/java/isolated/ReloadableJava11TypeMapping.java @@ -232,8 +232,9 @@ private JavaType generic(Type.TypeVar type, String signature) { private JavaType.FullyQualified classType(Type.ClassType classType, String signature) { Symbol.ClassSymbol sym = (Symbol.ClassSymbol) classType.tsym; Type.ClassType symType = (Type.ClassType) sym.type; + String fqn = sym.flatName().toString(); - JavaType.FullyQualified fq = typeCache.get(sym.flatName().toString()); + JavaType.FullyQualified fq = typeCache.get(fqn); JavaType.Class clazz = (JavaType.Class) (fq instanceof JavaType.Parameterized ? ((JavaType.Parameterized) fq).getType() : fq); if (clazz == null) { if (!sym.completer.isTerminal()) { @@ -243,12 +244,12 @@ private JavaType.FullyQualified classType(Type.ClassType classType, String signa clazz = new JavaType.Class( null, sym.flags_field, - sym.flatName().toString(), + fqn, getKind(sym), null, null, null, null, null, null, null ); - typeCache.put(sym.flatName().toString(), clazz); + typeCache.put(fqn, clazz); JavaType.FullyQualified supertype = TypeUtils.asFullyQualified(type(symType.supertype_field)); @@ -276,7 +277,7 @@ private JavaType.FullyQualified classType(Type.ClassType classType, String signa if (elem instanceof Symbol.VarSymbol && (elem.flags_field & (Flags.SYNTHETIC | Flags.BRIDGE | Flags.HYPOTHETICAL | Flags.GENERATEDCONSTR | Flags.ANONCONSTR)) == 0) { - if (sym.flatName().toString().equals("java.lang.String") && elem.name.toString().equals("serialPersistentFields")) { + if (fqn.equals("java.lang.String") && elem.name.toString().equals("serialPersistentFields")) { // there is a "serialPersistentFields" member within the String class which is used in normal Java // serialization to customize how the String field is serialized. This field is tripping up Jackson // serialization and is intentionally filtered to prevent errors. @@ -358,12 +359,19 @@ private JavaType.Class.Kind getKind(Symbol.ClassSymbol sym) { return variableType(((JCTree.JCVariableDecl) tree).sym); } else if (tree instanceof JCTree.JCAnnotatedType && ((JCTree.JCAnnotatedType) tree).getUnderlyingType() instanceof JCTree.JCArrayTypeTree) { return annotatedArray((JCTree.JCAnnotatedType) tree); + } else if (tree instanceof JCTree.JCClassDecl) { + symbol = ((JCTree.JCClassDecl) tree).sym; + } else if (tree instanceof JCTree.JCFieldAccess) { + symbol = ((JCTree.JCFieldAccess) tree).sym; } return type(((JCTree) tree).type, symbol); } - private @Nullable JavaType type(Type type, Symbol symbol) { + private @Nullable JavaType type(@Nullable Type type, @Nullable Symbol symbol) { + if (type == null && symbol != null) { + type = symbol.type; + } if (type instanceof Type.MethodType || type instanceof Type.ForAll) { return methodInvocationType(type, symbol); } 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 e6501675503..03daab50f46 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 @@ -33,7 +33,7 @@ import static org.openrewrite.java.Assertions.java; @SuppressWarnings({"CaughtExceptionImmediatelyRethrown", "LombokGetterMayBeUsed", "LombokSetterMayBeUsed", "DefaultAnnotationParam", "NotNullFieldNotInitialized", "ProtectedMemberInFinalClass", "WriteOnlyObject", "ConcatenationWithEmptyString"}) -@EnabledOnJre(JRE.JAVA_17) +@EnabledOnJre({JRE.JAVA_11, JRE.JAVA_17}) class LombokTest implements RewriteTest { @BeforeAll @@ -426,7 +426,7 @@ class A { Map map; void m() { log.info("string = " + string); - log.info(() -> "map = %s".formatted(map)); + log.info(() -> String.format("map = %s", map)); } } """