From f3c82d5d7f9f5e4c3127e74ef55da759d561f9e5 Mon Sep 17 00:00:00 2001 From: Kamil Patryk Kozakowski Date: Mon, 13 Jul 2026 21:51:22 +0200 Subject: [PATCH] Fail closed on ambiguous simple enum names Track enum name collisions during scan and refuse to resolve enum values by simple name when multiple enums share the same identifier across packages. Co-authored-by: Cursor --- .../ast/common/CodebaseContext.java | 10 +++++++++- .../ast/common/CodebaseContextTest.java | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) 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 6d6ade7..922091c 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 @@ -482,7 +482,12 @@ public class CodebaseContext { } } enumValues.put(fqn, values); - if (!simpleNameToFqn.containsKey(simpleName)) { + if (simpleNameToFqn.containsKey(simpleName)) { + String existingFqn = simpleNameToFqn.get(simpleName); + if (existingFqn != null && !existingFqn.equals(fqn)) { + ambiguousSimpleNames.add(simpleName); + } + } else { simpleNameToFqn.put(simpleName, fqn); } @@ -516,6 +521,9 @@ public class CodebaseContext { List values = enumValues.get(cleanName); if (values == null) { + if (!cleanName.contains(".") && ambiguousSimpleNames.contains(cleanName)) { + return null; + } String fqn = simpleNameToFqn.get(cleanName); if (fqn != null) values = enumValues.get(fqn); } 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 5ce5e6d..690faed 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 @@ -231,6 +231,25 @@ class CodebaseContextTest { assertThat(context.findEntryPointClasses(List.of("EnableStateMachine"))).isEmpty(); } + @Test + void getEnumValuesShouldFailClosedForAmbiguousSimpleEnumName() throws IOException { + Files.createDirectories(tempDir.resolve("a")); + Files.createDirectories(tempDir.resolve("b")); + Files.writeString(tempDir.resolve("a/OrderEvent.java"), + "package a; public enum OrderEvent { PAY }"); + Files.writeString(tempDir.resolve("b/OrderEvent.java"), + "package b; public enum OrderEvent { SHIP }"); + + CodebaseContext context = new CodebaseContext(); + context.scan(tempDir); + + assertThat(context.getEnumValues("OrderEvent")) + .as("ambiguous simple enum name must not resolve to arbitrary package") + .isNull(); + assertThat(context.getEnumValues("a.OrderEvent")).containsExactly("a.OrderEvent.PAY"); + assertThat(context.getEnumValues("b.OrderEvent")).containsExactly("b.OrderEvent.SHIP"); + } + @Test void shouldFindClassWithFullyQualifiedAnnotation() throws IOException { String source = """