diff --git a/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/ast/common/CodebaseContext.java b/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/ast/common/CodebaseContext.java index 922091c..6271b04 100644 --- a/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/ast/common/CodebaseContext.java +++ b/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/ast/common/CodebaseContext.java @@ -442,6 +442,12 @@ public class CodebaseContext { } if (!visited.add(cleanName)) return; + // Fail closed on ambiguous simple type names: we must not pick an arbitrary FQN and widen + // to unrelated implementations across packages. + if (cleanName != null && !cleanName.contains(".") && ambiguousSimpleNames.contains(cleanName)) { + return; + } + // Try direct match List directImpls = interfaceToImpls.get(cleanName); diff --git a/state_machine_exporter/src/test/java/click/kamil/springstatemachineexporter/ast/common/CodebaseContextTest.java b/state_machine_exporter/src/test/java/click/kamil/springstatemachineexporter/ast/common/CodebaseContextTest.java index 690faed..322f432 100644 --- a/state_machine_exporter/src/test/java/click/kamil/springstatemachineexporter/ast/common/CodebaseContextTest.java +++ b/state_machine_exporter/src/test/java/click/kamil/springstatemachineexporter/ast/common/CodebaseContextTest.java @@ -250,6 +250,29 @@ class CodebaseContextTest { assertThat(context.getEnumValues("b.OrderEvent")).containsExactly("b.OrderEvent.SHIP"); } + @Test + void getImplementationsShouldFailClosedForAmbiguousSimpleTypeName() throws IOException { + Files.createDirectories(tempDir.resolve("a")); + Files.createDirectories(tempDir.resolve("b")); + Files.writeString(tempDir.resolve("a/Service.java"), + "package a; public interface Service {}"); + Files.writeString(tempDir.resolve("a/AServiceImpl.java"), + "package a; public class AServiceImpl implements Service {}"); + Files.writeString(tempDir.resolve("b/Service.java"), + "package b; public interface Service {}"); + Files.writeString(tempDir.resolve("b/BServiceImpl.java"), + "package b; public class BServiceImpl implements Service {}"); + + CodebaseContext context = new CodebaseContext(); + context.scan(tempDir); + + assertThat(context.getImplementations("Service")) + .as("ambiguous simple interface name must not widen to arbitrary impls") + .isEmpty(); + assertThat(context.getImplementations("a.Service")).contains("a.AServiceImpl"); + assertThat(context.getImplementations("b.Service")).contains("b.BServiceImpl"); + } + @Test void shouldFindClassWithFullyQualifiedAnnotation() throws IOException { String source = """