mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-19 14:32:59 +08:00
fix(ts/js): scope a variable's initializer walk to the variable (#693 for TS/JS)
Two defects in one place — extractVariable's JS-family branch. 1. The initializer walk ran with only the FILE on the node stack, so `const cfg = loadConfig()` recorded the FILE as loadConfig's caller. That is literally the leak #693 described and #744 fixed for Go; the closing note there said "JS/TS didn't have this gap", which holds only for the `const f = () => …` shape (an arrow value delegates to extractFunction and gets its own node). 2. Object literals were excluded from the walk outright, on the grounds that their function-valued members are extracted individually below — but that only happens for EXPORTED consts. `const obj = { handler: () => target() }` therefore contributed nothing at all: no member node, and no call edge to anything. Fix: walk the initializer with the declared symbol pushed on the stack, and skip only the shapes whose members really are extracted one-by-one (exported object-of-functions, RTK endpoints, Pinia setup, Vue store collections) — walking those too would double-count each member arrow's calls. Two follow-on repairs the change surfaced: - CFML `<cfscript>` bodies are delegated to a separate extractor as if they were a whole module, so a `var x = helper()` local inside a `<cffunction>` minted a top-level variable node and the walk attributed `helper` to it. Those snippet-top-level non-callables are locals of the enclosing function; their refs now redirect to it, like the snippet's file-attributed refs already did. - codegraph_explore stopped listing a synthesized dynamic-dispatch link once the same pair also had a static call edge: it asked getCallers/getCallees, which return one row per NEIGHBOUR (the #1086 de-dup), so the static edge hid the heuristic one. It now asks the edges directly. This is exactly the RTK thunk case — `dispatch(innerThunk(n))` inside a thunk initializer is now a real static edge, which is the point, but the synthesized hop must still show up in the summary. Measured on three independent TypeScript trees (codegraph's own src/, evcc-ng, gv-grx — 499 files): 0 lost nodes/edges/refs, 780 refs re-attributed from the file node to the declaring constant, 228 genuinely new refs from object literals, node and edge counts unchanged. Kernel parity holds (324/324 non-deferred files byte-identical).
This commit is contained in:
@@ -398,18 +398,26 @@ impl<'t> Walker<'t> {
|
||||
}
|
||||
}
|
||||
|
||||
// Walk the initializer for calls — except the object/store shapes
|
||||
// whose members are extracted method-by-method below.
|
||||
// Walk the initializer for calls, ATTRIBUTED to the declared symbol
|
||||
// (#693) — except the object/store shapes whose members are
|
||||
// extracted method-by-method below (walking those too would
|
||||
// double-count each member arrow's calls). Before this the walk ran
|
||||
// with only the FILE on the stack (`const cfg = load()` recorded the
|
||||
// file as load's caller) and object literals were skipped outright.
|
||||
let members_extracted_separately = extract_object_methods
|
||||
|| rtk_endpoints.is_some()
|
||||
|| pinia_setup.is_some()
|
||||
|| !store_collections.is_empty();
|
||||
if let Some(v) = value {
|
||||
let vk = v.kind();
|
||||
if vk != "object"
|
||||
&& vk != "object_expression"
|
||||
&& !(extract_object_methods && vk == "call_expression")
|
||||
&& rtk_endpoints.is_none()
|
||||
&& pinia_setup.is_none()
|
||||
&& store_collections.is_empty()
|
||||
{
|
||||
self.visit_function_body(v);
|
||||
if !members_extracted_separately {
|
||||
match var_row {
|
||||
Some(row) => {
|
||||
self.stack.push(Scope { row, kind, name: name.clone() });
|
||||
self.visit_function_body(v);
|
||||
self.stack.pop();
|
||||
}
|
||||
None => self.visit_function_body(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user