C
This commit is contained in:
@@ -476,46 +476,8 @@ public class CodebaseContext {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try direct match
|
List<String> directImpls = resolveDirectImplementations(typeName, cleanName);
|
||||||
List<String> directImpls = interfaceToImpls.get(cleanName);
|
String directImplsSource = describeDirectImplsSource(typeName, cleanName);
|
||||||
String directImplsSource = directImpls != null ? "direct" : null;
|
|
||||||
|
|
||||||
// Try FQN match if input was simple name
|
|
||||||
if (directImpls == null) {
|
|
||||||
String fqn = simpleNameToFqn.get(typeName);
|
|
||||||
if (fqn != null) {
|
|
||||||
directImpls = interfaceToImpls.get(fqn);
|
|
||||||
if (directImpls != null) {
|
|
||||||
directImplsSource = "simple→fqn";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try simple name match if input was FQN
|
|
||||||
if (directImpls == null && cleanName.contains(".")) {
|
|
||||||
String simpleName = cleanName.substring(cleanName.lastIndexOf('.') + 1);
|
|
||||||
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) {
|
|
||||||
directImplsSource = "fqn→simple";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (directImplsSource == null) {
|
|
||||||
directImplsSource = "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (log.isDebugEnabled()) {
|
if (log.isDebugEnabled()) {
|
||||||
log.debug(
|
log.debug(
|
||||||
@@ -535,6 +497,80 @@ public class CodebaseContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Union direct subclasses/implementors from every index bucket that refers to the same supertype.
|
||||||
|
* Generated stubs often index under a literal FQN ({@code extends abcd.ccc.AML}) while hand-written
|
||||||
|
* sources index under the simple name ({@code extends AML}) — merge instead of stopping at the first hit.
|
||||||
|
*/
|
||||||
|
private List<String> resolveDirectImplementations(String typeName, String cleanName) {
|
||||||
|
LinkedHashSet<String> merged = new LinkedHashSet<>();
|
||||||
|
if (cleanName == null || cleanName.isBlank()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
addImplsForKey(merged, cleanName);
|
||||||
|
|
||||||
|
if (!cleanName.contains(".")) {
|
||||||
|
String fqn = simpleNameToFqn.get(typeName != null ? typeName : cleanName);
|
||||||
|
if (fqn != null && !fqn.equals(cleanName)) {
|
||||||
|
addImplsForKey(merged, fqn);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String simpleName = cleanName.substring(cleanName.lastIndexOf('.') + 1);
|
||||||
|
if (!ambiguousSimpleNames.contains(simpleName)) {
|
||||||
|
addImplsForKey(merged, simpleName);
|
||||||
|
} else {
|
||||||
|
String pkg = cleanName.substring(0, cleanName.lastIndexOf('.'));
|
||||||
|
List<String> simpleImpls = interfaceToImpls.get(simpleName);
|
||||||
|
if (simpleImpls != null) {
|
||||||
|
for (String impl : simpleImpls) {
|
||||||
|
if (impl.startsWith(pkg + ".")) {
|
||||||
|
merged.add(impl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return merged.isEmpty() ? null : new ArrayList<>(merged);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addImplsForKey(Set<String> merged, String key) {
|
||||||
|
List<String> impls = interfaceToImpls.get(key);
|
||||||
|
if (impls != null) {
|
||||||
|
merged.addAll(impls);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String describeDirectImplsSource(String typeName, String cleanName) {
|
||||||
|
if (cleanName == null || cleanName.isBlank()) {
|
||||||
|
return "none";
|
||||||
|
}
|
||||||
|
boolean hasDirect = interfaceToImpls.containsKey(cleanName);
|
||||||
|
boolean hasFqnAlias = false;
|
||||||
|
boolean hasSimpleAlias = false;
|
||||||
|
if (!cleanName.contains(".")) {
|
||||||
|
String fqn = simpleNameToFqn.get(typeName != null ? typeName : cleanName);
|
||||||
|
hasFqnAlias = fqn != null && interfaceToImpls.containsKey(fqn);
|
||||||
|
} else {
|
||||||
|
String simpleName = cleanName.substring(cleanName.lastIndexOf('.') + 1);
|
||||||
|
hasSimpleAlias = interfaceToImpls.containsKey(simpleName);
|
||||||
|
}
|
||||||
|
if (hasDirect && (hasFqnAlias || hasSimpleAlias)) {
|
||||||
|
return "merged";
|
||||||
|
}
|
||||||
|
if (hasDirect) {
|
||||||
|
return "direct";
|
||||||
|
}
|
||||||
|
if (hasFqnAlias) {
|
||||||
|
return "simple→fqn";
|
||||||
|
}
|
||||||
|
if (hasSimpleAlias) {
|
||||||
|
return "fqn→simple";
|
||||||
|
}
|
||||||
|
return "none";
|
||||||
|
}
|
||||||
|
|
||||||
private void indexEnum(EnumDeclaration ed, String parentFqn, Path javaFile) {
|
private void indexEnum(EnumDeclaration ed, String parentFqn, Path javaFile) {
|
||||||
String simpleName = ed.getName().getIdentifier();
|
String simpleName = ed.getName().getIdentifier();
|
||||||
String fqn = parentFqn.isEmpty() ? simpleName : parentFqn + "." + simpleName;
|
String fqn = parentFqn.isEmpty() ? simpleName : parentFqn + "." + simpleName;
|
||||||
|
|||||||
@@ -275,6 +275,32 @@ class CodebaseContextTest {
|
|||||||
assertThat(context.getImplementations("b.Service")).containsExactly("b.BServiceImpl");
|
assertThat(context.getImplementations("b.Service")).containsExactly("b.BServiceImpl");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getImplementationsShouldMergeFqnAndSimpleNameIndexBuckets() throws IOException {
|
||||||
|
Files.createDirectories(tempDir.resolve("abcd/ccc"));
|
||||||
|
Files.writeString(tempDir.resolve("abcd/ccc/AML.java"), """
|
||||||
|
package abcd.ccc;
|
||||||
|
public abstract class AML {}
|
||||||
|
""");
|
||||||
|
Files.writeString(tempDir.resolve("abcd/ccc/RealListener.java"), """
|
||||||
|
package abcd.ccc;
|
||||||
|
public class RealListener extends AML {}
|
||||||
|
""");
|
||||||
|
// KAPT-style stub: no import, FQN in extends clause
|
||||||
|
Files.writeString(tempDir.resolve("abcd/ccc/Av.java"), """
|
||||||
|
package abcd.ccc;
|
||||||
|
public class Av extends abcd.ccc.AML {}
|
||||||
|
""");
|
||||||
|
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
assertThat(context.getImplementations("abcd.ccc.AML"))
|
||||||
|
.containsExactlyInAnyOrder("abcd.ccc.Av", "abcd.ccc.RealListener");
|
||||||
|
assertThat(context.getImplementations("AML"))
|
||||||
|
.containsExactlyInAnyOrder("abcd.ccc.Av", "abcd.ccc.RealListener");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void areSameTypeOrUnambiguousSimpleMatchShouldFailClosedForAmbiguousTypes() throws IOException {
|
void areSameTypeOrUnambiguousSimpleMatchShouldFailClosedForAmbiguousTypes() throws IOException {
|
||||||
Files.createDirectories(tempDir.resolve("a"));
|
Files.createDirectories(tempDir.resolve("a"));
|
||||||
|
|||||||
Reference in New Issue
Block a user