Skip to content

Commit 0bceb5c

Browse files
committed
Fix ChangeType UnsupportedOperationException on JS FunctionCall nodes
ChangeType.postVisit() falls through to the TypedTree branch for JS.FunctionCall nodes, which throws UnsupportedOperationException because withType() is not supported on MethodCall implementations. Add a MethodCall catch-all branch before the TypedTree fallback that uses withMethodType() instead, handling JS.FunctionCall and any other future MethodCall implementations not explicitly handled.
1 parent 5b212d9 commit 0bceb5c

9 files changed

Lines changed: 90 additions & 4 deletions

File tree

rewrite-csharp/csharp/OpenRewrite/Java/Rpc/JavaReceiver.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,8 @@ public override J VisitClassDeclaration(ClassDeclaration classDecl, RpcReceiveQu
262262
var implements_ = q.Receive(classDecl.Implements, c => VisitContainer(c, q));
263263
var permits = q.Receive(classDecl.Permits, c => VisitContainer(c, q));
264264
var body = q.Receive((J)classDecl.Body, el => (J)VisitNonNull(el, q));
265-
return classDecl.WithId(_pvId).WithPrefix(_pvPrefix).WithMarkers(_pvMarkers).WithLeadingAnnotations(leadingAnnotations!).WithModifiers(modifiers!).WithClassKind((ClassDeclaration.Kind)kind!).WithName((Identifier)name!).WithTypeParameters(typeParameters).WithPrimaryConstructor(primaryConstructor).WithExtends(extends_).WithImplements(implements_).WithPermits(permits).WithBody((Block)body!);
265+
var type = q.Receive(classDecl.Type, t => (JavaType.FullyQualified?)VisitType(t, q));
266+
return classDecl.WithId(_pvId).WithPrefix(_pvPrefix).WithMarkers(_pvMarkers).WithLeadingAnnotations(leadingAnnotations!).WithModifiers(modifiers!).WithClassKind((ClassDeclaration.Kind)kind!).WithName((Identifier)name!).WithTypeParameters(typeParameters).WithPrimaryConstructor(primaryConstructor).WithExtends(extends_).WithImplements(implements_).WithPermits(permits).WithBody((Block)body!).WithType(type);
266267
}
267268

268269
private J VisitClassDeclarationKind(ClassDeclaration.Kind kind, RpcReceiveQueue q)

rewrite-csharp/csharp/OpenRewrite/Java/Rpc/JavaSender.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ public override J VisitClassDeclaration(ClassDeclaration classDecl, RpcSendQueue
234234
q.GetAndSend(classDecl, c => c.Implements, impl => VisitContainer(impl, q));
235235
q.GetAndSend(classDecl, c => c.Permits, perm => VisitContainer(perm, q));
236236
q.GetAndSend(classDecl, c => (J)c.Body, j => Visit(j, q));
237+
q.GetAndSend(classDecl, c => AsRef(c.Type), type => VisitType(GetValueNonNull<JavaType>(type), q));
237238
return classDecl;
238239
}
239240

rewrite-java/src/main/java/org/openrewrite/java/ChangeType.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,9 @@ private void addImport(JavaType.FullyQualified owningClass) {
221221
} else if (j instanceof J.NewClass) {
222222
J.NewClass n = (J.NewClass) j;
223223
j = n.withConstructorType(updateType(n.getConstructorType()));
224+
} else if (j instanceof MethodCall) {
225+
MethodCall call = (MethodCall) j;
226+
j = (J) call.withMethodType(updateType(call.getMethodType()));
224227
} else if (tree instanceof TypedTree) {
225228
j = ((TypedTree) tree).withType(updateType(((TypedTree) tree).getType()));
226229
} else if (tree instanceof JavaSourceFile) {

rewrite-java/src/main/java/org/openrewrite/java/internal/rpc/JavaReceiver.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,8 @@ public J visitClassDeclaration(J.ClassDeclaration classDecl, RpcReceiveQueue q)
146146
.getPadding().withExtends(q.receive(classDecl.getPadding().getExtends(), e -> visitLeftPadded(e, q)))
147147
.getPadding().withImplements(q.receive(classDecl.getPadding().getImplements(), i -> visitContainer(i, q)))
148148
.getPadding().withPermits(q.receive(classDecl.getPadding().getPermits(), p -> visitContainer(p, q)))
149-
.withBody(q.receive(classDecl.getBody(), b -> (J.Block) visitNonNull(b, q)));
149+
.withBody(q.receive(classDecl.getBody(), b -> (J.Block) visitNonNull(b, q)))
150+
.withType(q.receive(classDecl.getType(), t -> (JavaType.FullyQualified) visitType(t, q)));
150151
}
151152

152153
private J.ClassDeclaration.Kind visitClassDeclarationKind(J.ClassDeclaration.Kind kind, RpcReceiveQueue q) {

rewrite-java/src/main/java/org/openrewrite/java/internal/rpc/JavaSender.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ public J visitClassDeclaration(J.ClassDeclaration classDecl, RpcSendQueue q) {
140140
q.getAndSend(classDecl, c -> c.getPadding().getImplements(), impl -> visitContainer(impl, q));
141141
q.getAndSend(classDecl, c -> c.getPadding().getPermits(), impl -> visitContainer(impl, q));
142142
q.getAndSend(classDecl, J.ClassDeclaration::getBody, j -> visit(j, q));
143+
q.getAndSend(classDecl, c -> asRef(c.getType()), type -> visitType(getValueNonNull(type), q));
143144
return classDecl;
144145
}
145146

rewrite-javascript/rewrite/src/java/rpc.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,7 @@ export class JavaSender extends JavaVisitor<RpcSendQueue> {
713713
await q.getAndSend(cls, c => c.implements, impl => this.visitContainer(impl, q));
714714
await q.getAndSend(cls, c => c.permitting, perm => this.visitContainer(perm, q));
715715
await q.getAndSend(cls, c => c.body, body => this.visit(body, q));
716+
await q.getAndSend(cls, c => asRef(c.type), type => this.visitType(type, q));
716717
return cls;
717718
}
718719

@@ -1435,7 +1436,8 @@ export class JavaReceiver extends JavaVisitor<RpcReceiveQueue> {
14351436
extends: await q.receive(cls.extends, ext => this.visitLeftPadded(ext, q)),
14361437
implements: await q.receive(cls.implements, impl => this.visitContainer(impl, q)),
14371438
permitting: await q.receive(cls.permitting, perm => this.visitContainer(perm, q)),
1438-
body: await q.receive(cls.body, body => this.visit(body, q))
1439+
body: await q.receive(cls.body, body => this.visit(body, q)),
1440+
type: await q.receive(cls.type, type => this.visitType(type, q)) as Type.Class
14391441
};
14401442
return updateIfChanged(cls, updates);
14411443
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
* <p>
4+
* Licensed under the Moderne Source Available License (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://docs.moderne.io/licensing/moderne-source-available-license
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.javascript;
17+
18+
import org.junit.jupiter.api.Test;
19+
import org.openrewrite.java.ChangeType;
20+
import org.openrewrite.java.tree.J;
21+
import org.openrewrite.javascript.tree.JS;
22+
import org.openrewrite.test.RewriteTest;
23+
24+
import java.util.concurrent.atomic.AtomicBoolean;
25+
26+
import static org.assertj.core.api.Assertions.assertThat;
27+
import static org.openrewrite.javascript.Assertions.javascript;
28+
29+
/**
30+
* Prove that {@link ChangeType}, written for Java, does not fail on JavaScript source files
31+
* containing {@link JS.FunctionCall} nodes.
32+
*/
33+
class ChangeTypeAdaptabilityTest implements RewriteTest {
34+
35+
@Test
36+
void functionCallDoesNotThrow() {
37+
AtomicBoolean hasFunctionCall = new AtomicBoolean(false);
38+
rewriteRun(
39+
spec -> spec.recipe(new ChangeType("Foo", "Bar", false)),
40+
javascript(
41+
"""
42+
class Foo {
43+
getHandler() {
44+
return function() { return 1; };
45+
}
46+
}
47+
var result = new Foo().getHandler()();
48+
""",
49+
"""
50+
class Bar {
51+
getHandler() {
52+
return function() { return 1; };
53+
}
54+
}
55+
var result = new Bar().getHandler()();
56+
""",
57+
spec -> spec.afterRecipe(cu -> {
58+
new JavaScriptVisitor<Integer>() {
59+
@Override
60+
public JS.FunctionCall visitFunctionCall(JS.FunctionCall functionCall, Integer p) {
61+
hasFunctionCall.set(true);
62+
return functionCall;
63+
}
64+
}.visit(cu, 0);
65+
assertThat(hasFunctionCall.get())
66+
.as("Expected parsed JavaScript to contain a JS.FunctionCall node")
67+
.isTrue();
68+
assertThat(cu.getClasses().getFirst().getType())
69+
.as("Expected JS ClassDeclaration to have type attribution")
70+
.isNotNull();
71+
})
72+
)
73+
);
74+
}
75+
}

rewrite-python/rewrite/src/rewrite/rpc/python_receiver.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -727,10 +727,11 @@ def _visit_j_class_declaration(self, class_decl, q: RpcReceiveQueue):
727727
lambda c: self._receive_container(c, q) if c else None
728728
)
729729
body = q.receive(class_decl.body)
730+
type_ = q.receive(class_decl.type)
730731
return replace_if_changed(class_decl, leading_annotations=leading_annotations, modifiers=modifiers,
731732
kind=kind, name=name, type_parameters=type_parameters,
732733
primary_constructor=primary_constructor, extends=extends, implements=implements,
733-
permits=permits, body=body)
734+
permits=permits, body=body, type=type_)
734735

735736
def _visit_j_class_declaration_kind(self, kind, q: RpcReceiveQueue):
736737
from rewrite.java.tree import ClassDeclaration

rewrite-python/rewrite/src/rewrite/rpc/python_sender.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,7 @@ def _visit_j_class_declaration(self, class_decl, q: 'RpcSendQueue') -> None:
678678
q.get_and_send(class_decl, lambda x: x.padding.permits if hasattr(x.padding, 'permits') else None,
679679
lambda c: self._visit_container(c, q) if c else None)
680680
q.get_and_send(class_decl, lambda x: x.body, lambda el: self._visit(el, q))
681+
q.get_and_send_as_ref(class_decl, lambda x: x.type, lambda t: self._visit_type(t, q) if t else None)
681682

682683
def _visit_j_class_declaration_kind(self, kind, q: 'RpcSendQueue') -> None:
683684
# Java ClassDeclaration.Kind: preVisit IS automatically called by _visit before this method

0 commit comments

Comments
 (0)