* 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 )
| 2012 | * can still try the file-based path. |
| 2013 | */ |
| 2014 | function resolveGoCrossPackageReference( |
| 2015 | ref: UnresolvedRef, |
| 2016 | imports: ImportMapping[], |
| 2017 | context: ResolutionContext |
| 2018 | ): ResolvedRef | null { |
| 2019 | const mod = context.getGoModule?.(); |
| 2020 | if (!mod) return null; |
| 2021 | |
| 2022 | // Qualified call: receiver before `.`, member after. A bare reference |
| 2023 | // (no dot) is a same-file/in-package call — handled elsewhere. |
| 2024 | const dotIdx = ref.referenceName.indexOf('.'); |
| 2025 | if (dotIdx <= 0) return null; |
| 2026 | const receiver = ref.referenceName.substring(0, dotIdx); |
| 2027 | const memberName = ref.referenceName.substring(dotIdx + 1); |
| 2028 | if (!memberName) return null; |
| 2029 | |
| 2030 | for (const imp of imports) { |
| 2031 | if (imp.localName !== receiver) continue; |
| 2032 | // Only in-module imports map to a known directory. |
| 2033 | if (imp.source !== mod.modulePath && !imp.source.startsWith(mod.modulePath + '/')) { |
| 2034 | continue; |
| 2035 | } |
| 2036 | const pkgDir = imp.source === mod.modulePath |
| 2037 | ? '' |
| 2038 | : imp.source.substring(mod.modulePath.length + 1); |
| 2039 | |
| 2040 | // Look up the member by name and pick the candidate whose file lives |
| 2041 | // directly in the package directory. Match the immediate parent dir |
| 2042 | // exactly so a call to `pkga.FuncX` doesn't accidentally land on a |
| 2043 | // `FuncX` declared in `pkga/subpkg/`. |
| 2044 | const candidates = context.getNodesByName(memberName); |
| 2045 | for (const node of candidates) { |
| 2046 | if (node.language !== 'go') continue; |
| 2047 | if (!node.isExported) continue; |
| 2048 | const fp = node.filePath.replace(/\\/g, '/'); |
| 2049 | const lastSlash = fp.lastIndexOf('/'); |
| 2050 | const fileDir = lastSlash >= 0 ? fp.substring(0, lastSlash) : ''; |
| 2051 | if (fileDir === pkgDir) { |
| 2052 | return { |
| 2053 | original: ref, |
| 2054 | targetNodeId: node.id, |
| 2055 | confidence: 0.9, |
| 2056 | resolvedBy: 'import', |
| 2057 | }; |
| 2058 | } |
| 2059 | } |
| 2060 | } |
| 2061 | return null; |
| 2062 | } |
| 2063 | |
| 2064 | /** Recursive depth cap for re-export chain following. Real codebases |
| 2065 | * rarely chain barrels more than 2–3 deep; 8 is a generous safety |
no test coverage detected