fix(resolution): Go cross-package qualified calls resolve via go.mod (#388)#469
Merged
Conversation
…388) `pkga.FuncX(...)` calls in any non-`/internal/` Go package were dropping through the import resolver because `isExternalImport` flagged every non-relative, non-`/internal/` Go import as third-party — the resolver had no idea what the project's own module path was. The remaining candidates fell through to name-matching's path-proximity scoring, which on a layered handler/service/domain/dao codebase picks one accidental candidate per call site (~<1% recall, per issue #388's 5303-vs-1 figure). - New `src/resolution/go-module.ts` parses the `module ...` directive from the project-root `go.mod` and exposes it via `getGoModule()` on the resolution context. - `isExternalImport(go)` now returns `false` for imports that start with `<module-path>/` (or equal it). The existing `/internal/` escape hatch is preserved for projects without a parsed go.mod. - `resolveViaImport` gets a dedicated Go cross-package branch that strips the module prefix to a project-relative directory and resolves the qualified member by looking up `getNodesByName(member)` filtered to that exact directory and `isExported=true`. Sub-packages don't collide with their parents. - Go extractor now sets `isExported` from the identifier's first character (uppercase = exported, Go's universal 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 the three failure modes: in-module disambiguation with same-name funcs in two packages, aliased imports, and stdlib calls not being false-resolved to in-project nodes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
pkga.FuncX(...)cross-package calls in any Go monorepo were dropping through the import resolver and falling back to name-matching's path-proximity scoring, which on a layered codebase (handler/service/domain/dao) picks one accidental candidate per call site (~<1% recall — see issue #388's 5,303-vs-1 figure).Root cause:
isExternalImport(go)flagged any import without/internal/as third-party, because the resolver had no idea what the project's own module path was.codegraph_callers,_callees,_impact, and_traceall silently degraded to near-empty on real Go codebases.What changed
src/resolution/go-module.ts(new) — parses themodule ...directive from project-rootgo.mod, exposed viagetGoModule()onResolutionContext.isExternalImport(go)— treats<module-path>/...imports as in-module; existing/internal/escape hatch preserved.resolveViaImport— dedicated Go cross-package branch strips the module prefix to a directory and resolves the qualified member bygetNodesByName(member)filtered to that exact directory +isExported=true. Sub-packages don't collide with parents; same-name funcs in different packages don't false-merge.go.tsextractor — setsisExportedfrom the identifier's first character (Go's universal uppercase=exported convention). The resolver needs this flag to filter candidates.Measured impact
Validated on gRPC-Go (1,031
.gofiles, layered packages):callsedgescallsfmt.*/ stdlib false-resolvesAlso validated:
Convertfuncs in two packages — only the imported one resolvesimport alias "github.com/.../pkg") — resolve via alias/internal/packages (still resolve under both old and new path)Test plan
codegraph_callersseverely under-reports cross-package qualified calls (pkg.Func(...)) — <1% recall on a large Go monorepo #388 (3-pkg monorepo) — went from 0 → 5 expected edgesCloses #388.
🤖 Generated with Claude Code