Skip to content

Commit 76c4321

Browse files
authored
Fix JavaTemplate expression replacement inside return statements (#7096)
* Fix JavaTemplate expression replacement inside return statements When a method invocation or new class expression is the direct expression of a J.Return (or other non-block expression context), coordinates.replace() produces STATEMENT_PREFIX but parseBlockStatements fails because the template is an expression. Detect expression context by checking if the parent tree is not a J.Block, and use parseExpression instead. * Add test for J.NewClass replacement inside return statements
1 parent 7a7478e commit 76c4321

File tree

2 files changed

+95
-0
lines changed

2 files changed

+95
-0
lines changed

rewrite-java-test/src/test/java/org/openrewrite/java/JavaTemplateTest8Test.java

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.openrewrite.Cursor;
2020
import org.openrewrite.DocumentExample;
2121
import org.openrewrite.ExecutionContext;
22+
import org.openrewrite.java.tree.J;
2223
import org.openrewrite.java.tree.NameTree;
2324
import org.openrewrite.marker.SearchResult;
2425
import org.openrewrite.test.RewriteTest;
@@ -28,6 +29,80 @@
2829

2930
class JavaTemplateTest8Test implements RewriteTest {
3031

32+
@Test
33+
void replaceMethodInvocationInsideReturn() {
34+
rewriteRun(
35+
spec -> spec.recipe(toRecipe(() -> new JavaIsoVisitor<>() {
36+
final JavaTemplate t = JavaTemplate.builder("String.valueOf(#{any(String)})")
37+
.build();
38+
39+
@Override
40+
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
41+
method = super.visitMethodInvocation(method, ctx);
42+
if (method.getSimpleName().equals("toString")) {
43+
return t.apply(getCursor(), method.getCoordinates().replace(),
44+
method.getSelect());
45+
}
46+
return method;
47+
}
48+
})),
49+
java(
50+
"""
51+
class Test {
52+
String test(String s) {
53+
return s.toString();
54+
}
55+
}
56+
""",
57+
"""
58+
class Test {
59+
String test(String s) {
60+
return String.valueOf(s);
61+
}
62+
}
63+
"""
64+
)
65+
);
66+
}
67+
68+
@Test
69+
void replaceNewClassInsideReturn() {
70+
rewriteRun(
71+
spec -> spec.recipe(toRecipe(() -> new JavaIsoVisitor<>() {
72+
final JavaTemplate t = JavaTemplate.builder("new StringBuilder(#{any(String)})")
73+
.build();
74+
75+
@Override
76+
public J.NewClass visitNewClass(J.NewClass newClass, ExecutionContext ctx) {
77+
newClass = super.visitNewClass(newClass, ctx);
78+
if (newClass.getClazz() != null &&
79+
newClass.getClazz().toString().equals("StringBuffer") &&
80+
newClass.getArguments().size() == 1) {
81+
return t.apply(getCursor(), newClass.getCoordinates().replace(),
82+
newClass.getArguments().get(0));
83+
}
84+
return newClass;
85+
}
86+
})),
87+
java(
88+
"""
89+
class Test {
90+
CharSequence test(String s) {
91+
return new StringBuffer(s);
92+
}
93+
}
94+
""",
95+
"""
96+
class Test {
97+
CharSequence test(String s) {
98+
return new StringBuilder(s);
99+
}
100+
}
101+
"""
102+
)
103+
);
104+
}
105+
31106
@DocumentExample
32107
@Test
33108
void parameterizedMatch() {

rewrite-java/src/main/java/org/openrewrite/java/internal/template/JavaTemplateJavaExtension.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,12 +435,32 @@ public J visitMethodInvocation(J.MethodInvocation method, Integer integer) {
435435
}
436436
return m;
437437
}
438+
if (loc == STATEMENT_PREFIX && isScope(method) &&
439+
!(getCursor().getParentTreeCursor().getValue() instanceof J.Block)) {
440+
// Method invocation is used as an expression (e.g., inside return, assignment),
441+
// not as a standalone statement in a block. Parse as expression replacement.
442+
return autoFormat(unsubstitute(templateParser.parseExpression(
443+
getCursor(),
444+
substitutedTemplate,
445+
substitutions.getTypeVariables(),
446+
loc))
447+
.withPrefix(method.getPrefix()), integer);
448+
}
438449
return maybeReplaceStatement(method, J.class, 0);
439450
}
440451

441452
@Override
442453
public J visitNewClass(J.NewClass newClass, Integer p) {
443454
if (isScope(newClass)) {
455+
if (loc == STATEMENT_PREFIX &&
456+
!(getCursor().getParentTreeCursor().getValue() instanceof J.Block)) {
457+
return autoFormat(unsubstitute(templateParser.parseExpression(
458+
getCursor(),
459+
substitutedTemplate,
460+
substitutions.getTypeVariables(),
461+
loc))
462+
.withPrefix(newClass.getPrefix()), p);
463+
}
444464
// allow a `J.NewClass` to also be replaced by an expression
445465
return maybeReplaceStatement(newClass, J.class, p);
446466
}

0 commit comments

Comments
 (0)