more fixes

This commit is contained in:
2026-06-20 07:21:19 +02:00
parent d59f4ac166
commit 3ac057618f
2 changed files with 276 additions and 1 deletions

View File

@@ -55,7 +55,26 @@ public class ConstantResolver {
return resolveManual(sn, context, visited); return resolveManual(sn, context, visited);
} }
if (expr instanceof MethodInvocation mi) { if (expr instanceof MethodInvocation mi) {
if ((mi.getName().getIdentifier().equals("name") || mi.getName().getIdentifier().equals("toString")) && mi.arguments().isEmpty()) {
String val = resolveInternal(mi.getExpression(), context, visited);
if (val != null) {
// If it was resolved as a fully qualified enum string like "MyEvents.ORDER_PAID", strip it
if (val.contains(".")) {
return val.substring(val.lastIndexOf('.') + 1);
}
return val;
}
// Fallback for unresolved AST nodes
if (mi.getExpression() instanceof QualifiedName qn) {
return qn.getName().getIdentifier();
} else if (mi.getExpression() instanceof SimpleName sn) {
return sn.getIdentifier();
}
}
IMethodBinding mb = mi.resolveMethodBinding(); IMethodBinding mb = mi.resolveMethodBinding();
if (mb != null) { if (mb != null) {
ITypeBinding returnType = mb.getReturnType(); ITypeBinding returnType = mb.getReturnType();

View File

@@ -1081,4 +1081,260 @@ class CallGraphBuilderTest {
assertThat(chains).hasSize(1); assertThat(chains).hasSize(1);
assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents()).containsExactlyInAnyOrder("EVENT_A", "EVENT_B"); assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents()).containsExactlyInAnyOrder("EVENT_A", "EVENT_B");
} }
}
@Test
void shouldResolveConstantsFromSwitchStatementInMethod() throws IOException {
String source = """
package com.example;
public class OrderController {
private OrderService service;
public void handleStatus(DomainEvent domainEvent) {
service.process(domainEvent.getEventForStatus());
}
}
class DomainEvent {
private OrderStatus status;
public String getEventForStatus() {
switch (status) {
case NEW: return "CREATE_EVENT";
case PAID: return "PAY_EVENT";
default: return "CANCEL_EVENT";
}
}
}
class OrderService {
public void process(String event) {}
}
enum OrderStatus { NEW, PAID }
""";
Path tempDir = Files.createTempDirectory("callgraph_switch");
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
CodebaseContext context = new CodebaseContext();
context.scan(tempDir);
CallGraphBuilder builder = new CallGraphBuilder(context);
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
List<CallChain> chains = builder.findChains(List.of(entryPoint), List.of(trigger));
org.assertj.core.api.Assertions.assertThat(chains).hasSize(1);
org.assertj.core.api.Assertions.assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents()).containsExactlyInAnyOrder("CREATE_EVENT", "PAY_EVENT", "CANCEL_EVENT");
}
@Test
void shouldGracefullyHandleComplexMethodChainsWithoutCrashing() throws IOException {
String source = """
package com.example;
import java.util.Optional;
public class OrderController {
private OrderService service;
public void handleStatus(DomainEvent domainEvent) {
service.process(Optional.of(domainEvent).get().getEvent());
}
}
class DomainEvent {
public String getEvent() { return "COMPLEX_EVENT"; }
}
class OrderService {
public void process(String event) {}
}
""";
Path tempDir = Files.createTempDirectory("callgraph_complex_chain");
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
CodebaseContext context = new CodebaseContext();
context.scan(tempDir);
CallGraphBuilder builder = new CallGraphBuilder(context);
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
List<CallChain> chains = builder.findChains(List.of(entryPoint), List.of(trigger));
org.assertj.core.api.Assertions.assertThat(chains).hasSize(1);
}
@Test
void shouldNotCrashOnStaticMethodWrapping() throws IOException {
String source = """
package com.example;
public class OrderController {
private OrderService service;
public void handleStatus(DomainEvent domainEvent) {
service.process(String.valueOf(domainEvent.getEvent()));
}
}
class DomainEvent {
public String getEvent() { return "STATIC_WRAP_EVENT"; }
}
class OrderService {
public void process(String event) {}
}
""";
Path tempDir = Files.createTempDirectory("callgraph_static_wrap");
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
CodebaseContext context = new CodebaseContext();
context.scan(tempDir);
CallGraphBuilder builder = new CallGraphBuilder(context);
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
List<CallChain> chains = builder.findChains(List.of(entryPoint), List.of(trigger));
org.assertj.core.api.Assertions.assertThat(chains).hasSize(1);
}
@Test
void shouldResolveConstantsFromStringConcatenation() throws IOException {
String source = """
package com.example;
public class OrderController {
private OrderService service;
public void handleStatus(DomainEvent domainEvent) {
service.process(domainEvent.getCustomEvent());
}
}
class DomainEvent {
public static final String PREFIX = "ORDER_";
public String getCustomEvent() {
return PREFIX + "CREATED";
}
}
class OrderService {
public void process(String event) {}
}
""";
Path tempDir = Files.createTempDirectory("callgraph_concat");
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
CodebaseContext context = new CodebaseContext();
context.scan(tempDir);
CallGraphBuilder builder = new CallGraphBuilder(context);
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
List<CallChain> chains = builder.findChains(List.of(entryPoint), List.of(trigger));
org.assertj.core.api.Assertions.assertThat(chains).hasSize(1);
org.assertj.core.api.Assertions.assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents()).containsExactlyInAnyOrder("ORDER_CREATED");
}
@Test
void shouldResolveConstantsFromEnumReturn() throws IOException {
String source = """
package com.example;
public class OrderController {
private OrderService service;
public void handleStatus(DomainEvent domainEvent) {
service.process(domainEvent.getEventEnum());
}
}
class DomainEvent {
public String getEventEnum() {
return MyEvents.ORDER_PAID.name();
}
}
class OrderService {
public void process(String event) {}
}
enum MyEvents { ORDER_PAID }
""";
Path tempDir = Files.createTempDirectory("callgraph_enum_return");
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
CodebaseContext context = new CodebaseContext();
context.scan(tempDir);
CallGraphBuilder builder = new CallGraphBuilder(context);
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
List<CallChain> chains = builder.findChains(List.of(entryPoint), List.of(trigger));
org.assertj.core.api.Assertions.assertThat(chains).hasSize(1);
org.assertj.core.api.Assertions.assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents()).containsExactlyInAnyOrder("ORDER_PAID");
}
@Test
void shouldResolveConstantsFromAlternativeGetterName() throws IOException {
String source = """
package com.example;
public class OrderController {
private OrderService service;
public void handleStatus(DomainEvent domainEvent) {
service.process(domainEvent.get2Type());
}
}
class DomainEvent {
public String get2Type() {
return "ALTERNATIVE_EVENT";
}
}
class OrderService {
public void process(String event) {}
}
""";
Path tempDir = Files.createTempDirectory("callgraph_alt_getter");
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
CodebaseContext context = new CodebaseContext();
context.scan(tempDir);
CallGraphBuilder builder = new CallGraphBuilder(context);
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
List<CallChain> chains = builder.findChains(List.of(entryPoint), List.of(trigger));
org.assertj.core.api.Assertions.assertThat(chains).hasSize(1);
org.assertj.core.api.Assertions.assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents()).containsExactlyInAnyOrder("ALTERNATIVE_EVENT");
}
@Test
void shouldResolveConstantsFromStringToString() throws IOException {
String source = """
package com.example;
public class OrderController {
private OrderService service;
public void handleStatus(DomainEvent domainEvent) {
service.process(domainEvent.getEventString());
}
}
class DomainEvent {
public static final String ACTUAL_STRING = "STRING_LITERAL_EVENT";
public String getEventString() {
return ACTUAL_STRING.toString();
}
}
class OrderService {
public void process(String event) {}
}
""";
Path tempDir = Files.createTempDirectory("callgraph_string_tostring");
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
CodebaseContext context = new CodebaseContext();
context.scan(tempDir);
CallGraphBuilder builder = new CallGraphBuilder(context);
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
List<CallChain> chains = builder.findChains(List.of(entryPoint), List.of(trigger));
org.assertj.core.api.Assertions.assertThat(chains).hasSize(1);
org.assertj.core.api.Assertions.assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents()).containsExactlyInAnyOrder("STRING_LITERAL_EVENT");
}
}