`pkga.FuncX(...)` cross-package calls in Go monorepos were dropping through the import resolver — `isExternalImport(go)` flagged any non-`/internal/` import as third-party because the resolver had no idea what the project's own module path was. Resolution fell back to name matching with path-proximity scoring, which on a layered codebase picks one accidental candidate per call site (~<1% recall per #388's 5,303-vs-1 figure). - `src/resolution/go-module.ts` (new) parses the `module ...` directive from project-root `go.mod`, exposed via `getGoModule()` on `ResolutionContext`. - `isExternalImport(go)` treats `<module-path>/...` imports as in-module; the existing `/internal/` escape hatch is preserved for repos without a parsed go.mod. - `resolveViaImport` gets a Go cross-package branch that strips the module prefix to a project-relative directory, then resolves the qualified member via `getNodesByName(member)` filtered to that exact directory and `isExported=true`. Sub-packages don't collide with their parents; same-name funcs in different packages don't false-merge. - Go extractor sets `isExported` from the identifier's first character (Go's universal uppercase=exported convention). The resolver depends on this to filter candidates. Validation on gRPC-Go (1,031 .go files, layered package tree): total `calls` edges: 23,803 -> 34,105 (+43%) cross-pkg `calls`: 10,880 -> 19,929 (+83%) fmt/strconv/etc. stdlib calls: stay external (no false positives) Tests cover in-module disambiguation with same-name funcs in two packages, aliased imports, and stdlib calls not being false-resolved to in-project nodes. Closes #388.
This commit is contained in:
@@ -21,6 +21,7 @@ import { resolveViaImport, extractImportMappings, extractReExports } from './imp
|
||||
import { detectFrameworks } from './frameworks';
|
||||
import { synthesizeCallbackEdges } from './callback-synthesizer';
|
||||
import { loadProjectAliases, type AliasMap } from './path-aliases';
|
||||
import { loadGoModule, type GoModule } from './go-module';
|
||||
import { logDebug } from '../errors';
|
||||
import type { ReExport } from './types';
|
||||
import { LRUCache } from './lru-cache';
|
||||
@@ -157,6 +158,8 @@ export class ReferenceResolver {
|
||||
// `null` = computed and absent. Treated as immutable for the
|
||||
// resolver's lifetime; callers re-create the resolver if config changes.
|
||||
private projectAliases: AliasMap | null | undefined = undefined;
|
||||
// go.mod module path. Same lazy/immutable convention as projectAliases.
|
||||
private goModule: GoModule | null | undefined = undefined;
|
||||
|
||||
constructor(projectRoot: string, queries: QueryBuilder) {
|
||||
this.projectRoot = projectRoot;
|
||||
@@ -370,6 +373,13 @@ export class ReferenceResolver {
|
||||
return this.projectAliases;
|
||||
},
|
||||
|
||||
getGoModule: () => {
|
||||
if (this.goModule === undefined) {
|
||||
this.goModule = loadGoModule(this.projectRoot);
|
||||
}
|
||||
return this.goModule;
|
||||
},
|
||||
|
||||
getReExports: (filePath: string, language) => {
|
||||
const cached = this.reExportCache.get(filePath);
|
||||
if (cached) return cached;
|
||||
|
||||
Reference in New Issue
Block a user