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:
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,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user