mirror of
https://github.com/morluto/rea
synced 2026-10-26 06:16:17 -04:00
9df94a62cd
* feat(javascript): link assignment right-hand sides to their writes A declaration carries its value forward, but a later assignment to a variable, a property or through a compound operator stopped the forward trace at the right-hand-side read. The semantic IR now records one syntactic assignment flow per simple assignment target (ranges, operator, owning callable), and the graph projection connects each retained read of the right-hand side to the write occurrence through a defines relation. Compound assignments also consume the previous value of the target; the written value itself stays unknown, keeping the boundary of #4342 intact. Fixes #6193 * fix(javascript): preserve computed key assignment ownership * test(javascript): retain nested callback assignment ownership * docs(javascript): record assignment influence verification * test(mcp): isolate mocks and acknowledge cancellation boundaries * test(javascript): retain CLI diagnostics and current-main verification --------- Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
88 lines
3.5 KiB
TypeScript
88 lines
3.5 KiB
TypeScript
import { mkdir, writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { expect } from "vitest";
|
|
import { z } from "zod";
|
|
import { javaScriptSemanticTraceResultSchema } from "../../../src/domain/javascript/javascriptSemanticTraceSchemas.js";
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
import { cliTest } from "../../support/cli/cliFixture.js";
|
|
|
|
cliTest(
|
|
"traces reassignment inputs through the public analysis and trace commands",
|
|
async ({ cli }) => {
|
|
const root = await createTestTempDirectory("rea-assignment-influence-");
|
|
for (const [name, body, label] of [
|
|
["declaration", "const next = accel; return next;", "next"],
|
|
["variable", "speed = accel; return speed;", "speed"],
|
|
["property", "state.speed = accel; return state.speed;", "speed"],
|
|
["compound", "state.speed += accel; return state.speed;", "speed"],
|
|
[
|
|
"computed-key",
|
|
"const obj = { [speed = accel]() {} }; return speed;",
|
|
"speed",
|
|
],
|
|
] as const) {
|
|
const directory = join(root, name);
|
|
await mkdir(directory);
|
|
await writeFile(
|
|
join(directory, "main.js"),
|
|
`const accel = 0.001; let speed = 6; const state = { speed: 6 }; export function update() { ${body} }`,
|
|
);
|
|
// The compiled CLI prepares Darwin ownership helpers; hosts that select
|
|
// a matching SDK explicitly must pass that selection through, as the
|
|
// other compiled-CLI lifecycle tests do.
|
|
const environment: { readonly SDKROOT: string } | Record<string, never> =
|
|
process.env.SDKROOT === undefined
|
|
? {}
|
|
: { SDKROOT: process.env.SDKROOT };
|
|
const analyzed = await cli.run({
|
|
arguments: ["analyze-javascript-application", directory, "--json"],
|
|
environment,
|
|
});
|
|
expect(analyzed.exitCode, `${name} analyzed: ${analyzed.stderr}`).toBe(0);
|
|
const queryFile = join(root, `${name}.json`);
|
|
const query = {
|
|
application: analyzed.json,
|
|
query: {
|
|
seed: { kind: "literal", value: 0.001 },
|
|
direction: "forward-influence",
|
|
},
|
|
};
|
|
await writeFile(queryFile, JSON.stringify(query));
|
|
const traced = await cli.run({
|
|
arguments: ["trace-javascript-semantics", queryFile, "--json"],
|
|
});
|
|
expect(traced.exitCode, `${name} traced: ${traced.stderr}`).toBe(0);
|
|
const { normalized_result: result } = z
|
|
.object({ normalized_result: javaScriptSemanticTraceResultSchema })
|
|
.parse(traced.json);
|
|
expect(
|
|
result.nodes.some(
|
|
(node) =>
|
|
["binding", "property-slot"].includes(node.kind) &&
|
|
node.label === label,
|
|
),
|
|
).toBe(true);
|
|
// Later property reads remain uncertain after a mutable write. Follow
|
|
// candidates explicitly rather than upgrading their pre-existing resolution.
|
|
await writeFile(
|
|
queryFile,
|
|
JSON.stringify({
|
|
...query,
|
|
query: { ...query.query, include_ambiguous_dynamic_edges: true },
|
|
}),
|
|
);
|
|
const expanded = await cli.run({
|
|
arguments: ["trace-javascript-semantics", queryFile, "--json"],
|
|
});
|
|
expect(expanded.exitCode, `${name} expanded: ${expanded.stderr}`).toBe(0);
|
|
const expandedResult = z
|
|
.object({ normalized_result: javaScriptSemanticTraceResultSchema })
|
|
.parse(expanded.json).normalized_result;
|
|
expect(expandedResult.nodes.map((node) => node.label)).toContain(
|
|
"return",
|
|
);
|
|
}
|
|
},
|
|
120_000,
|
|
);
|