Skip to content

Commit

Permalink
Merge branch 'main' into validate-written-classes
Browse files Browse the repository at this point in the history
  • Loading branch information
timtebeek authored Feb 10, 2025
2 parents 623cd4e + c50c746 commit a2aae5e
Show file tree
Hide file tree
Showing 11 changed files with 307 additions and 170 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ public enum Delimiter {
PATTERN_SLASHY_STRING("~/", "/"),
PATTERN_DOLLAR_SLASHY_STRING("~$/", "$/"),
SINGLE_LINE_COMMENT("//", "\n"),
MULTILINE_COMMENT("/*", "*/");
MULTILINE_COMMENT("/*", "*/"),
ARRAY("[", "]");

public final String open;
public final String close;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,4 +163,63 @@ class Test {}
)
);
}

@Issue("https://github.com/openrewrite/rewrite/issues/4853")
@Test
void annotationOnVariable() {
rewriteRun(
groovy(
"""
@Foo def a = "a"
"""
)
);
}

@Test
void groovyTransformFieldAnnotationOnVariable() {
rewriteRun(
groovy(
"""
import groovy.transform.Field
@Field def a = [1, 2, 3]
"""
)
);
}

@Test
void groovyTransformFieldFQNAnnotationOnVariable() {
rewriteRun(
groovy(
"""
@groovy.transform.Field def a = [1, 2, 3]
"""
)
);
}

@Test
void groovyTransformFieldFQNAnnotationOnVariableWithReference() {
rewriteRun(
groovy(
"""
def z = 1 + 2
@groovy.transform.Field def a = z
"""
)
);
}

@Test
void groovyTransformFieldFQNAnnotationOnVariableWithMethodInvocation() {
rewriteRun(
groovy(
"""
@groovy.transform.Field def a = callSomething()
"""
)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -141,17 +141,17 @@ public J visitAnnotation(AnnotationTree node, Space fmt) {

args = JContainer.build(argsPrefix, expressions, Markers.EMPTY);
} else {
String remaining = source.substring(cursor, endPos(node));

// TODO: technically, if there is code like this, we have a bug, but seems exceedingly unlikely:
// @MyAnnotation /* Comment () that contains parentheses */ ()

if (remaining.contains("(") && remaining.contains(")")) {
int saveCursor = cursor;
Space prefix = whitespace();
if (source.charAt(cursor) == '(') {
skip("(");
args = JContainer.build(
sourceBefore("("),
prefix,
singletonList(padRight(new J.Empty(randomId(), sourceBefore(")"), Markers.EMPTY), EMPTY)),
Markers.EMPTY
);
} else {
cursor = saveCursor;
}
}

Expand Down Expand Up @@ -1655,7 +1655,9 @@ public J visitWildcard(WildcardTree node, Space fmt) {
return null;
}
try {
String prefix = source.substring(cursor, max(cursor, getActualStartPosition((JCTree) t)));
// The spacing of initialized enums such as `ONE (1)` is handled in the `visitNewClass` method, so set it explicitly to “” here.
String prefix = t instanceof JCNewClass && hasFlag(((JCNewClass) t).type.tsym.flags(), Flags.ENUM) ? ""
: source.substring(cursor, indexOfNextNonWhitespace(cursor, source));
cursor += prefix.length();
@SuppressWarnings("unchecked") J2 j = (J2) scan(t, formatWithCommentTree(prefix, (JCTree) t, docCommentTable.getCommentTree((JCTree) t)));
return j;
Expand Down Expand Up @@ -1685,14 +1687,6 @@ 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 <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 @@ -2043,7 +2037,11 @@ private void cursor(int n) {
}

private boolean hasFlag(ModifiersTree modifiers, long flag) {
return (((JCModifiers) modifiers).flags & flag) != 0L;
return hasFlag(((JCModifiers) modifiers).flags, flag);
}

private boolean hasFlag(long flags, long flag) {
return (flags & flag) != 0L;
}

@SuppressWarnings("unused")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,17 +148,17 @@ public J visitAnnotation(AnnotationTree node, Space fmt) {

args = JContainer.build(argsPrefix, expressions, Markers.EMPTY);
} else {
String remaining = source.substring(cursor, endPos(node));

// TODO: technically, if there is code like this, we have a bug, but seems exceedingly unlikely:
// @MyAnnotation /* Comment () that contains parentheses */ ()

if (remaining.contains("(") && remaining.contains(")")) {
int saveCursor = cursor;
Space prefix = whitespace();
if (source.charAt(cursor) == '(') {
skip("(");
args = JContainer.build(
sourceBefore("("),
prefix,
singletonList(padRight(new J.Empty(randomId(), sourceBefore(")"), Markers.EMPTY), EMPTY)),
Markers.EMPTY
);
} else {
cursor = saveCursor;
}
}

Expand Down Expand Up @@ -1738,30 +1738,19 @@ public J visitWildcard(WildcardTree node, Space fmt) {
return null;
}
try {
String prefix = source.substring(cursor, Math.max(cursor, getActualStartPosition((JCTree) t)));
// The spacing of initialized enums such as `ONE (1)` is handled in the `visitNewClass` method, so set it explicitly to “” here.
String prefix = t instanceof JCNewClass && hasFlag(((JCNewClass) t).type.tsym.flags(), Flags.ENUM) ? ""
: 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));
Space p = formatWithCommentTree(prefix, (JCTree) t, docCommentTable.getCommentTree((JCTree) t));
@SuppressWarnings("unchecked") J2 j = (J2) scan(t, p);
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;
}
}

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 Down Expand Up @@ -2129,7 +2118,11 @@ private void cursor(int n) {
}

private boolean hasFlag(ModifiersTree modifiers, long flag) {
return (((JCModifiers) modifiers).flags & flag) != 0L;
return hasFlag(((JCModifiers) modifiers).flags, flag);
}

private boolean hasFlag(long flags, long flag) {
return (flags & flag) != 0L;
}

@SuppressWarnings("unused")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,17 +148,17 @@ public J visitAnnotation(AnnotationTree node, Space fmt) {

args = JContainer.build(argsPrefix, expressions, Markers.EMPTY);
} else {
String remaining = source.substring(cursor, endPos(node));

// TODO: technically, if there is code like this, we have a bug, but seems exceedingly unlikely:
// @MyAnnotation /* Comment () that contains parentheses */ ()

if (remaining.contains("(") && remaining.contains(")")) {
int saveCursor = cursor;
Space prefix = whitespace();
if (source.charAt(cursor) == '(') {
skip("(");
args = JContainer.build(
sourceBefore("("),
prefix,
singletonList(padRight(new J.Empty(randomId(), sourceBefore(")"), Markers.EMPTY), EMPTY)),
Markers.EMPTY
);
} else {
cursor = saveCursor;
}
}

Expand Down Expand Up @@ -1766,7 +1766,9 @@ public J visitWildcard(WildcardTree node, Space fmt) {
return null;
}
try {
String prefix = source.substring(cursor, Math.max(cursor, getActualStartPosition((JCTree) t)));
// The spacing of initialized enums such as `ONE (1)` is handled in the `visitNewClass` method, so set it explicitly to “” here.
String prefix = t instanceof JCNewClass && hasFlag(((JCNewClass) t).type.tsym.flags(), Flags.ENUM) ? ""
: 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);
Expand All @@ -1782,14 +1784,6 @@ 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 Down Expand Up @@ -2157,7 +2151,11 @@ private void cursor(int n) {
}

private boolean hasFlag(ModifiersTree modifiers, long flag) {
return (((JCModifiers) modifiers).flags & flag) != 0L;
return hasFlag(((JCModifiers) modifiers).flags, flag);
}

private boolean hasFlag(long flags, long flag) {
return (flags & flag) != 0L;
}

@SuppressWarnings("unused")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,17 +138,17 @@ public J visitAnnotation(AnnotationTree node, Space fmt) {

args = JContainer.build(argsPrefix, expressions, Markers.EMPTY);
} else {
String remaining = source.substring(cursor, endPos(node));

// TODO: technically, if there is code like this, we have a bug, but seems exceedingly unlikely:
// @MyAnnotation /* Comment () that contains parentheses */ ()

if (remaining.contains("(") && remaining.contains(")")) {
int saveCursor = cursor;
Space prefix = whitespace();
if (source.charAt(cursor) == '(') {
skip("(");
args = JContainer.build(
sourceBefore("("),
prefix,
singletonList(padRight(new J.Empty(randomId(), sourceBefore(")"), Markers.EMPTY), EMPTY)),
Markers.EMPTY
);
} else {
cursor = saveCursor;
}
}

Expand Down Expand Up @@ -1639,7 +1639,9 @@ public J visitWildcard(WildcardTree node, Space fmt) {
return null;
}
try {
String prefix = source.substring(cursor, Math.max(cursor, getActualStartPosition((JCTree) t)));
// The spacing of initialized enums such as `ONE (1)` is handled in the `visitNewClass` method, so set it explicitly to “” here.
String prefix = t instanceof JCNewClass && hasFlag(((JCNewClass) t).type.tsym.flags(), Flags.ENUM) ? ""
: source.substring(cursor, indexOfNextNonWhitespace(cursor, source));
cursor += prefix.length();
@SuppressWarnings("unchecked") J2 j = (J2) scan(t, formatWithCommentTree(prefix, (JCTree) t, docCommentTable.getCommentTree((JCTree) t)));
return j;
Expand Down Expand Up @@ -1669,14 +1671,6 @@ 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 && isLombokGenerated(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) {
if (t == null) {
return null;
Expand Down Expand Up @@ -2039,7 +2033,11 @@ private void cursor(int n) {
}

private boolean hasFlag(ModifiersTree modifiers, long flag) {
return (((JCModifiers) modifiers).flags & flag) != 0L;
return hasFlag(((JCModifiers) modifiers).flags, flag);
}

private boolean hasFlag(long flags, long flag) {
return (flags & flag) != 0L;
}

@SuppressWarnings("unused")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.MinimumJava11;
import org.openrewrite.java.MinimumJava17;
Expand Down Expand Up @@ -913,6 +915,38 @@ public void test() {
);
}

@ParameterizedTest
@ValueSource(strings = {
"AllArgsConstructor",
"Builder",
"Data",
"EqualsAndHashCode",
"NoArgsConstructor",
"RequiredArgsConstructor",
"ToString",
"Value",
"With"
})
@MinimumJava11
void npeSeenOnMultipleAnnotations(String annotation) {
rewriteRun(
spec -> spec.parser(JavaParser.fromJavaVersion().classpath(JavaParser.runtimeClasspath())),
java(
//language=java
String.format("""
import lombok.%s;
import org.jspecify.annotations.Nullable;
@%1$s
public class Foo {
@Nullable
String bar;
}
""", annotation)
)
);
}

/**
* These test lombok features that we do not fully support.
* Code should still parse and print back to its original source code but type information may be missing.
Expand Down
Loading

0 comments on commit a2aae5e

Please sign in to comment.