* Resolve a Java/Kotlin reference whose receiver is the simple name of * an imported FQN: `Foo.bar(...)` where `import com.example.Foo;`. The * imported FQN converts to a file-path suffix (`com/example/Foo.java` * or `.kt`) which uniquely identifies the right symbol when multiple * classes share
( ref: UnresolvedRef, imports: ImportMapping[], context: ResolutionContext )
| 1690 | * ref) and `import static <Foo>.bar` style imports of a single member. |
| 1691 | */ |
| 1692 | function resolveJavaImportedReference( |
| 1693 | ref: UnresolvedRef, |
| 1694 | imports: ImportMapping[], |
| 1695 | context: ResolutionContext |
| 1696 | ): ResolvedRef | null { |
| 1697 | if (imports.length === 0) return null; |
| 1698 | |
| 1699 | const ext = ref.language === 'kotlin' ? '.kt' : '.java'; |
| 1700 | |
| 1701 | for (const imp of imports) { |
| 1702 | const matchesBare = imp.localName === ref.referenceName; |
| 1703 | const matchesQualified = ref.referenceName.startsWith(imp.localName + '.'); |
| 1704 | if (!matchesBare && !matchesQualified) continue; |
| 1705 | |
| 1706 | // Convert FQN to a file-path suffix. `com.example.Foo` -> |
| 1707 | // `com/example/Foo.java` (or `.kt`). The actual file may live |
| 1708 | // under any source root (`src/main/java/`, `src/`, etc.), so match |
| 1709 | // by suffix rather than exact path. |
| 1710 | const fqnPath = imp.source.replace(/\./g, '/') + ext; |
| 1711 | |
| 1712 | // Which symbol name to look up: the class itself, or a member. |
| 1713 | const memberName = matchesBare |
| 1714 | ? imp.localName |
| 1715 | : ref.referenceName.substring(imp.localName.length + 1); |
| 1716 | |
| 1717 | const candidates = context.getNodesByName(memberName); |
| 1718 | for (const node of candidates) { |
| 1719 | if (node.language !== ref.language) continue; |
| 1720 | const fp = node.filePath.replace(/\\/g, '/'); |
| 1721 | if (fp.endsWith(fqnPath) || fp.endsWith('/' + fqnPath)) { |
| 1722 | return { |
| 1723 | original: ref, |
| 1724 | targetNodeId: node.id, |
| 1725 | confidence: 0.9, |
| 1726 | resolvedBy: 'import', |
| 1727 | }; |
| 1728 | } |
| 1729 | } |
| 1730 | |
| 1731 | // `import static com.example.Foo.bar;` — the FQN's tail is the |
| 1732 | // member name, the part before is the owner class. Look up the |
| 1733 | // member named `<imp.localName>` (e.g. `bar`) and prefer the |
| 1734 | // candidate whose file matches the parent FQN's path. |
| 1735 | if (matchesBare) { |
| 1736 | const dot = imp.source.lastIndexOf('.'); |
| 1737 | if (dot > 0) { |
| 1738 | const ownerFqn = imp.source.substring(0, dot); |
| 1739 | const ownerPath = ownerFqn.replace(/\./g, '/') + ext; |
| 1740 | for (const node of candidates) { |
| 1741 | if (node.language !== ref.language) continue; |
| 1742 | const fp = node.filePath.replace(/\\/g, '/'); |
| 1743 | if (fp.endsWith(ownerPath) || fp.endsWith('/' + ownerPath)) { |
| 1744 | return { |
| 1745 | original: ref, |
| 1746 | targetNodeId: node.id, |
| 1747 | confidence: 0.9, |
| 1748 | resolvedBy: 'import', |
| 1749 | }; |
no test coverage detected