Extend reactive operator support for map and switchMap lambda remapping.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -603,5 +603,95 @@ class InheritanceAndNestedResolutionTest {
|
||||
assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents())
|
||||
.containsExactly("OrderEvent.SHIP");
|
||||
}
|
||||
|
||||
@Test
|
||||
void callGraphShouldResolveGetterInsideMapLambda() throws IOException {
|
||||
writeJava("com/example/Api.java", """
|
||||
package com.example;
|
||||
public class ApiController {
|
||||
StateMachine sm;
|
||||
Payload payload;
|
||||
public void handle() {
|
||||
Mono.just(payload).map(p -> sm.sendEvent(p.getEvent()));
|
||||
}
|
||||
}
|
||||
class Payload {
|
||||
OrderEvent event = OrderEvent.PAY;
|
||||
OrderEvent getEvent() { return event; }
|
||||
}
|
||||
enum OrderEvent { PAY, SHIP }
|
||||
class StateMachine { void sendEvent(OrderEvent event) {} }
|
||||
class Mono {
|
||||
static Mono just(Object o) { return new Mono(); }
|
||||
Mono map(Object fn) { return this; }
|
||||
}
|
||||
class OrderStateMachineConfig {}
|
||||
""");
|
||||
scan();
|
||||
|
||||
HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
|
||||
EntryPoint entry = EntryPoint.builder()
|
||||
.className("com.example.ApiController")
|
||||
.methodName("handle")
|
||||
.build();
|
||||
TriggerPoint trigger = TriggerPoint.builder()
|
||||
.className("com.example.StateMachine")
|
||||
.methodName("sendEvent")
|
||||
.event("event")
|
||||
.build();
|
||||
|
||||
List<CallChain> chains = engine.findChains(List.of(entry), List.of(trigger));
|
||||
assertThat(chains).hasSize(1);
|
||||
assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents())
|
||||
.containsExactly("OrderEvent.PAY");
|
||||
}
|
||||
|
||||
@Test
|
||||
void callGraphShouldResolveGetterThroughNestedSwitchMap() throws IOException {
|
||||
writeJava("com/example/Api.java", """
|
||||
package com.example;
|
||||
public class ApiController {
|
||||
StateMachine sm;
|
||||
Payload payload;
|
||||
public void handle() {
|
||||
Mono.just(payload)
|
||||
.switchMap(x -> Mono.just(x.getInner()))
|
||||
.switchMap(p -> sm.sendEvent(p.getEvent()));
|
||||
}
|
||||
}
|
||||
class Payload {
|
||||
Inner inner = new Inner();
|
||||
Inner getInner() { return inner; }
|
||||
}
|
||||
class Inner {
|
||||
OrderEvent event = OrderEvent.SHIP;
|
||||
OrderEvent getEvent() { return event; }
|
||||
}
|
||||
enum OrderEvent { PAY, SHIP }
|
||||
class StateMachine { void sendEvent(OrderEvent event) {} }
|
||||
class Mono {
|
||||
static Mono just(Object o) { return new Mono(); }
|
||||
Mono switchMap(Object fn) { return this; }
|
||||
}
|
||||
class OrderStateMachineConfig {}
|
||||
""");
|
||||
scan();
|
||||
|
||||
HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
|
||||
EntryPoint entry = EntryPoint.builder()
|
||||
.className("com.example.ApiController")
|
||||
.methodName("handle")
|
||||
.build();
|
||||
TriggerPoint trigger = TriggerPoint.builder()
|
||||
.className("com.example.StateMachine")
|
||||
.methodName("sendEvent")
|
||||
.event("event")
|
||||
.build();
|
||||
|
||||
List<CallChain> chains = engine.findChains(List.of(entry), List.of(trigger));
|
||||
assertThat(chains).hasSize(1);
|
||||
assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents())
|
||||
.containsExactly("OrderEvent.SHIP");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,4 +96,46 @@ class ReactiveExpressionSupportTest {
|
||||
eventExpr, context.getConstantResolver(), context);
|
||||
assertThat(remapped).isEqualTo("payload.getEvent()");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRemapLambdaParameterGetterInsideMap(@TempDir Path tempDir) throws Exception {
|
||||
String source = """
|
||||
package com.example;
|
||||
public class Runner {
|
||||
Payload payload;
|
||||
StateMachine sm;
|
||||
void run() {
|
||||
Mono.just(payload).map(p -> p.getEvent());
|
||||
}
|
||||
}
|
||||
class Payload { String getEvent() { return "X"; } }
|
||||
class StateMachine { void sendEvent(String e) {} }
|
||||
class Mono {
|
||||
static Mono just(Object o) { return new Mono(); }
|
||||
Mono map(Object fn) { return this; }
|
||||
}
|
||||
""";
|
||||
Path file = tempDir.resolve("Runner.java");
|
||||
Files.writeString(file, source);
|
||||
|
||||
CodebaseContext context = new CodebaseContext();
|
||||
context.scan(tempDir);
|
||||
|
||||
org.eclipse.jdt.core.dom.TypeDeclaration runner = context.getTypeDeclaration("com.example.Runner");
|
||||
CompilationUnit cu = (CompilationUnit) runner.getRoot();
|
||||
final MethodInvocation[] getEventCall = new MethodInvocation[1];
|
||||
cu.accept(new org.eclipse.jdt.core.dom.ASTVisitor() {
|
||||
@Override
|
||||
public boolean visit(MethodInvocation node) {
|
||||
if ("getEvent".equals(node.getName().getIdentifier()) && getEventCall[0] == null) {
|
||||
getEventCall[0] = node;
|
||||
}
|
||||
return super.visit(node);
|
||||
}
|
||||
});
|
||||
|
||||
String remapped = ReactiveExpressionSupport.remapLambdaParameterGetter(
|
||||
getEventCall[0], context.getConstantResolver(), context);
|
||||
assertThat(remapped).isEqualTo("payload.getEvent()");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user