* 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 )
| 1938 | * ref) and `import static <Foo>.bar` style imports of a single member. |
| 1939 | */ |
| 1940 | function resolveJavaImportedReference( |
| 1941 | ref: UnresolvedRef, |
| 1942 | imports: ImportMapping[], |
| 1943 | context: ResolutionContext |
| 1944 | ): ResolvedRef | null { |
| 1945 | if (imports.length === 0) return null; |
| 1946 | |
| 1947 | const ext = ref.language === 'kotlin' ? '.kt' : '.java'; |
| 1948 | |
| 1949 | for (const imp of imports) { |
| 1950 | const matchesBare = imp.localName === ref.referenceName; |
| 1951 | const matchesQualified = ref.referenceName.startsWith(imp.localName + '.'); |
| 1952 | if (!matchesBare && !matchesQualified) continue; |
| 1953 | |
| 1954 | // Convert FQN to a file-path suffix. `com.example.Foo` -> |
| 1955 | // `com/example/Foo.java` (or `.kt`). The actual file may live |
| 1956 | // under any source root (`src/main/java/`, `src/`, etc.), so match |
| 1957 | // by suffix rather than exact path. |
| 1958 | const fqnPath = imp.source.replace(/\./g, '/') + ext; |
| 1959 | |
| 1960 | // Which symbol name to look up: the class itself, or a member. |
| 1961 | const memberName = matchesBare |
| 1962 | ? imp.localName |
| 1963 | : ref.referenceName.substring(imp.localName.length + 1); |
| 1964 | |
| 1965 | const candidates = context.getNodesByName(memberName); |
| 1966 | for (const node of candidates) { |
| 1967 | if (node.language !== ref.language) continue; |
| 1968 | const fp = node.filePath.replace(/\\/g, '/'); |
| 1969 | if (fp.endsWith(fqnPath) || fp.endsWith('/' + fqnPath)) { |
| 1970 | return { |
| 1971 | original: ref, |
| 1972 | targetNodeId: node.id, |
| 1973 | confidence: 0.9, |
| 1974 | resolvedBy: 'import', |
| 1975 | }; |
| 1976 | } |
| 1977 | } |
| 1978 | |
| 1979 | // `import static com.example.Foo.bar;` — the FQN's tail is the |
| 1980 | // member name, the part before is the owner class. Look up the |
| 1981 | // member named `<imp.localName>` (e.g. `bar`) and prefer the |
| 1982 | // candidate whose file matches the parent FQN's path. |
| 1983 | if (matchesBare) { |
| 1984 | const dot = imp.source.lastIndexOf('.'); |
| 1985 | if (dot > 0) { |
| 1986 | const ownerFqn = imp.source.substring(0, dot); |
| 1987 | const ownerPath = ownerFqn.replace(/\./g, '/') + ext; |
| 1988 | for (const node of candidates) { |
| 1989 | if (node.language !== ref.language) continue; |
| 1990 | const fp = node.filePath.replace(/\\/g, '/'); |
| 1991 | if (fp.endsWith(ownerPath) || fp.endsWith('/' + ownerPath)) { |
| 1992 | return { |
| 1993 | original: ref, |
| 1994 | targetNodeId: node.id, |
| 1995 | confidence: 0.9, |
| 1996 | resolvedBy: 'import', |
| 1997 | }; |
no test coverage detected