fix(javascript): preserve semantic uncertainty and lexical ownership

This commit is contained in:
morluto committed 2026-10-14 09:47:29 +08:00
1 parent 9fe54c4073
commit 896696b2e7
16 files changed
+525 -20

No files matched your search

@@ -58,6 +58,7 @@ interface CallCollectionContext {
readonly JavaScriptSemanticBindingState[]
>;
readonly ancestors: t.Node[];
readonly callableStack: JavaScriptSemanticCallable[];
}
/** Recover direct-call and lexical-capture candidates from inert syntax. */
@@ -77,6 +78,7 @@ export const collectJavaScriptSemanticCalls = (
const callableById = new Map(
callables.map((callable) => [callable.callableId, callable]),
);
const callableStack: JavaScriptSemanticCallable[] = [];
const context: CallCollectionContext = {
state,
callableById,
@@ -84,8 +86,8 @@ export const collectJavaScriptSemanticCalls = (
bindingResolutionCache: new Map(),
parameterBindingCache: new Map(),
ancestors: [],
callableStack,
};
const callableStack: JavaScriptSemanticCallable[] = [];
traverseJavaScriptAst(program, {
enter: (node, parent) => {
const callableId = semanticCallableIdForNode(node);
@@ -316,19 +318,49 @@ const collectCapture = (
)
return;
if (semanticReferenceRole(node, parent) === null) return;
const captureOwner = computedMethodKeyOwner(node, owner, context);
if (captureOwner === undefined || captureOwner.bodyScopeId === null) return;
const binding = resolveSemanticBindingState(state, node, node.name);
if (
binding === undefined ||
bindingIsWithinCallable(binding, owner.bodyScopeId, state)
bindingIsWithinCallable(binding, captureOwner.bodyScopeId, state)
)
return;
output.closureCaptures.push({
callableId: owner.callableId,
callableId: captureOwner.callableId,
bindingId: binding.bindingId,
referenceLocation: range(node),
});
};
const computedMethodKeyOwner = (
node: t.Node,
owner: JavaScriptSemanticCallable,
context: CallCollectionContext,
): JavaScriptSemanticCallable | undefined => {
const ownerNode = context.ancestors.findLast(
(candidate) => semanticCallableIdForNode(candidate) === owner.callableId,
);
if (
ownerNode === undefined ||
(!t.isObjectMethod(ownerNode) && !t.isClassMethod(ownerNode)) ||
!ownerNode.computed
)
return owner;
let child = node;
let parent = context.state.parentsByNode.get(child);
while (parent !== undefined && parent !== ownerNode) {
child = parent;
parent = context.state.parentsByNode.get(parent);
}
if (parent !== ownerNode || ownerNode.key !== child) return owner;
return enclosingFunction(
context.callableStack.filter(
({ callableId }) => callableId !== owner.callableId,
),
);
};
const collectDynamicProperty = (
node: t.Node,
owner: JavaScriptSemanticCallable | undefined,