Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions src/pipeline/registry.c
Original file line number Diff line number Diff line change
Expand Up @@ -618,9 +618,19 @@ static cbm_resolution_t resolve_import_map(const cbm_registry_t *r, const char *
* function name and suffix is NULL, so the target QN must be
* resolved.requireAdmin — not just resolved, which would point at the
* module node and miss the function entirely. */
const char *direct = cbm_ht_get_key(r->exact, resolved);
if (direct) {
return (cbm_resolution_t){direct, "import_map", CONF_IMPORT_MAP, REG_RESOLVED};
/* Direct hit ONLY for suffix-less callees (an aliased direct-symbol
* import called bare: `from m import f as g; g()` — #875/#979). With a
* suffix present (`imported.method()`), returning the bare base here
* would swallow the suffix and bind the call to the imported symbol's
* own node (a Variable/Class/module) instead of base.method — exactly
* the mis-resolution the comment above warns about. That regressed
* django-scale graphs by ~11K CALLS/TESTS edges (Signal.send calls
* degraded to edges onto the signal variables themselves). #1000 */
if (!suffix || !suffix[0]) {
const char *direct = cbm_ht_get_key(r->exact, resolved);
if (direct) {
return (cbm_resolution_t){direct, "import_map", CONF_IMPORT_MAP, REG_RESOLVED};
}
}
char candidate[CBM_SZ_512];
if (suffix && suffix[0]) {
Expand Down
23 changes: 23 additions & 0 deletions tests/test_registry.c
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,28 @@ TEST(tsjs_suppress_keeps_high_confidence_and_non_methods) {

/* ── Suite ─────────────────────────────────────────────────────── */

/* Method call THROUGH an imported symbol that is itself an indexed node
* (`from m import sig; sig.send()`). The import-map value is the symbol QN
* and the callee carries a suffix — resolution must return symbol.send, NOT
* the bare symbol node. The #979 direct-hit early return swallowed the
* suffix whenever the base symbol existed as an exact node, degrading
* django-scale graphs by ~11K CALLS/TESTS edges (e.g. every
* `user_logged_in.send(...)` bound to the signal VARIABLE instead of
* Signal.send). Regression guard for #1000. */
TEST(resolve_import_map_alias_with_suffix_hits_method) {
cbm_registry_t *r = cbm_registry_new();
cbm_registry_add(r, "user_logged_in", "proj.auth.signals.user_logged_in", "Variable");
cbm_registry_add(r, "send", "proj.auth.signals.user_logged_in.send", "Method");
const char *keys[] = {"user_logged_in"};
const char *vals[] = {"proj.auth.signals.user_logged_in"};
cbm_resolution_t res =
cbm_registry_resolve(r, "user_logged_in.send", "proj.auth.views", keys, vals, 1);
ASSERT_STR_EQ(res.qualified_name, "proj.auth.signals.user_logged_in.send");
ASSERT_STR_EQ(res.strategy, "import_map");
cbm_registry_free(r);
PASS();
}

SUITE(registry) {
/* FQN */
RUN_TEST(fqn_simple);
Expand Down Expand Up @@ -838,6 +860,7 @@ SUITE(registry) {
RUN_TEST(resolve_import_map);
RUN_TEST(resolve_import_map_bare_function);
RUN_TEST(resolve_import_map_bare_alias);
RUN_TEST(resolve_import_map_alias_with_suffix_hits_method);
RUN_TEST(resolve_unique_name);
RUN_TEST(resolve_unresolved);
RUN_TEST(resolve_many_nodes);
Expand Down
Loading