Skip to content

fix(graph): key IMPORTS edge dedup on local_name, not just source/target#770

Open
apappas1129 wants to merge 2 commits into
DeusData:mainfrom
apappas1129:fix/768-imports-multi-symbol-dedup
Open

fix(graph): key IMPORTS edge dedup on local_name, not just source/target#770
apappas1129 wants to merge 2 commits into
DeusData:mainfrom
apappas1129:fix/768-imports-multi-symbol-dedup

Conversation

@apappas1129

@apappas1129 apappas1129 commented Jul 2, 2026

Copy link
Copy Markdown

What does this PR do?

Closes #768: a single import { A, B } from './lib' statement produced exactly
one IMPORTS edge instead of two — whichever symbol got written second
silently overwrote the first.

cbm_gbuf_insert_edge() (src/graph_buffer/graph_buffer.c) dedups edges on
(source_id, target_id, type) only. Two named imports from the same specifier
resolve to the same (source_file, target_file) pair, so the second
IMPORTS edge collides with the first on that key. On collision the function
doesn't merge or reject — it replaces properties_json outright (the
existing /* Merge properties (just replace for now) */ comment suggests
this was a known stub).

This has a bigger blast radius than "who imports X" queries: pass_calls.c,
pass_usages.c, pass_semantic.c, and pass_lsp_cross.c all build their
local_name -> resolved module QN maps for cross-file call resolution by
parsing exactly one local_name out of each IMPORTS edge's properties. So
the dropped symbol's calls also failed to resolve cross-file, not just the
import edge itself.

make_edge_key() now folds local_name into the dedup key specifically for
IMPORTS edges, so two different symbols imported from the same specifier
become two distinct edges. A re-inserted identical import (e.g. an idempotent
re-index) still dedups to one edge. No changes were needed in any of the four
downstream consumers — they already iterate edges and parse one local_name
per edge; they simply see both edges now that dedup stops collapsing them.
Other edge types (CALLS, HTTP_CALLS, DEFINES, ...) keep their original
(source, target, type) key, unchanged.

Checklist

  • Every commit is signed off (git commit -s)
  • Tests pass locally (make -f Makefile.cbm test) — full suite green
    except one pre-existing, unrelated RSS-ceiling test in
    test_incremental.c (~2.6GB vs 2048MB limit) on this machine
  • Lint passes (make -f Makefile.cbm lint-ci) — could not verify
    locally, cppcheck/clang-format are not installed in this
    environment; relying on CI
  • New behavior is covered by a test: added gbuf_imports_multi_symbol_dedup
    in tests/test_graph_buffer.c, asserting two same-target imports with
    different local_name produce distinct edges while a repeated
    identical import still dedups to one

Two named imports from the same specifier resolve to the same
(source_file, target_file) pair, so cbm_gbuf_insert_edge's dedup key
of (source_id, target_id, type) collapsed them into one edge - the
second import's properties_json silently replaced the first's,
dropping whichever symbol lost the write race.

This breaks more than "who imports X" queries: pass_calls.c,
pass_usages.c, pass_semantic.c, and pass_lsp_cross.c all build their
local_name -> resolved module QN maps for cross-file call resolution
by parsing exactly one local_name out of each file's IMPORTS edges, so
the dropped symbol's calls also failed to resolve cross-file.

make_edge_key now folds local_name into the key for IMPORTS edges
specifically, so two different symbols from the same specifier become
two distinct edges while a re-inserted identical import (idempotent
re-index) still dedups to one. Other edge types keep their original
(source, target, type) key unchanged.

Closes DeusData#768.

Signed-off-by: Alexandros Pappas <11921291+apappas1129@users.noreply.github.com>
@apappas1129 apappas1129 requested a review from DeusData as a code owner July 2, 2026 13:29
Signed-off-by: Alexandros Pappas <11921291+apappas1129@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.

Multiple named imports from one specifier produce only a single IMPORTS edge — sibling symbols are invisible to the graph

1 participant