Skip to content

Commit

Permalink
Add lombok support for java-11 (#4769)
Browse files Browse the repository at this point in the history
* Add lombok support for java-11

* Handle erroneous nodes in open rewrite (#4412)

* Handle erroneous nodes in a tree

* Add visitErroneous to all java parser visitors

* Override the visitVariable to handle erroneous identifier names set by JavacParser

* retain name and suffix for erroneous varDecl

* override the visitVariable to handle error identifiers in all java parser visitors

* Remove sysout

* Update rewrite-java-test/src/test/java/org/openrewrite/java/JavaParserTest.java

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Update rewrite-java-test/src/test/java/org/openrewrite/java/JavaParserTest.java

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* handle errors in method params, variable declarations, fix tests

* Add missing license headers

* fix compilation error

* fix compilation error in Java8ParserVisitor

* Apply code suggestions from bot

* fix cases for statementDelim

* fix block statement template generator to handle adding semicolon

* fix ChangeStaticFieldToMethod recipe

* Record compiler errors from erroneous LST nodes

* Adjustments for comments

* Java 17 parser adjustment alos in 8, 11 and 21

* Add `FindCompileErrorsTest` & move away from deprecated `print()`

---------

Co-authored-by: Jonathan Schnéider <[email protected]>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Tim te Beek <[email protected]>
Co-authored-by: aboyko <[email protected]>

* Make Groovy Parser correctly handle nested parenthesis (#4801)

* WIP

* Format

* Format

* Move grabbing of whitespace and resetting cursor to where it is actually required

* Extra check is not required

* Use toString

* Add `emptyListLiteralWithParentheses` test

* Add `insideFourParenthesesAndEnters` test

* Move list tests all to ListTest

* Add `emptyMapLiteralWithParentheses`

* Review feedback and fix new testcases

* Add `attributeWithParentheses`

* Improve AttributeTest

* Improve AttributeTest

* Improve AttributeTest

* Improve AttributeTest

* Improve AttributeTest

* Review fix new testcases

* Revert edit to testcase

* Add and fix testcase with newline

* Add JavaDoc and move logic regarding whitespace and resetting cursor

---------

Co-authored-by: lingenj <[email protected]>

* suppress javax.json (#4804)

* suppress javax.json

* Update suppressions.xml

* Refactor SpringReference (#4805)

* Separating and clearer naming

* Add license header

* Review feedback

* refactor: Update Gradle wrapper (#4808)

Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.gradle.UpdateGradleWrapper?organizationId=T3BlblJld3JpdGU%3D#defaults=W3sibmFtZSI6ImFkZElmTWlzc2luZyIsInZhbHVlIjoiRmFsc2UifV0=

Co-authored-by: Moderne <[email protected]>

* Add recipe to remove Gradle Enterprise and Develocity (#4809)

* Add recipe to remove Gradle Enterprise and Develocity

* Remove left over java plugin

* Add a UsesType precondition to ReplaceConstant

* Allow file scheme in `RemoteArchive` to simplify testing (#4791)

* Allow file scheme in `RemoteArchive` to simplify testing

While it might look a bit controversial, the file scheme can also point to a remote (for instance a mounted network share) file. By allowing the `file://` scheme we can use `RemoteArchive` for those files.

As a useful side effect, this makes testing RemoteArchive handling a lot easier.

* fix test

* Update rewrite-core/src/test/java/org/openrewrite/remote/RemoteArchiveTest.java

Co-authored-by: Sam Snyder <[email protected]>

---------

Co-authored-by: Sam Snyder <[email protected]>

* Try alternative way of determining parenthesis level for `BinaryExpression` when AST doesn't provide `_INSIDE_PARENTHESES_LEVEL` flag (#4807)

* Add a `isClassAvailable` method to the ReflectionUtils (#4810)

* Add a `isClassAvailable` method to the ReflectionUtils

* Add a `isClassAvailable` method to the ReflectionUtils

* Add a `isClassAvailable` method to the ReflectionUtils

* Update rewrite.yml to enforce CompareEnumsWithEqualityOperator

* Correctly map generic return and parameter types in `JavaReflectionTypeMapping` (#4812)

* Polish formatting

* Add more scenarios to JavaTypeGoat for simply typed fields and methods that return exceptions.

* Support mapping of generic thrown exception types (#4813)

* refactor: Enum values should be compared with "==" (#4811)

Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.staticanalysis.CompareEnumsWithEqualityOperator?organizationId=T3BlblJld3JpdGU%3D

Co-authored-by: Moderne <[email protected]>

* Keep the names of generic type variables defined by methods. (#4814)

* Make the same performance improvement to parameter names allocations that we previously made to Java 17/21 in #3345.

* Fix Java reflection mapping of generic typed fields. (#4815)

* Revert parenthesis changes (#4818)

* Revert "Try alternative way of determining parenthesis level for `BinaryExpression` when AST doesn't provide `_INSIDE_PARENTHESES_LEVEL` flag (#4807)"

This reverts commit e59e48b.

* Revert "Make Groovy Parser correctly handle nested parenthesis (#4801)"

This reverts commit 91a031a.

* JavaTemplate bug when inserting `final var` into for-each (#4806)

* JavaTemplate bug when inserting `final var` into for-each

* Split variable declarations when they contain stop comment

* Reduce accidental changes between Java 11 and 17 parsers

* Add missing import

---------

Co-authored-by: Udayani Vaka <[email protected]>
Co-authored-by: Jonathan Schnéider <[email protected]>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Tim te Beek <[email protected]>
Co-authored-by: aboyko <[email protected]>
Co-authored-by: Laurens Westerlaken <[email protected]>
Co-authored-by: lingenj <[email protected]>
Co-authored-by: Peter Streef <[email protected]>
Co-authored-by: Shannon Pamperl <[email protected]>
Co-authored-by: Moderne <[email protected]>
Co-authored-by: Sam Snyder <[email protected]>
  • Loading branch information
12 people authored Dec 31, 2024
1 parent 000a4f0 commit 2a8bf2d
Show file tree
Hide file tree
Showing 5 changed files with 254 additions and 94 deletions.
1 change: 1 addition & 0 deletions rewrite-java-11/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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.+")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

/**
Expand All @@ -77,14 +87,16 @@ public class ReloadableJava11Parser implements JavaParser {
private final JavaCompiler compiler;
private final ResettableLog compilerLog;
private final Collection<NamedStyles> styles;

private ReloadableJava11Parser(boolean logCompilationWarningsAndErrors,
@Nullable Collection<Path> classpath,
Collection<byte[]> classBytesClasspath,
@Nullable Collection<Input> dependsOn,
Charset charset,
Collection<NamedStyles> styles,
JavaTypeCache typeCache) {
private final List<Processor> annotationProcessors;

private ReloadableJava11Parser(
boolean logCompilationWarningsAndErrors,
@Nullable Collection<Path> classpath,
Collection<byte[]> classBytesClasspath,
@Nullable Collection<Input> dependsOn,
Charset charset,
Collection<NamedStyles> styles,
JavaTypeCache typeCache) {
this.classpath = classpath;
this.dependsOn = dependsOn;
this.styles = styles;
Expand All @@ -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<String> 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);

Expand All @@ -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);
}
}
}
Expand Down Expand Up @@ -191,37 +262,44 @@ LinkedHashMap<Input, JCTree.JCCompilationUnit> parseInputsToCompilerAst(Iterable
}

LinkedHashMap<Input, JCTree.JCCompilationUnit> cus = new LinkedHashMap<>();
acceptedInputs(sourceFiles).forEach(input1 -> {
List<ReloadableJava11ParserInputFileObject> 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<JCTree.JCCompilationUnit> jcCompilationUnits = compiler.parseFiles((List<JavaFileObject>) (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;
}
Expand Down Expand Up @@ -361,8 +439,7 @@ public String inferBinaryName(Location location, JavaFileObject file) {
public Iterable<JavaFileObject> list(Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse) throws IOException {
if (StandardLocation.CLASS_PATH.equals(location)) {
Iterable<JavaFileObject> 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());
Expand All @@ -373,6 +450,7 @@ public Iterable<JavaFileObject> 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;

Expand Down Expand Up @@ -406,10 +484,6 @@ public String getPackage() {
return pkg;
}

public String getClassName() {
return className;
}

@Override
public InputStream openInputStream() {
return new ByteArrayInputStream(classBytes);
Expand Down
Loading

0 comments on commit 2a8bf2d

Please sign in to comment.