Scope implementation and transition capping to package-qualified types

When an interface simple name is ambiguous, resolve FQN implementation
lookups within the requested package only, and stop capping polymorphic
candidates to machine transitions by constant name across enum types.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-14 05:45:23 +02:00
parent 828f1260cd
commit 82f4e5c087
4 changed files with 45 additions and 8 deletions

View File

@@ -404,7 +404,7 @@ public final class MachineEnumCanonicalizer {
} }
if (!transitionEvents.isEmpty()) { if (!transitionEvents.isEmpty()) {
result = capToConfiguredTransitionEvents(result, transitionEvents); result = capToConfiguredTransitionEvents(result, transitionEvents, eventTypeFqn);
} }
return result; return result;
} }
@@ -414,7 +414,8 @@ public final class MachineEnumCanonicalizer {
*/ */
static List<String> capToConfiguredTransitionEvents( static List<String> capToConfiguredTransitionEvents(
List<String> candidates, List<String> candidates,
List<String> transitionEvents) { List<String> transitionEvents,
String eventTypeFqn) {
if (transitionEvents == null || transitionEvents.isEmpty()) { if (transitionEvents == null || transitionEvents.isEmpty()) {
return candidates == null ? List.of() : candidates; return candidates == null ? List.of() : candidates;
} }
@@ -430,9 +431,21 @@ public final class MachineEnumCanonicalizer {
capped.add(candidate); capped.add(candidate);
continue; continue;
} }
String candidateType = enumTypeFromRef(candidate);
String constant = constantName(candidate); String constant = constantName(candidate);
for (String transitionEvent : transitionEvents) { for (String transitionEvent : transitionEvents) {
if (constantName(transitionEvent).equals(constant)) { if (!constantName(transitionEvent).equals(constant)) {
continue;
}
String transitionType = enumTypeFromRef(transitionEvent);
if (candidate.contains(".") && transitionEvent.contains(".")) {
if (candidateType != null && transitionType != null
&& enumTypesMatch(candidateType, transitionType)) {
capped.add(transitionEvent);
break;
}
} else if (eventTypeFqn != null && transitionType != null
&& enumTypesMatch(eventTypeFqn, transitionType)) {
capped.add(transitionEvent); capped.add(transitionEvent);
break; break;
} }

View File

@@ -460,9 +460,23 @@ public class CodebaseContext {
} }
// Try simple name match if input was FQN // Try simple name match if input was FQN
if (directImpls == null && typeName.contains(".")) { if (directImpls == null && cleanName.contains(".")) {
String simpleName = typeName.substring(typeName.lastIndexOf('.') + 1); String simpleName = cleanName.substring(cleanName.lastIndexOf('.') + 1);
directImpls = interfaceToImpls.get(simpleName); List<String> simpleImpls = interfaceToImpls.get(simpleName);
if (simpleImpls != null) {
if (ambiguousSimpleNames.contains(simpleName)) {
String pkg = cleanName.substring(0, cleanName.lastIndexOf('.'));
List<String> pkgScoped = new ArrayList<>();
for (String impl : simpleImpls) {
if (impl.startsWith(pkg + ".")) {
pkgScoped.add(impl);
}
}
directImpls = pkgScoped.isEmpty() ? null : pkgScoped;
} else {
directImpls = simpleImpls;
}
}
} }
if (directImpls != null) { if (directImpls != null) {

View File

@@ -263,6 +263,16 @@ class MachineEnumCanonicalizerTest {
assertThat(enriched.getPolymorphicEvents()).isNullOrEmpty(); assertThat(enriched.getPolymorphicEvents()).isNullOrEmpty();
} }
@Test
void shouldNotCapForeignPackageEnumConstantToMachineTransition() {
List<String> capped = MachineEnumCanonicalizer.capToConfiguredTransitionEvents(
List.of("b.OrderEvent.PAY"),
List.of("a.OrderEvent.PAY"),
"a.OrderEvent");
assertThat(capped).isEmpty();
}
@Test @Test
void shouldValidateRawNameConsistencyForCanonicalHelpers() { void shouldValidateRawNameConsistencyForCanonicalHelpers() {
assertThat(MachineEnumCanonicalizer.isRawNameConsistentWithFullIdentifier( assertThat(MachineEnumCanonicalizer.isRawNameConsistentWithFullIdentifier(

View File

@@ -271,8 +271,8 @@ class CodebaseContextTest {
assertThat(context.getImplementations("Service")) assertThat(context.getImplementations("Service"))
.as("ambiguous simple interface name must not widen to arbitrary impls") .as("ambiguous simple interface name must not widen to arbitrary impls")
.isEmpty(); .isEmpty();
assertThat(context.getImplementations("a.Service")).contains("a.AServiceImpl"); assertThat(context.getImplementations("a.Service")).containsExactly("a.AServiceImpl");
assertThat(context.getImplementations("b.Service")).contains("b.BServiceImpl"); assertThat(context.getImplementations("b.Service")).containsExactly("b.BServiceImpl");
} }
@Test @Test