From b6325f1864bed4f1d36b67db39bef245f1b4bdcc Mon Sep 17 00:00:00 2001 From: swapnil Date: Tue, 29 Sep 2026 23:54:04 -0700 Subject: [PATCH 1/2] impact: a TypeScript signature use is one row again, not a resolved duplicate Symptom: `impact` on a TypeScript type listed each function that names it in a parameter as "names it in its signature (a parameter); also: names it (METHOD_PARAM)". tests/run.py typescript/union-alias-member failed 2 of 3 checks (the alias itself, and the no-common-member control). Cause: the signature rule reads type_use and adds a lineless resolved row for every parameter or return position. It exists for languages whose type_refs carry no line (Java), where the typeref rule matches nothing and the text grep mislabelled the use. TypeScript's type_refs do carry a line, so the typeref rule already named the same signature at its site, and both rows were merged into one entry. Fix: the signature row is added only for a (callable, context) the typeref rule has not already named with a line. Done in both backends (impact.dl sig_at_line, graph_sql.py rule 273b). The text row is still dropped for any callable type_use resolves, so Java answers are unchanged. Tests: union-alias-member 3/3 on both the Datalog and SQL backends (1/3 on each before). java/signature-use-resolved-by-type-use 2/2 on both. tests/run.py --lang typescript 185/185 with the companion case fix (181/185 before); --lang java 304/304. Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com> --- plugins/axiomcode/skills/axiomcode/scripts/dl/impact.dl | 7 +++++-- plugins/axiomcode/skills/axiomcode/scripts/graph_sql.py | 9 ++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/plugins/axiomcode/skills/axiomcode/scripts/dl/impact.dl b/plugins/axiomcode/skills/axiomcode/scripts/dl/impact.dl index 600cf0fe..57d46319 100644 --- a/plugins/axiomcode/skills/axiomcode/scripts/dl/impact.dl +++ b/plugins/axiomcode/skills/axiomcode/scripts/dl/impact.dl @@ -664,8 +664,11 @@ direct(q, m, "reads", w, "resolved", "", 0) :- target(q, "field", fl, _), persis sig_words("METHOD_PARAM", "a parameter"). sig_words("METHOD_RETURN", "the return type"). .decl sig_resolved(q:symbol, c:symbol) sig_resolved(q, c) :- target(q, "type", t, _), sigtype(c, t, _, _), !inside_target(q, c). -direct(q, c, "uses", cat("names it in its signature (", w, ")"), "resolved", "", 0) :- target(q, "type", t, _), sigtype(c, t, ctx, 0), sig_words(ctx, w), !inside_target(q, c). -direct(q, c, "uses", cat("names it in its signature (a type argument of ", w, ")"), "resolved", "", 0) :- target(q, "type", t, _), sigtype(c, t, ctx, d), d > 0, sig_words(ctx, w), !inside_target(q, c). +// a signature typeref already names at its line (the rule above, by name with a site) gets no second, lineless row +.decl sig_at_line(q:symbol, c:symbol, ctx:symbol) +sig_at_line(q, c, ctx) :- target(q, "type", t, _), typ(t, n, _), typeref(c, n, ctx, _, _), sig_words(ctx, _). +direct(q, c, "uses", cat("names it in its signature (", w, ")"), "resolved", "", 0) :- target(q, "type", t, _), sigtype(c, t, ctx, 0), sig_words(ctx, w), !inside_target(q, c), !sig_at_line(q, c, ctx). +direct(q, c, "uses", cat("names it in its signature (a type argument of ", w, ")"), "resolved", "", 0) :- target(q, "type", t, _), sigtype(c, t, ctx, d), d > 0, sig_words(ctx, w), !inside_target(q, c), !sig_at_line(q, c, ctx). direct(q, c, "uses", "names it (a signature or a declaration)", "text", f, l) :- target(q, "type", t, _), typ(t, n, _), textuse(c, n, f, l), !inside_target(q, c), !sig_resolved(q, c). direct(q, c, "uses", cat("uses ", n, ", imported from it"), "by name", f, l) :- target(q, "type", _, _), importuse(c, n, f, l), !inside_target(q, c). direct(q, c, "reads", cat("calls the generated getter ", an, "()"), "by name", f, l) :- target(q, "type", t, _), gen(t, "get"), field(fl, t, _, _, _), accessor(fl, an, "read"), unresolved(c, an, k, f, l), !ctor_kind(k), !inside_target(q, c). diff --git a/plugins/axiomcode/skills/axiomcode/scripts/graph_sql.py b/plugins/axiomcode/skills/axiomcode/scripts/graph_sql.py index 549f6ebd..1424c15b 100644 --- a/plugins/axiomcode/skills/axiomcode/scripts/graph_sql.py +++ b/plugins/axiomcode/skills/axiomcode/scripts/graph_sql.py @@ -2438,8 +2438,9 @@ def direct_for_type(q, tids, at, inside, textuse, importuse, rel, code=None): holder = typeref_holder(at, aspans, {i for (i,) in q("SELECT id FROM symbols WHERE kind = 'module'")} if aspans else set()) def trefs(n): return q("SELECT name, file, line, context FROM type_refs WHERE line > 0 AND name = ?", n) - over, todo = set(), [] - sig_resolved = set() # callables whose signature type_use resolves to it (273b) # alias_over(q, a), and the aliases still to expand + over, todo = set(), [] # alias_over(q, a), and the aliases still to expand + sig_resolved = set() # callables whose signature type_use resolves to it (273b) + sig_at_line = set() # (c, ctx) rule 273 names with a line: 273b adds no second row # ── a type the container INJECTS (rule 186) ──────────────────────────────────────────────────────────── # direct(q,c,"uses",cat("receives it by dependency injection (",kind,") — …"),"resolved","",0) @@ -2566,7 +2567,8 @@ def trefs(n): return q("SELECT name, file, line, context FROM type_refs WHERE li for nm, f, l, ctx in trefs(n): c = holder(f, l) if (c, f, l) in other: continue # typeref_other: the name at that line builds another type of it - if c and c not in inside: rows.append((c, 'uses', f'names it ({ctx})', 'by name', f, l)) + if c and c not in inside: + rows.append((c, 'uses', f'names it ({ctx})', 'by name', f, l)); sig_at_line.add((c, ctx)) if c in anames and c not in inside and c not in over: over.add(c); todo.append(c) # 273b — a signature the engine RESOLVED to this type (#1422): `type_use` holds each parameter, return and # type-argument position with the type it names. Java writes no line into type_refs, so rule 273 matched none of @@ -2579,6 +2581,7 @@ def trefs(n): return q("SELECT name, file, line, context FROM type_refs WHERE li AND context IN ('METHOD_PARAM', 'METHOD_RETURN')""", t): if not c or c in inside: continue sig_resolved.add(c) + if (c, ctx) in sig_at_line: continue # rule 273 already names this signature at its line what = SIG_CTX_WORDS.get(ctx, ctx.lower()) if depth and int(depth) > 0: what = f"a type argument of {what}" rows.append((c, 'uses', f'names it in its signature ({what})', 'resolved', '', 0)) From 022202148797d9eed5cc76bb283d2964f67f5516 Mon Sep 17 00:00:00 2001 From: swapnil Date: Tue, 29 Sep 2026 23:54:04 -0700 Subject: [PATCH 2/2] tests: typescript/pipeline-array-dispatch is no longer pending MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Symptom: tests/run.py typescript/pipeline-array-dispatch failed 0 of 2 with "marked pending ... but it PASSES — remove the marker". Cause: both checks were marked pending on the gap where a function held in an array had no edge from the code that iterates it. "typescript: a function held in an array or table is run by the call that iterates or indexes it" closed that gap: `impact measure --tests` now reaches src/pipeline.test.ts and `path run widenRange` finds the one-hop dispatch chain. The case was PEND on that commit's parent and passing-while-pending from that commit on. Fix: drop both pending markers, and expect_error on the path check, which now exits 0 with a chain. Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com> --- tests/cases/typescript/pipeline-array-dispatch/case.json | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/tests/cases/typescript/pipeline-array-dispatch/case.json b/tests/cases/typescript/pipeline-array-dispatch/case.json index 7a5d03bc..f963d5ed 100644 --- a/tests/cases/typescript/pipeline-array-dispatch/case.json +++ b/tests/cases/typescript/pipeline-array-dispatch/case.json @@ -14,8 +14,7 @@ ], "avoid": [ "0 of 1 test method(s)" - ], - "pending": "#967 \u2014 a callable registered as DATA (an array element) has no edge from whoever iterates it; PEND is decided from want/avoid only, so a crash here is still a failure" + ] }, { "why": "the same silence, one hop earlier: the pipeline's own caller is the iterating function", @@ -29,9 +28,7 @@ ], "avoid": [ "no chain of resolved calls" - ], - "pending": "#967 \u2014 a callable registered as DATA (an array element) has no edge from whoever iterates it; PEND is decided from want/avoid only, so a crash here is still a failure", - "expect_error": true + ] } ] -} \ No newline at end of file +}