Skip to content

fix(go): resolve cross-package field-chain calls via type dispatch - #1939

Open
xkchok wants to merge 4 commits into
DeusData:mainfrom
xkchok:fix/go-cross-package-field-dispatch
Open

fix(go): resolve cross-package field-chain calls via type dispatch#1939
xkchok wants to merge 4 commits into
DeusData:mainfrom
xkchok:fix/go-cross-package-field-dispatch

Conversation

@xkchok

@xkchok xkchok commented Aug 30, 2026

Copy link
Copy Markdown

Summary

Go calls written through an aliased cross-package struct field (h.svc.Method(ctx, req)) previously fell through to fuzzy suffix matching instead of type dispatch. Three defects caused it:

  1. Dispatch walk bypassed — the per-file Go dispatch walk in cbm_pxc_dispatch_file was bypassed, so field selectors were never descended.
  2. Struct fields dropped — fields extracted as flat Field defs were discarded by the LSP label map, so every Go struct registered with zero fields in the cross-file registry.
  3. Import-alias receiver QNs unresolvable — type texts written through aliases produced qualified names that exist nowhere in the registry.

Changes

  • internal/cbm/lsp/go_lsp.c — restore the Go field-chain walk: descend receiver.field selectors through the shared prebuilt registry and emit lsp_type_dispatch / lsp_embed_dispatch edges; add go_requalify_via_imports to re-qualify alias segments through the calling file's import map when the direct receiver-type lookup misses.
  • internal/cbm/extract_defs.c + src/pipeline/pass_lsp_cross.c — fold per-field Field defs into their owning struct inside cbm_pxc_collect_all_defs (one site covers both the prebuilt-registry path and the per-file fallback).
  • tests/test_parallel.c — regression: a cross-package field chain resolves end to end with an lsp_type_dispatch edge from the parallel resolver.
  • tests/test_go_lsp.c — regression: an alias-typed struct field re-qualifies through the import map and lands on the real receiver type.

Separate chore commit (drop it if unwanted): env-gated dispatch tracing (CBM_DISPATCH_TRACE / CBM_TRACE_FILE, file sink) in go_lsp.c, since resolve-worker stderr is swallowed by the daemon supervisor. No behavior change when unset.

Test plan

  • make test-runner clean build
  • ./build/test-runner parallel go_lsp — 125 passed, regression fixtures dispatch as expected
  • ./scripts/build.sh clean production build

Verified scope

  • Synthetic two-package Go fixture: 49/49 handler→service field-chain calls resolve at 0.95 confidence via type dispatch (previously fuzzy match 0.33).
  • Known gap, separate issue: the same calls regress to fuzzy match when the module is indexed nested inside an umbrella monorepo root (receiver-QN reconstruction from nested module dirs), and calls through generated client factories (service-guard structs) still yield no cross-service edges.

xkchok added 2 commits August 30, 2026 21:03
Go calls written through an aliased struct field (h.svc.Method(ctx,
req)) previously fell through to fuzzy suffix matching because:

- the per-file Go dispatch walk was bypassed, so field selectors were
  never descended;
- struct fields extracted as flat "Field" defs were dropped by the LSP
  label map, so every Go struct registered with zero fields;
- receiver types written through import aliases (svc.Svc) produced
  qualified names that exist nowhere in the project registry.

Changes:

- restore the Go walk in cbm_pxc_dispatch_file and descend field
  selectors through the shared prebuilt registry, emitting
  lsp_type_dispatch edges;
- fold per-field defs into their owning struct inside
  cbm_pxc_collect_all_defs (one site covers both the prebuilt-registry
  path and the per-file fallback);
- re-qualify the last alias segment through the calling file's import
  map when the direct receiver-type lookup misses
  (go_requalify_via_imports).

Tests:

- tests/test_parallel.c: cross-package field chain resolves end to end
  with an lsp_type_dispatch edge in the parallel resolver;
- tests/test_go_lsp.c: alias-typed struct field re-qualifies through
  the import map and lands on the real receiver type.

Signed-off-by: xkchok <143325001+xkchok@users.noreply.github.com>
The Go resolve worker's stderr is swallowed by the daemon supervisor,
so dispatch diagnostics are unreachable. Add a file-sink trace gated by
CBM_DISPATCH_TRACE (path via CBM_TRACE_FILE, default
/tmp/cbm_dispatch_trace.txt, append): emits one line per NAMED-receiver
dispatch attempt, successful emit, and failure reason. Aids upstream
debugging of receiver-QN/lookup mismatches without changing behavior
when unset.

Signed-off-by: xkchok <143325001+xkchok@users.noreply.github.com>
@xkchok
xkchok force-pushed the fix/go-cross-package-field-dispatch branch from d4416bc to 54cfae5 Compare August 30, 2026 13:03
@github-actions

Copy link
Copy Markdown

Thanks for opening this — it has been seen, and it is queued.

This note is automated, but it is not a brush-off: it exists so you know where your PR stands instead of having to guess from silence.

Current review status: working through a backlog. 0.9.1-rc.1 is out, so the release freeze that held reviews is over — but it left a large queue of open pull requests behind it, and we are reading through them oldest-first. The background is in discussion #1144.

What that means for this PR, concretely:

  • It will not be closed for inactivity. No stale bot touches pull requests here.
  • It may still sit a while before a human reads it. That is on us, not on you.
  • Older PRs are read first, so a recent one is not being skipped — it is behind a queue.

Things that will genuinely speed it up whenever review does happen:

  • Keep it rebased on main — the tree is moving quickly right now, and a conflicting branch cannot be reviewed as the diff you intended.
  • Get CI green, or say which failures you believe are pre-existing.
  • Keep the change to one claim. Bundled features and refactors get split before they get merged, which costs you a round trip.
  • Every commit needs a sign-off (git commit -s) — CI enforces DCO.

If this fixes a bug, a reproduction we can run is worth more than a description of the symptom.

Thanks for contributing, and sorry in advance for the wait.

The manual wrap disagreed with clang-format's packing heuristic: with
two parameters still fitting under the 100-column limit, the formatter
packs them onto the first line rather than breaking after the first
argument. Fix the wrap so the PR cleanly passes the ci lint gate.

Signed-off-by: xkchok <143325001+xkchok@users.noreply.github.com>
run_go_field_chain_sequential tears down the results, registry and
cross arenas but never calls harness_ctx_free_tables, so pass_calls'
CBMReturnTypeTable (strdup'd method return types) leaked whenever the
helper ran. ASan builds caught it: AddressSanitizer: 32911 byte(s)
leaked in 6 allocation(s) from build_return_type_table, failing
test-diag, test-lsan-macos and both gcc sanitizer shards. Verify
locally: build/c/test-runner parallel -> 73 passed, no leaks.

Signed-off-by: xkchok <143325001+xkchok@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant