Skip to content

Commit

Permalink
Partial support Lombok for java 8 (mostly done) (#4855)
Browse files Browse the repository at this point in the history
* Support Lombok for java 8

* Support Lombok for java 8

* Support Lombok for java 8

* Add rewrite-java-lombok to classpath

* Improve message

* Remove `TimedTodo` to be more like the other java parsers

* cleanup

* Add JavaCompiler `delegate` fix

* Support Lomboks `var` and `val` for Java 8

* Cleanup

* Cleanup

* Cleanup of `isLombokGenerated` methods

* Cleanup of `isLombokGenerated` methods

* Cleanup of `isLombokGenerated` methods

* Cleanup of `isLombokGenerated` methods

* Cleanup of `isLombokGenerated` methods

* Cleanup

* Improve `onConstructor` and `onConstructorNoArgs` args

* Fix missing `onConstructor_` check

* Cleanup

* Apply suggestions from code review

Co-authored-by: Knut Wannheden <[email protected]>

* Improve tests

* Merge branch 'main' into lombok-java-8

* Rename JavacAnnotationHandler with no action to XNoOpHandler

* Rename JavacAnnotationHandler with no action to XNoOpHandler

* Improve lomboks `ExampleException` test

---------

Co-authored-by: Knut Wannheden <[email protected]>
  • Loading branch information
2 people authored and sambsnyd committed Jan 15, 2025
1 parent f2308e6 commit 0f496b9
Show file tree
Hide file tree
Showing 15 changed files with 491 additions and 301 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

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;
Expand All @@ -27,6 +26,7 @@
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.*;
import com.sun.tools.javac.util.Context;
import lombok.Generated;
import org.jspecify.annotations.Nullable;
import org.openrewrite.Cursor;
import org.openrewrite.ExecutionContext;
Expand Down Expand Up @@ -1519,9 +1519,11 @@ private J.VariableDeclarations visitVariables(List<VariableTree> nodes, Space fm
// this is a lambda parameter with an inferred type expression
typeExpr = null;
} else {
boolean lombokVal = isLombokVal(node);
Space space = whitespace();
boolean lombokVal = source.startsWith("val", cursor);
cursor += 3; // skip `val` or `var`
typeExpr = new J.Identifier(randomId(),
sourceBefore(lombokVal ? "val" : "var"),
space,
Markers.build(singletonList(JavaVarKeyword.build())),
emptyList(),
lombokVal ? "val" : "var",
Expand Down Expand Up @@ -1684,8 +1686,8 @@ public J visitWildcard(WildcardTree node, Space fmt) {
}

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)) {
// The variable's start position in the source is wrongly after lombok's `@val` annotation
if (t instanceof JCVariableDecl && isLombokGenerated(t)) {
return ((JCVariableDecl) t).mods.annotations.get(0).getStartPosition();
}
return t.getStartPosition();
Expand Down Expand Up @@ -1861,7 +1863,7 @@ private List<JRightPadded<Statement>> convertStatements(@Nullable List<? extends

Map<Integer, List<Tree>> treesGroupedByStartPosition = new LinkedHashMap<>();
for (Tree t : trees) {
if (isLombokGenerated(t)) {
if (!(t instanceof JCVariableDecl) && isLombokGenerated(t)) {
continue;
}
treesGroupedByStartPosition.computeIfAbsent(((JCTree) t).getStartPosition(), k -> new ArrayList<>(1)).add(t);
Expand Down Expand Up @@ -1893,51 +1895,23 @@ private List<JRightPadded<Statement>> convertStatements(@Nullable List<? extends
return converted;
}

private static boolean isLombokVal(JCTree.JCVariableDecl t) {
if (t.sym != null && t.sym.getMetadata() != null) {
for (Attribute.Compound a : t.sym.getDeclarationAttributes()) {
if ("lombok.val".equals(a.type.toString())) {
return true;
}
}
}
return false;
}

private static boolean isLombokGenerated(Tree t) {
Tree tree = (t instanceof JCAnnotation) ? ((JCAnnotation) t).getAnnotationType() : t;

Symbol sym = null;
if (t instanceof JCAnnotation) {
t = ((JCAnnotation) t).getAnnotationType();
}
if (t instanceof JCIdent) {
sym = ((JCIdent) t).sym;
} else if (t instanceof JCTree.JCMethodDecl) {
sym = ((JCMethodDecl) t).sym;
} else if (t instanceof JCTree.JCClassDecl) {
sym = ((JCClassDecl) t).sym;
} else if (t instanceof JCTree.JCVariableDecl) {
sym = ((JCVariableDecl) t).sym;
if (tree instanceof JCIdent) {
sym = ((JCIdent) tree).sym;
} else if (tree instanceof JCTree.JCMethodDecl) {
sym = ((JCMethodDecl) tree).sym;
} else if (tree instanceof JCTree.JCClassDecl) {
sym = ((JCClassDecl) tree).sym;
} else if (tree instanceof JCTree.JCVariableDecl) {
sym = ((JCVariableDecl) tree).sym;
return sym != null && sym.getDeclarationAttributes().stream().anyMatch(a -> "lombok.val".equals(a.type.toString()));
}
return isLombokGenerated(sym);
}

private static boolean isLombokGenerated(@Nullable Symbol sym) {
if (sym == null) {
return false;
}
// Lombok val is represented as a @lombok.val on a "final" modifier, neither which appear in source
if ("lombok.val".equals(sym.getQualifiedName().toString())) {
return true;
}
if (sym.getMetadata() == null) {
return false;
}
for (Attribute.Compound a : sym.getDeclarationAttributes()) {
if ("lombok.Generated".equals(a.type.toString())) {
return true;
}
}
return false;
//noinspection ConstantConditions
return sym != null && ("lombok.val".equals(sym.getQualifiedName().toString()) || sym.getAnnotation(Generated.class) != null);
}

/**
Expand Down Expand Up @@ -2117,7 +2091,6 @@ private ReloadableJava11ModifierResults sortedModifiersAndAnnotations(ModifiersT
for (int i = cursor; i < source.length(); i++) {
if (annotationPosTable.containsKey(i)) {
JCAnnotation jcAnnotation = annotationPosTable.get(i);
// Skip over lombok's "@val" annotation which does not actually appear in source
if (isLombokGenerated(jcAnnotation.getAnnotationType())) {
continue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@
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;
import com.sun.tools.javac.tree.EndPosTable;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.*;
import com.sun.tools.javac.util.Context;
import lombok.Generated;
import org.jspecify.annotations.Nullable;
import org.openrewrite.Cursor;
import org.openrewrite.ExecutionContext;
Expand Down Expand Up @@ -1597,9 +1597,11 @@ private J.VariableDeclarations visitVariables(List<VariableTree> nodes, Space fm
// this is a lambda parameter with an inferred type expression
typeExpr = null;
} else {
boolean lombokVal = isLombokVal(node);
Space space = whitespace();
boolean lombokVal = source.startsWith("val", cursor);
cursor += 3; // skip `val` or `var`
typeExpr = new J.Identifier(randomId(),
sourceBefore(lombokVal ? "val" : "var"),
space,
Markers.build(singletonList(JavaVarKeyword.build())),
emptyList(),
lombokVal ? "val" : "var",
Expand Down Expand Up @@ -1747,6 +1749,14 @@ public J visitWildcard(WildcardTree node, Space fmt) {
}
}

private static int getActualStartPosition(JCTree t) {
// The variable's start position in the source is wrongly after lombok's `@val` annotation
if (t instanceof JCVariableDecl && isLombokGenerated(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:");
Expand All @@ -1771,14 +1781,6 @@ private void reportJavaParsingException(Throwable ex) {
ctx.getOnError().accept(new JavaParsingException(message.toString(), ex));
}

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 <J2 extends @Nullable J> @Nullable JRightPadded<J2> convert(@Nullable Tree t, Function<Tree, Space> suffix) {
return convert(t, suffix, j -> Markers.EMPTY);
}
Expand Down Expand Up @@ -1942,7 +1944,7 @@ private List<JRightPadded<Statement>> convertStatements(@Nullable List<? extends

Map<Integer, List<Tree>> treesGroupedByStartPosition = new LinkedHashMap<>();
for (Tree t : trees) {
if (isLombokGenerated(t)) {
if (!(t instanceof JCVariableDecl) && isLombokGenerated(t)) {
continue;
}
treesGroupedByStartPosition.computeIfAbsent(((JCTree) t).getStartPosition(), k -> new ArrayList<>(1)).add(t);
Expand Down Expand Up @@ -1974,51 +1976,23 @@ private List<JRightPadded<Statement>> convertStatements(@Nullable List<? extends
return converted;
}

private static boolean isLombokVal(JCTree.JCVariableDecl t) {
if (t.sym != null && t.sym.getMetadata() != null) {
for (Attribute.Compound a : t.sym.getDeclarationAttributes()) {
if ("lombok.val".equals(a.type.toString())) {
return true;
}
}
}
return false;
}

private static boolean isLombokGenerated(Tree t) {
Tree tree = (t instanceof JCAnnotation) ? ((JCAnnotation) t).getAnnotationType() : t;

Symbol sym = null;
if (t instanceof JCAnnotation) {
t = ((JCAnnotation) t).getAnnotationType();
}
if (t instanceof JCIdent) {
sym = ((JCIdent) t).sym;
} else if (t instanceof JCTree.JCMethodDecl) {
sym = ((JCMethodDecl) t).sym;
} else if (t instanceof JCTree.JCClassDecl) {
sym = ((JCClassDecl) t).sym;
} else if (t instanceof JCTree.JCVariableDecl) {
sym = ((JCVariableDecl) t).sym;
if (tree instanceof JCIdent) {
sym = ((JCIdent) tree).sym;
} else if (tree instanceof JCTree.JCMethodDecl) {
sym = ((JCMethodDecl) tree).sym;
} else if (tree instanceof JCTree.JCClassDecl) {
sym = ((JCClassDecl) tree).sym;
} else if (tree instanceof JCTree.JCVariableDecl) {
sym = ((JCVariableDecl) tree).sym;
return sym != null && sym.getDeclarationAttributes().stream().anyMatch(a -> "lombok.val".equals(a.type.toString()));
}
return isLombokGenerated(sym);
}

private static boolean isLombokGenerated(@Nullable Symbol sym) {
if (sym == null) {
return false;
}
// Lombok val is represented as a @lombok.val on a "final" modifier, neither which appear in source
if ("lombok.val".equals(sym.getQualifiedName().toString())) {
return true;
}
if (sym.getMetadata() == null) {
return false;
}
for (Attribute.Compound a : sym.getDeclarationAttributes()) {
if ("lombok.Generated".equals(a.type.toString())) {
return true;
}
}
return false;
//noinspection ConstantConditions
return sym != null && ("lombok.val".equals(sym.getQualifiedName().toString()) || sym.getAnnotation(Generated.class) != null);
}

/**
Expand Down Expand Up @@ -2199,7 +2173,6 @@ private ReloadableJava17ModifierResults sortedModifiersAndAnnotations(ModifiersT
for (int i = cursor; i < source.length(); i++) {
if (annotationPosTable.containsKey(i)) {
JCAnnotation jcAnnotation = annotationPosTable.get(i);
// Skip over lombok's "@val" annotation which does not actually appear in source
if (isLombokGenerated(jcAnnotation.getAnnotationType())) {
continue;
}
Expand Down
Loading

0 comments on commit 0f496b9

Please sign in to comment.