mirror of
https://github.com/morluto/rea
synced 2026-10-27 06:33:32 -04:00
fix(javascript): preserve alias and container uncertainty
This commit is contained in:
@@ -838,6 +838,85 @@ it("retains array length presence without inventing an exact length", () => {
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it.each([
|
||||
["object spread", 'const root = { known: "value", ...dynamic };'],
|
||||
["array spread", 'const root = ["value", ...dynamic];'],
|
||||
])(
|
||||
"keeps omitted initializer coverage on the owning binding: %s",
|
||||
(_label, source) => {
|
||||
const graph = graphFor(source);
|
||||
const root = graph.nodes.find(
|
||||
({ kind, label }) => kind === "binding" && label === "root",
|
||||
);
|
||||
if (root === undefined) throw new Error("Expected root binding");
|
||||
const result = queryJavaScriptSemanticGraph(graph, {
|
||||
seed: { kind: "semantic-node", node_id: root.node_id },
|
||||
direction: "forward-influence",
|
||||
});
|
||||
|
||||
expect(result.coverage.status).toBe("partial");
|
||||
expect(result.unknowns).toContainEqual(
|
||||
expect.objectContaining({
|
||||
node_id: root.node_id,
|
||||
family: "object-flow",
|
||||
reason: "ambiguous-target",
|
||||
detail: expect.stringContaining("Initializer container has unknown"),
|
||||
}),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
it.each([
|
||||
["bare if", 'if (flag) alias = { mode: "other" }; alias.mode = "updated";'],
|
||||
[
|
||||
"logical expression",
|
||||
'flag && (alias = { mode: "other" }); alias.mode = "updated";',
|
||||
],
|
||||
[
|
||||
"logical assignment",
|
||||
'flag ||= (alias = { mode: "other" }); alias.mode = "updated";',
|
||||
],
|
||||
["or assignment", 'alias ||= { mode: "other" }; alias.mode = "updated";'],
|
||||
[
|
||||
"nullish assignment",
|
||||
'alias ??= { mode: "other" }; alias.mode = "updated";',
|
||||
],
|
||||
[
|
||||
"conditional mutation",
|
||||
'alias = { mode: "other" }; if (flag) alias.mode = "updated";',
|
||||
],
|
||||
[
|
||||
"short-circuit mutation",
|
||||
'alias = { mode: "other" }; flag && (alias.mode = "updated");',
|
||||
],
|
||||
])(
|
||||
"preserves possible alias mutation uncertainty in queries: %s",
|
||||
(_label, body) => {
|
||||
const graph = graphFor(`
|
||||
const shared = { mode: "initial" };
|
||||
let alias = shared;
|
||||
${body}
|
||||
`);
|
||||
const sharedMode = graph.nodes.find(
|
||||
({ kind, identity, properties }) =>
|
||||
kind === "property-slot" &&
|
||||
properties.property_pointer === "/mode" &&
|
||||
identity.role_key.includes("binding:shared"),
|
||||
);
|
||||
if (sharedMode === undefined) throw new Error("Expected shared mode slot");
|
||||
const result = queryJavaScriptSemanticGraph(graph, {
|
||||
seed: { kind: "semantic-node", node_id: sharedMode.node_id },
|
||||
direction: "backward-provenance",
|
||||
});
|
||||
|
||||
expect(sharedMode.properties.presence).toBe("unknown-coverage");
|
||||
expect(result.nodes).toContainEqual(
|
||||
expect.objectContaining({ node_id: sharedMode.node_id }),
|
||||
);
|
||||
expect(result.coverage.status).toBe("partial");
|
||||
},
|
||||
);
|
||||
|
||||
it.each(["{}", "unknownRoot", "{ a: null }"])(
|
||||
"preserves the requested deep leaf name after a blocked prefix: %s",
|
||||
(initializer) => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ApplicationGraphEvidence } from "../../domain/javascript/javascriptApplicationEvidenceSchemas.js";
|
||||
import type { JavaScriptSemanticGraphNode } from "../../domain/javascript/javascriptSemanticGraph.js";
|
||||
import type { JavaScriptSemanticGraphNode } from "../../domain/javascript/javascriptSemanticGraphSchemas.js";
|
||||
import type { JavaScriptSourceRange } from "../../domain/javascript/javascriptStaticAnalysisTypes.js";
|
||||
import type { JavaScriptArtifactFile } from "../../domain/javascript/javascriptArtifactFiles.js";
|
||||
|
||||
@@ -101,7 +101,7 @@ export const unavailableSemanticRootEvidence = (
|
||||
/** Explicit unknown evidence at one unresolved dynamic syntax location. */
|
||||
export const unknownSemanticEvidence = (
|
||||
file: JavaScriptArtifactFile,
|
||||
location: JavaScriptSourceRange,
|
||||
location: JavaScriptSourceRange | null,
|
||||
): ApplicationGraphEvidence => ({
|
||||
...observedSemanticEvidence(file, location),
|
||||
authority: "unknown",
|
||||
|
||||
@@ -3,10 +3,8 @@ import {
|
||||
semanticPropertyPointer,
|
||||
type JavaScriptSemanticSlot,
|
||||
} from "../../domain/javascript/javascriptSemanticSlots.js";
|
||||
import type {
|
||||
JavaScriptSemanticBinding,
|
||||
JavaScriptSemanticValue,
|
||||
} from "../../domain/javascript/javascriptSemanticIr.js";
|
||||
import type { JavaScriptSemanticBinding } from "../../domain/javascript/javascriptSemanticIr.js";
|
||||
import type { JavaScriptSemanticValue } from "../../domain/javascript/javascriptSemanticValueTypes.js";
|
||||
import { createJavaScriptSemanticGraphUnknown } from "../../domain/javascript/javascriptSemanticGraph.js";
|
||||
import {
|
||||
retainSemanticGraphNode,
|
||||
@@ -14,7 +12,10 @@ import {
|
||||
addSemanticGraphUnknown,
|
||||
} from "./JavaScriptSemanticGraphConstruction.js";
|
||||
import type { SemanticFlowProjectionContext } from "./JavaScriptSemanticGraphFlowProjection.js";
|
||||
import { observedSemanticEvidence } from "./JavaScriptSemanticGraphEvidence.js";
|
||||
import {
|
||||
observedSemanticEvidence,
|
||||
unknownSemanticEvidence,
|
||||
} from "./JavaScriptSemanticGraphEvidence.js";
|
||||
|
||||
/** Project bounded literal values and object slots for exact query seeds. */
|
||||
export const projectSemanticValues = (
|
||||
@@ -90,6 +91,24 @@ const projectValue = (input: ValueProjectionInput): void => {
|
||||
path: propertyPath,
|
||||
});
|
||||
}
|
||||
if (container.coverage.status === "partial")
|
||||
addSemanticGraphUnknown(
|
||||
context.state,
|
||||
createJavaScriptSemanticGraphUnknown({
|
||||
node_id: target.node_id,
|
||||
family: "object-flow",
|
||||
relation_kinds: ["writes-property"],
|
||||
reason: "ambiguous-target",
|
||||
detail: `Initializer container has unknown ${
|
||||
value.status === "object" ? "properties" : "items"
|
||||
}${container.coverage.omitted === null ? " with an unknown omitted count" : `; ${container.coverage.omitted} omitted`}.`,
|
||||
candidate_node_ids: [target.node_id],
|
||||
evidence: unknownSemanticEvidence(
|
||||
context.file,
|
||||
binding.definitions[0]?.location ?? null,
|
||||
),
|
||||
}),
|
||||
);
|
||||
} else if (value.status === "unknown" && value.resourceLimit !== undefined) {
|
||||
const location = binding.definitions[0]?.location ?? null;
|
||||
const evidence = observedSemanticEvidence(context.file, location);
|
||||
|
||||
Reference in New Issue
Block a user