* 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 )
| 1764 | * can still try the file-based path. |
| 1765 | */ |
| 1766 | function resolveGoCrossPackageReference( |
| 1767 | ref: UnresolvedRef, |
| 1768 | imports: ImportMapping[], |
| 1769 | context: ResolutionContext |
| 1770 | ): ResolvedRef | null { |
| 1771 | const mod = context.getGoModule?.(); |
| 1772 | if (!mod) return null; |
| 1773 | |
| 1774 | // Qualified call: receiver before `.`, member after. A bare reference |
| 1775 | // (no dot) is a same-file/in-package call — handled elsewhere. |
| 1776 | const dotIdx = ref.referenceName.indexOf('.'); |
| 1777 | if (dotIdx <= 0) return null; |
| 1778 | const receiver = ref.referenceName.substring(0, dotIdx); |
| 1779 | const memberName = ref.referenceName.substring(dotIdx + 1); |
| 1780 | if (!memberName) return null; |
| 1781 | |
| 1782 | for (const imp of imports) { |
| 1783 | if (imp.localName !== receiver) continue; |
| 1784 | // Only in-module imports map to a known directory. |
| 1785 | if (imp.source !== mod.modulePath && !imp.source.startsWith(mod.modulePath + '/')) { |
| 1786 | continue; |
| 1787 | } |
| 1788 | const pkgDir = imp.source === mod.modulePath |
| 1789 | ? '' |
| 1790 | : imp.source.substring(mod.modulePath.length + 1); |
| 1791 | |
| 1792 | // Look up the member by name and pick the candidate whose file lives |
| 1793 | // directly in the package directory. Match the immediate parent dir |
| 1794 | // exactly so a call to `pkga.FuncX` doesn't accidentally land on a |
| 1795 | // `FuncX` declared in `pkga/subpkg/`. |
| 1796 | const candidates = context.getNodesByName(memberName); |
| 1797 | for (const node of candidates) { |
| 1798 | if (node.language !== 'go') continue; |
| 1799 | if (!node.isExported) continue; |
| 1800 | const fp = node.filePath.replace(/\\/g, '/'); |
| 1801 | const lastSlash = fp.lastIndexOf('/'); |
| 1802 | const fileDir = lastSlash >= 0 ? fp.substring(0, lastSlash) : ''; |
| 1803 | if (fileDir === pkgDir) { |
| 1804 | return { |
| 1805 | original: ref, |
| 1806 | targetNodeId: node.id, |
| 1807 | confidence: 0.9, |
| 1808 | resolvedBy: 'import', |
| 1809 | }; |
| 1810 | } |
| 1811 | } |
| 1812 | } |
| 1813 | return null; |
| 1814 | } |
| 1815 | |
| 1816 | /** Recursive depth cap for re-export chain following. Real codebases |
| 1817 | * rarely chain barrels more than 2–3 deep; 8 is a generous safety |
no test coverage detected