fix(native): add dispatch-table PTS resolution in JS extractor#1690
Conversation
…ch WASM
WASM resolves `({a:fnA,b:fnB})[key]()` by seeding arrayElemBindings under a
synthetic `<dt_line_col>` name and emitting a `<dt_line_col>[*]` call, allowing
the PTS solver to expand the wildcard to each concrete target.
Native was falling through to the generic `<dynamic:computed-key>` path and
emitting a file-sink edge instead. Add `extract_dispatch_table_call` in the
native JS extractor (mirroring WASM `extractSubscriptCallInfo` RES-2 branch)
and call it from `handle_call_expr` before the `extract_call_info` fallback.
Also adds two unit tests that lock in the behaviour.
Closes #1684
Greptile SummaryThis PR adds
Confidence Score: 4/5Safe to merge; the dispatch-table path is well-contained, falls back correctly for non-matching patterns, and is verified by the parity and benchmark test suites. The new function correctly intercepts inline object-literal subscript calls, builds array-elem bindings, and emits the expected wildcard call. The core resolution path is unaffected for all other call patterns. Two minor issues exist: crates/codegraph-core/src/extractors/javascript.rs — specifically the Important Files Changed
Reviews (1): Last reviewed commit: "fix(native): add dispatch-table PTS reso..." | Re-trigger Greptile |
| "pair" => { | ||
| if let Some(val) = child.child_by_field_name("value") { | ||
| if val.kind() == "identifier" { | ||
| let text = node_text(&val, source); | ||
| if !JS_BUILTIN_GLOBALS.contains(&text) { | ||
| array_elem_bindings.push(ArrayElemBinding { | ||
| array_name: table_name.clone(), | ||
| index: idx, | ||
| elem_name: text.to_string(), | ||
| }); | ||
| idx += 1; | ||
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
idx not incremented for non-identifier pair values
When a pair's value is a non-identifier expression (e.g., an arrow function or a literal like {a: () => {}, b: fnB}), idx is not incremented, so fnB gets index 0 instead of 1. The existing collect_array_elem_bindings (line ~3272) always increments idx for every non-punctuation element regardless of whether it contributes a binding, preserving positional accuracy. Here indices are consecutive only over identifier-valued entries.
This doesn't affect [*]-wildcard resolution (the PTS solver generates a constraint for every entry regardless of its index), but it does affect the spread_arg_bindings codepath in build_edges.rs (lines 251-278): that path iterates 0..=array_max_index to map positional spread parameters, and array_max_index is derived from the highest index stored in array_elem_bindings. If any future caller seeds a spread-arg binding against a dispatch-table name, the index gap would silently misalign parameter slots.
| "function fnA() {}\n\ | ||
| function fnB() {}\n\ | ||
| function run(k) { ({a: fnA, b: fnB})[k](); }", | ||
| ); | ||
| let dt_call = s.calls.iter().find(|c| c.name.starts_with("<dt_") && c.name.ends_with(">[*]")); | ||
| assert!(dt_call.is_some(), "dispatch-table call missing for parenthesized object; got: {:?}", s.calls); | ||
| } | ||
| } |
There was a problem hiding this comment.
Second test covers the same pattern as the first
Both dispatch_table_emits_dt_call_and_array_elem_bindings and dispatch_table_parenthesized_object_also_works use a parenthesized object literal ({...})[k](). The second test's name implies it specifically verifies the parenthesized path, but so does the first — there is no test for the unparenthesized form obj_literal[k]() where the subscript object is a bare object node (which can appear in non-statement expression contexts, e.g., as an argument). The non-parenthesized branch in extract_dispatch_table_call goes through else { obj } and is never exercised by the current test suite.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Summary
({a:fnA,b:fnB})[key]()dispatch-table calls instead of resolving todtFn1/dtFn2extract_dispatch_table_callincrates/codegraph-core/src/extractors/javascript.rs— mirrors WASMextractSubscriptCallInfoRES-2 branch (src/extractors/javascript.ts:3196–3233)arrayElemBindingsunder a synthetic<dt_line_col>name and emit<dt_line_col>[*]so the PTS solver resolves to each targetVerification
All 8
build-paritytests pass, all 5pts-javascriptresolution benchmark tests pass.Closes #1684
Test plan
cargo test -p codegraph-core dispatch_table— 2 new tests passnode scripts/parity-compare.mjs— pts-javascript PARITY OKnpx vitest run tests/benchmarks/resolution/resolution-benchmark.test.ts -t "pts-javascript"— 5/5 passnpx vitest run tests/integration/build-parity.test.ts— 8/8 pass