fix(javascript): avoid invented computed destructuring properties (#507)

Signed-off-by: Rudy Celekli <rudy@gradiahq.com>
This commit is contained in:
Rudy Mizrahi Celekli 2026-10-05 02:59:10 -04:00 committed by GitHub
parent 2fd11961cf
commit 80777d96da
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 46 additions and 2 deletions

View File

@ -170,6 +170,24 @@ it("projects built-in resource acquisition and release", () => {
).toMatchObject({ status: "partial" });
});
it("does not project a literal property slot for dynamic destructuring", () => {
const graph = graphFor(`
const source = { token: "TOKEN" };
const key = getKey();
const { [key]: value } = source;
`);
expect(
graph.relations.filter(({ relation }) => relation === "destructures"),
).toEqual([]);
expect(
graph.nodes.find(
({ kind, properties }) =>
kind === "property-slot" && properties.name === "key",
),
).toBeUndefined();
});
it("projects literal seeds and static object flow", () => {
const graph = graphFor(`
const source = { token: "TOKEN", count: 1 };

View File

@ -36,6 +36,32 @@ describe("JavaScript semantic analysis: dataflow 1", () => {
);
});
it("does not invent property names for dynamic destructuring keys", () => {
const ir = analyzeJavaScriptSemantics(`
const source = { token: "TOKEN" };
const key = getKey();
const { [key]: dynamic } = source;
const { ["token"]: literal } = source;
const { token: renamed } = source;
const { token } = source;
const { [1]: numeric } = source;
`);
const destructures = ir.objectOperations.filter(
({ kind }) => kind === "destructure",
);
expect(destructures.map(({ propertyName }) => propertyName)).toEqual([
"token",
"token",
"token",
"1",
]);
expect(
destructures.every(({ resolution }) => resolution === "complete"),
).toBe(true);
expect(topLevelBinding(ir, "dynamic").value.status).toBe("unknown");
});
it("does not project positional argument flow after a spread", () => {
const ir = analyzeJavaScriptSemantics(`
function target(first, second, third) { return third; }

View File

@ -13,7 +13,7 @@ import {
type JavaScriptSemanticAnalysisState,
} from "./javascriptSemanticState.js";
import { traverseJavaScriptAst } from "./javascriptSemanticTraversal.js";
import { propertyName, range } from "./javascriptStaticAnalysisHelpers.js";
import { range } from "./javascriptStaticAnalysisHelpers.js";
/** Recover bounded static property reads, writes, spreads, and destructuring. */
export const collectJavaScriptSemanticObjects = (
@ -103,7 +103,7 @@ const collectDestructuring = (
const objectBindingId = expressionBindingId(node.init, state);
for (const property of node.id.properties) {
if (!t.isObjectProperty(property)) continue;
const name = propertyName(property.key);
const name = semanticStaticPropertyName(property.key, property.computed);
if (name.length === 0) continue;
const target = bindingIdentifier(property.value);
addObjectOperation(