Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
820ff83
chore: remove dead duplicate type declarations from types.ts (closes …
carlos-alm Jul 2, 2026
9fa4279
chore: remove unused iterComplexity export from complexity-query.ts (…
carlos-alm Jul 2, 2026
0f9bbe6
refactor: extract shared aggregate and typed-array helpers from leide…
carlos-alm Jul 2, 2026
f3e1119
refactor: extract shared name-map scanner into scripts/lib/name-map.mjs
carlos-alm Jul 2, 2026
e945bca
fix: replace event-loop-blocking Atomics.wait with shared sleepSync i…
carlos-alm Jul 2, 2026
4a348db
refactor: extract shared resolveFileTree helper from dataflow.ts and …
carlos-alm Jul 2, 2026
8fed8bc
refactor: extend DEFAULTS with previously-hardcoded config constants
carlos-alm Jul 2, 2026
f0a4882
refactor: move generatePlotHTML from features/graph-enrichment.ts to …
carlos-alm Jul 2, 2026
370f336
refactor: decompose extractDestructuredBindingsWalk/handleVariableDec…
carlos-alm Jul 2, 2026
ad14cf7
refactor: decompose resolveFallbackTargets/buildEdges/buildCallEdgesN…
carlos-alm Jul 2, 2026
51c3816
refactor: extract NativeOrchestrationSession from tryNativeOrchestrat…
carlos-alm Jul 2, 2026
63ab855
refactor: split embedRemote into request-executor and response-valida…
carlos-alm Jul 2, 2026
f31468c
fix: correct in-place mutation bug in applyExcludeTestsShorthand and …
carlos-alm Jul 2, 2026
57d3782
fix: correct connection-leak ordering in openReadonlyWithNative, dedu…
carlos-alm Jul 2, 2026
506e2ce
fix: add debug() logging to silent catch blocks across builder pipeli…
carlos-alm Jul 2, 2026
dbf34b8
refactor: split buildChaContext into three focused builder functions …
carlos-alm Jul 2, 2026
a1946af
refactor: split purgeAndAddReverseDeps and wire fast-skip-diag via co…
carlos-alm Jul 2, 2026
0e83ba0
refactor: extract getOrCreateBatchStmt, dedupe batch-insert helpers (…
carlos-alm Jul 2, 2026
c0c1f7d
fix: address quality issues in domain/graph/resolver (docs check ackn…
carlos-alm Jul 2, 2026
57143a8
fix: adopt buildFileConditionSQL in prepare.ts and move console.log o…
carlos-alm Jul 2, 2026
21db9a9
fix: address quality issues in graph unified model (model.ts merge() …
carlos-alm Jul 2, 2026
f7ce310
fix: address quality issues in features/complexity-query.ts (docs che…
carlos-alm Jul 2, 2026
5b708ee
fix: address quality issues in features/cochange.ts (docs check ackno…
carlos-alm Jul 2, 2026
7c3b869
fix: address quality issues in features/branch-compare.ts (docs check…
carlos-alm Jul 2, 2026
4a7f106
fix: wrap getChangedFilesBetweenRefs in branchCompareData's error bou…
carlos-alm Jul 4, 2026
320d6e9
fix: document buildFileConditionSQL's leading-AND contract in prepare…
carlos-alm Jul 4, 2026
edabe9a
fix: resolve merge conflicts with main (docs check acknowledged)
carlos-alm Jul 5, 2026
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
249 changes: 169 additions & 80 deletions src/domain/graph/resolver/points-to.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,106 +79,195 @@ function buildThisAssignmentMap(
}

/**
* Append parameter-flow and array/spread/forOf/callback constraints (Phases 8.3c and 8.3e).
* Phase 8.3c: parameter-flow constraints.
*
* Mutates `pts` (seeds array-element entries) and appends to `constraints`.
* For each call f(x) at argIndex i where f is locally defined, add
* constraint: pts(f::paramName_i) ⊇ pts(x). This makes the pts solver
* inter-procedural within the module so that `fn()` inside `f` resolves
* to the concrete function passed at each call site.
*
* Keys are scoped as "callee::paramName" to prevent name collisions: bare
* parameter names like `fn`, `cb`, and `callback` appear in many functions
* within the same file. Without scoping, pts(fn) from runA and runB would
* merge into a single set, producing spurious call edges. The scoped key is
* resolved in buildFileCallEdges by combining the enclosing caller's name
* with the call's name (see callerName::call.name lookup there).
*
* Scope: intra-module only (definitionParams contains local defs only).
*
* Appends to `constraints`.
*/
function buildParamAndArrayConstraints(
pts: PointsToMap,
function buildParamFlowConstraints(
constraints: Array<{ lhs: string; rhsKey: string }>,
paramBindings?: readonly ParamBinding[],
definitionParams?: ReadonlyMap<string, readonly string[]>,
): void {
if (!paramBindings || !definitionParams) return;
for (const { callee, argIndex, argName } of paramBindings) {
const params = definitionParams.get(callee);
if (!params || argIndex >= params.length) continue;
const paramName = params[argIndex];
if (paramName) constraints.push({ lhs: `${callee}::${paramName}`, rhsKey: argName });
}
}

/**
* Phase 8.3e: array-element bindings — seed concrete elements and wildcard.
*
* `arr[0]` etc. are seeded from literal arrays; `arr[*]` collects all elements.
*
* Mutates `pts` (seeds per-index entries) and appends to `constraints`.
*/
function buildArrayElemConstraints(
pts: PointsToMap,
constraints: Array<{ lhs: string; rhsKey: string }>,
arrayElemBindings?: readonly ArrayElemBinding[],
spreadArgBindings?: readonly SpreadArgBinding[],
forOfBindings?: readonly ForOfBinding[],
arrayCallbackBindings?: readonly ArrayCallbackBinding[],
): void {
// Phase 8.3c: parameter-flow constraints.
// For each call f(x) at argIndex i where f is locally defined, add
// constraint: pts(f::paramName_i) ⊇ pts(x). This makes the pts solver
// inter-procedural within the module so that `fn()` inside `f` resolves
// to the concrete function passed at each call site.
//
// Keys are scoped as "callee::paramName" to prevent name collisions: bare
// parameter names like `fn`, `cb`, and `callback` appear in many functions
// within the same file. Without scoping, pts(fn) from runA and runB would
// merge into a single set, producing spurious call edges. The scoped key is
// resolved in buildFileCallEdges by combining the enclosing caller's name
// with the call's name (see callerName::call.name lookup there).
//
// Scope: intra-module only (definitionParams contains local defs only).
if (paramBindings && definitionParams) {
for (const { callee, argIndex, argName } of paramBindings) {
const params = definitionParams.get(callee);
if (!params || argIndex >= params.length) continue;
const paramName = params[argIndex];
if (paramName) constraints.push({ lhs: `${callee}::${paramName}`, rhsKey: argName });
}
if (!arrayElemBindings || arrayElemBindings.length === 0) return;
for (const { arrayName, index, elemName } of arrayElemBindings) {
const elemKey = `${arrayName}[${index}]`;
const wildcardKey = `${arrayName}[*]`;
// Seed the per-index entry if the elemName is a concrete function.
if (!pts.has(elemKey)) pts.set(elemKey, new Set());
pts.get(elemKey)!.add(elemName);
// Wildcard: array[*] collects all element targets for imprecise spread/for-of.
constraints.push({ lhs: wildcardKey, rhsKey: elemKey });
}
}

// Phase 8.3e: array-element bindings — seed concrete elements and wildcard.
// `arr[0]` etc. are seeded from literal arrays; `arr[*]` collects all elements.
if (arrayElemBindings && arrayElemBindings.length > 0) {
for (const { arrayName, index, elemName } of arrayElemBindings) {
const elemKey = `${arrayName}[${index}]`;
const wildcardKey = `${arrayName}[*]`;
// Seed the per-index entry if the elemName is a concrete function.
if (!pts.has(elemKey)) pts.set(elemKey, new Set());
pts.get(elemKey)!.add(elemName);
// Wildcard: array[*] collects all element targets for imprecise spread/for-of.
constraints.push({ lhs: wildcardKey, rhsKey: elemKey });
}
/**
* Build a per-array index count from arrayElemBindings for precise
* per-index spread-argument constraints.
*/
function computeArrayMaxIndex(
arrayElemBindings: readonly ArrayElemBinding[] | undefined,
): Map<string, number> {
const arrayMaxIndex = new Map<string, number>();
for (const { arrayName, index } of arrayElemBindings ?? []) {
const cur = arrayMaxIndex.get(arrayName) ?? -1;
if (index > cur) arrayMaxIndex.set(arrayName, index);
}
return arrayMaxIndex;
}

// Phase 8.3e: spread-argument constraints.
// f(...arr) → pts[f::param_i] ⊇ pts[arr[i]] for each known element.
if (spreadArgBindings && spreadArgBindings.length > 0 && definitionParams) {
// Build a per-array index count from arrayElemBindings for precise per-index constraints.
const arrayMaxIndex = new Map<string, number>();
for (const { arrayName, index } of arrayElemBindings ?? []) {
const cur = arrayMaxIndex.get(arrayName) ?? -1;
if (index > cur) arrayMaxIndex.set(arrayName, index);
/**
* Push spread-argument constraints for one callee: precise per-element
* constraints when the source array's max index is known, otherwise a
* wildcard constraint for every parameter at/after startIndex.
*/
function pushSpreadArgConstraintsForCallee(
constraints: Array<{ lhs: string; rhsKey: string }>,
callee: string,
params: readonly string[],
arrayName: string,
startIndex: number,
maxIdx: number,
): void {
if (maxIdx >= 0) {
// Precise: per-element constraints.
for (let i = 0; i <= maxIdx; i++) {
const paramIdx = startIndex + i;
if (paramIdx >= params.length) break;
constraints.push({ lhs: `${callee}::${params[paramIdx]}`, rhsKey: `${arrayName}[${i}]` });
}

for (const { callee, arrayName, startIndex } of spreadArgBindings) {
const params = definitionParams.get(callee);
if (!params) continue;
const maxIdx = arrayMaxIndex.get(arrayName) ?? -1;
if (maxIdx >= 0) {
// Precise: per-element constraints.
for (let i = 0; i <= maxIdx; i++) {
const paramIdx = startIndex + i;
if (paramIdx >= params.length) break;
constraints.push({ lhs: `${callee}::${params[paramIdx]}`, rhsKey: `${arrayName}[${i}]` });
}
} else {
// Unknown array size: all params at/after startIndex get the wildcard.
for (let j = startIndex; j < params.length; j++) {
constraints.push({ lhs: `${callee}::${params[j]}`, rhsKey: `${arrayName}[*]` });
}
}
} else {
// Unknown array size: all params at/after startIndex get the wildcard.
for (let j = startIndex; j < params.length; j++) {
constraints.push({ lhs: `${callee}::${params[j]}`, rhsKey: `${arrayName}[*]` });
}
}
}

// Phase 8.3e: for-of iteration constraints.
// `for (const x of arr)` inside `outer` → pts[outer::x] ⊇ pts[arr[*]]
if (forOfBindings) {
for (const { varName, sourceName, enclosingFunc } of forOfBindings) {
constraints.push({ lhs: `${enclosingFunc}::${varName}`, rhsKey: `${sourceName}[*]` });
}
/**
* Phase 8.3e: spread-argument constraints.
*
* f(...arr) → pts[f::param_i] ⊇ pts[arr[i]] for each known element.
*
* Appends to `constraints`.
*/
function buildSpreadArgConstraints(
constraints: Array<{ lhs: string; rhsKey: string }>,
spreadArgBindings?: readonly SpreadArgBinding[],
arrayElemBindings?: readonly ArrayElemBinding[],
definitionParams?: ReadonlyMap<string, readonly string[]>,
): void {
if (!spreadArgBindings || spreadArgBindings.length === 0 || !definitionParams) return;
const arrayMaxIndex = computeArrayMaxIndex(arrayElemBindings);

for (const { callee, arrayName, startIndex } of spreadArgBindings) {
const params = definitionParams.get(callee);
if (!params) continue;
const maxIdx = arrayMaxIndex.get(arrayName) ?? -1;
pushSpreadArgConstraintsForCallee(constraints, callee, params, arrayName, startIndex, maxIdx);
}
}

// Phase 8.3e: Array.from / callback constraints.
// Array.from(source, cb) → pts[cb::param0] ⊇ pts[source[*]]
if (arrayCallbackBindings && definitionParams) {
for (const { sourceName, calleeName } of arrayCallbackBindings) {
const params = definitionParams.get(calleeName);
if (!params || params.length === 0) continue;
constraints.push({ lhs: `${calleeName}::${params[0]}`, rhsKey: `${sourceName}[*]` });
}
/**
* Phase 8.3e: for-of iteration constraints.
*
* `for (const x of arr)` inside `outer` → pts[outer::x] ⊇ pts[arr[*]]
*
* Appends to `constraints`.
*/
function buildForOfConstraints(
constraints: Array<{ lhs: string; rhsKey: string }>,
forOfBindings?: readonly ForOfBinding[],
): void {
if (!forOfBindings) return;
for (const { varName, sourceName, enclosingFunc } of forOfBindings) {
constraints.push({ lhs: `${enclosingFunc}::${varName}`, rhsKey: `${sourceName}[*]` });
}
}

/**
* Phase 8.3e: Array.from / callback constraints.
*
* Array.from(source, cb) → pts[cb::param0] ⊇ pts[source[*]]
*
* Appends to `constraints`.
*/
function buildArrayCallbackConstraints(
constraints: Array<{ lhs: string; rhsKey: string }>,
arrayCallbackBindings?: readonly ArrayCallbackBinding[],
definitionParams?: ReadonlyMap<string, readonly string[]>,
): void {
if (!arrayCallbackBindings || !definitionParams) return;
for (const { sourceName, calleeName } of arrayCallbackBindings) {
const params = definitionParams.get(calleeName);
if (!params || params.length === 0) continue;
constraints.push({ lhs: `${calleeName}::${params[0]}`, rhsKey: `${sourceName}[*]` });
}
}

/**
* Append parameter-flow and array/spread/forOf/callback constraints (Phases 8.3c and 8.3e).
*
* Delegates to one named helper per binding kind (buildParamFlowConstraints,
* buildArrayElemConstraints, buildSpreadArgConstraints, buildForOfConstraints,
* buildArrayCallbackConstraints) — each handler owns exactly one binding kind's
* guard + iteration + constraint-push shape, called in the same order the
* original inline blocks ran in (none of the blocks read state written by an
* earlier one, so extraction does not change solver input order).
*
* Mutates `pts` (seeds array-element entries) and appends to `constraints`.
*/
function buildParamAndArrayConstraints(
pts: PointsToMap,
constraints: Array<{ lhs: string; rhsKey: string }>,
paramBindings?: readonly ParamBinding[],
definitionParams?: ReadonlyMap<string, readonly string[]>,
arrayElemBindings?: readonly ArrayElemBinding[],
spreadArgBindings?: readonly SpreadArgBinding[],
forOfBindings?: readonly ForOfBinding[],
arrayCallbackBindings?: readonly ArrayCallbackBinding[],
): void {
buildParamFlowConstraints(constraints, paramBindings, definitionParams);
buildArrayElemConstraints(pts, constraints, arrayElemBindings);
buildSpreadArgConstraints(constraints, spreadArgBindings, arrayElemBindings, definitionParams);
buildForOfConstraints(constraints, forOfBindings);
buildArrayCallbackConstraints(constraints, arrayCallbackBindings, definitionParams);
}

/**
* Seed pts entries for object-rest parameter dispatch (Phase 8.3f).
*
Expand Down
Loading
Loading