* Resolve a Go cross-package qualified reference (`pkga.FuncX`) by matching * the package alias against an in-module import, stripping the module prefix * to a project-relative directory, and locating the exported symbol in any * `.go` file under that directory. Returns `null` for stdlib / third-
( ref: UnresolvedRef, imports: ImportMapping[], context: ResolutionContext )
| 2286 | * can still try the file-based path. |
| 2287 | */ |
| 2288 | function resolveGoCrossPackageReference( |
| 2289 | ref: UnresolvedRef, |
| 2290 | imports: ImportMapping[], |
| 2291 | context: ResolutionContext |
| 2292 | ): ResolvedRef | null { |
| 2293 | const mod = context.getGoModule?.(); |
| 2294 | if (!mod) return null; |
| 2295 | |
| 2296 | // Qualified call: receiver before `.`, member after. A bare reference |
| 2297 | // (no dot) is a same-file/in-package call — handled elsewhere. |
| 2298 | const dotIdx = ref.referenceName.indexOf('.'); |
| 2299 | if (dotIdx <= 0) return null; |
| 2300 | const receiver = ref.referenceName.substring(0, dotIdx); |
| 2301 | const memberName = ref.referenceName.substring(dotIdx + 1); |
| 2302 | if (!memberName) return null; |
| 2303 | |
| 2304 | for (const imp of imports) { |
| 2305 | if (imp.localName !== receiver) continue; |
| 2306 | // Only in-module imports map to a known directory. |
| 2307 | if (imp.source !== mod.modulePath && !imp.source.startsWith(mod.modulePath + '/')) { |
| 2308 | continue; |
| 2309 | } |
| 2310 | const pkgDir = imp.source === mod.modulePath |
| 2311 | ? '' |
| 2312 | : imp.source.substring(mod.modulePath.length + 1); |
| 2313 | |
| 2314 | // Look up the member by name and pick the candidate whose file lives |
| 2315 | // directly in the package directory. Match the immediate parent dir |
| 2316 | // exactly so a call to `pkga.FuncX` doesn't accidentally land on a |
| 2317 | // `FuncX` declared in `pkga/subpkg/`. |
| 2318 | const candidates = context.getNodesByName(memberName); |
| 2319 | for (const node of candidates) { |
| 2320 | if (node.language !== 'go') continue; |
| 2321 | if (!node.isExported) continue; |
| 2322 | const fp = node.filePath.replace(/\\/g, '/'); |
| 2323 | const lastSlash = fp.lastIndexOf('/'); |
| 2324 | const fileDir = lastSlash >= 0 ? fp.substring(0, lastSlash) : ''; |
| 2325 | if (fileDir === pkgDir) { |
| 2326 | return { |
| 2327 | original: ref, |
| 2328 | targetNodeId: node.id, |
| 2329 | confidence: 0.9, |
| 2330 | resolvedBy: 'import', |
| 2331 | }; |
| 2332 | } |
| 2333 | } |
| 2334 | } |
| 2335 | return null; |
| 2336 | } |
| 2337 | |
| 2338 | /** Recursive depth cap for re-export chain following. Real codebases |
| 2339 | * rarely chain barrels more than 2–3 deep; 8 is a generous safety |
no test coverage detected