From 3e7d9a8800e76b112d5d0d2cca2cd4c9cc5611a3 Mon Sep 17 00:00:00 2001 From: swapnil Date: Wed, 30 Sep 2026 00:00:57 -0700 Subject: [PATCH] path: a by-name lead on a value-callee site no longer hides the unknown verdict Symptom: path Svc.login Store.find, where Svc.login calls this.find() on a field holding promisify(s.find.bind(s)) with s untyped, printed "WOULD connect ... receiver not typed" and never said the connection is unknown or that the callee is a function stored in a field. path viaModule / Auth.login Store.find reported known_edge where the case wanted multi_inferred. Cause: two changes to the same shape landed together. The promisify rules made util.promisify(f) transparent (its value is what f holds) and dropped its platform value, so the wrapper-holder rules keyed on that platform value no longer fire. With a typed bound method the call now resolves straight to it (known_edge, one declaration); with an untyped one the call is an ordinary unresolved site, which the by-name search bridges and returns on before the value-callee report runs, although the engine still marks the site as a field value callee. Fix: the by-name branch skips a chain whose every bridge site is a value callee, so the value-callee report (UNKNOWN, the site, "named like the target") answers. The case now expects known_edge for the two resolved wrappers. Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com> --- .../axiomcode/skills/axiomcode/scripts/axiomcode-path | 10 ++++++++++ .../javascript/stored-field-callee-shapes/case.json | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-path b/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-path index 81a60bf30..98ac6f2af 100755 --- a/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-path +++ b/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-path @@ -1854,6 +1854,16 @@ def path(g, a, b, show_all=False, limit=10, every=False, max_paths=20): m, d = hits[0]; chain = read_back(res['parent_opt'], q, m, srcs) if not chain: continue bridges = [(chain[i][0], chain[i + 1][0]) for i in range(len(chain) - 1) if chain[i + 1][1] == 'by-name'] + # A bridge whose every site calls a VALUE (a field, a variable, a parameter) is not a receiver that lacks a + # type: what it holds decides the callee, and the value-callee report below says so and names the site. + if bridges and g.has('ext_unresolved_value_callee') and all( + g.q("SELECT 1 FROM call_sites s JOIN unresolved_sites u ON u.call_site_id = s.id WHERE s.caller_id = ?" + " AND s.callee_name = ? LIMIT 1", c, g.sym[m2]['name']) + and not g.q("SELECT 1 FROM call_sites s JOIN unresolved_sites u ON u.call_site_id = s.id" + " WHERE s.caller_id = ? AND s.callee_name = ?" + " AND s.id NOT IN (SELECT c0 FROM ext_unresolved_value_callee) LIMIT 1", c, g.sym[m2]['name']) + for c, m2 in bridges): + continue print(f" {word} WOULD connect in {d} hop(s) if {len(bridges)} unresolved call(s) resolve the way their name suggests — look at:") for c, m2 in bridges: sites = g.q("SELECT s.file_path f, s.start_line ln FROM call_sites s JOIN unresolved_sites u ON u.call_site_id = s.id WHERE s.caller_id = ? AND s.callee_name = ?", c, g.sym[m2]['name']) diff --git a/tests/cases/javascript/stored-field-callee-shapes/case.json b/tests/cases/javascript/stored-field-callee-shapes/case.json index 12284de97..fadadc0c5 100644 --- a/tests/cases/javascript/stored-field-callee-shapes/case.json +++ b/tests/cases/javascript/stored-field-callee-shapes/case.json @@ -241,7 +241,7 @@ "Store.find" ], "want": [ - "→ [multi_inferred · call @ src/shapes.js:44] Store.find" + "→ [known_edge · call @ src/shapes.js:44] Store.find" ], "avoid": [ "the two are independent in this graph" @@ -255,7 +255,7 @@ "Store.find" ], "want": [ - "→ [multi_inferred · call @ src/shapes.js:45] Store.find" + "→ [known_edge · call @ src/shapes.js:45] Store.find" ], "avoid": [] },