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:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user