fix(javascript): preserve uncertain with-scope member writes (#7914)

* fix(javascript): preserve uncertain with-scope member writes

* fix: retain nested initializer candidates

* fix(javascript): admit initializer values in worker transport

* fix(tests): remove duplicate MCP test imports
This commit is contained in:
morluto authored and GitHub committed 2026-10-29 09:23:34 +08:00
1 parent 611c30cb20
commit 95f0625781
7 files changed
+317 -20

No files matched your search

@@ -33,6 +33,150 @@ export const projectSemanticValues = (
target: bindingNode,
path: [],
});
if (binding.initializerValue !== undefined)
projectInitializerCandidates(
context,
binding,
binding.initializerValue,
binding.value,
[],
);
}
};
/** Preserve possible initializer flow when later effects make a slot unknown. */
const projectInitializerCandidates = (
context: SemanticFlowProjectionContext,
binding: JavaScriptSemanticBinding,
initial: JavaScriptSemanticValue,
current: JavaScriptSemanticValue,
path: readonly string[],
): void => {
const initialContainer = semanticContainer(initial);
const currentContainer = semanticContainer(current);
if (initialContainer === null || currentContainer === null) return;
const currentByName = new Map<string, JavaScriptSemanticProperty>();
for (const property of currentContainer.slots)
if (!currentByName.has(property.name))
currentByName.set(property.name, property);
for (const initialProperty of initialContainer.slots) {
const currentProperty = currentByName.get(initialProperty.name);
if (
initialProperty.presence !== "present" ||
currentProperty === undefined ||
currentProperty.presence === "absent"
)
continue;
const propertyPath = [...path, initialProperty.name];
if (currentProperty.value.status === "unknown") {
const slot = semanticPropertySlot(
context,
binding.bindingId,
propertyPath,
currentProperty,
);
projectUnknownInitializer({
context,
binding,
initial: initialProperty.value,
unknown: currentProperty.value,
target: slot,
path: propertyPath,
});
} else {
projectInitializerCandidates(
context,
binding,
initialProperty.value,
currentProperty.value,
propertyPath,
);
}
}
};
/** Retain initializer descendants as candidates without asserting the unknown receiver still exists. */
const projectUnknownInitializer = ({
context,
binding,
initial,
unknown,
target,
path,
}: {
context: SemanticFlowProjectionContext;
binding: JavaScriptSemanticBinding;
initial: JavaScriptSemanticValue;
unknown: Extract<
JavaScriptSemanticValue,
{ status: "unknown" | "ambiguous" | "cycle" }
>;
target: ReturnType<typeof semanticPropertySlot>;
path: readonly string[];
}): void => {
if (target === null) return;
const values =
initial.status === "literal"
? [initial.value]
: initial.status === "union"
? initial.values
: [];
for (const value of values) {
const literal = addLiteralNode(
context,
binding,
value,
`property:${semanticPropertyPointer(path)}`,
);
addSemanticGraphRelation(context.state, {
source: literal,
target,
relation: "defines",
resolution: "candidate",
});
if (literal !== null)
addSemanticGraphUnknown(context.state, {
node_id: literal.node_id,
family: "data-flow",
relation_kinds: ["defines"],
reason: "unknown-value",
detail: `The initializer may no longer determine this property: ${unknown.reason}`,
candidate_node_ids: [target.node_id],
evidence: unknownSemanticEvidence(
context.file,
binding.definitions[0]?.location ?? null,
),
});
}
const container = semanticContainer(initial);
if (container === null) return;
for (const property of container.slots) {
if (property.presence !== "present") continue;
const propertyPath = [...path, property.name];
const slot = semanticPropertySlot(
context,
binding.bindingId,
propertyPath,
{
name: property.name,
presence: "unknown-coverage",
value: unknown,
},
);
addSemanticGraphRelation(context.state, {
source: target,
target: slot,
relation: "writes-property",
resolution: "candidate",
});
projectUnknownInitializer({
context,
binding,
initial: property.value,
unknown,
target: slot,
path: propertyPath,
});
}
};