rea/src/domain/javascript/javascriptSemanticAnalysis....

97 lines
2.9 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { analyzeJavaScriptSemantics } from "./javascriptSemanticAnalysis.js";
const observe = (source: string) => {
const ir = analyzeJavaScriptSemantics(source);
return {
binding: ir.bindings.find((item) => item.name === "x")?.value,
returned: ir.callables.find((item) => item.name === "read")?.returnSites[0]
?.value,
};
};
describe("JavaScript semantic analysis: logical assignment", () => {
it.each(["||=", "??=", "&&="])(
"keeps a parameter uncertain after %s",
(operator) => {
const { binding, returned } = observe(
`function read(x) { x ${operator} 2; return x; }`,
);
expect(binding?.status).toBe("unknown");
expect(returned?.status).toBe("unknown");
},
);
it.each(["||=", "??=", "&&="])(
"keeps a destructured parameter uncertain after %s",
(operator) => {
const { binding, returned } = observe(
`function read({ x }) { x ${operator} 2; return x; }`,
);
expect(binding?.status).toBe("unknown");
expect(returned?.status).toBe("unknown");
},
);
it.each(["||=", "??=", "&&="])(
"keeps a catch binding uncertain after %s",
(operator) => {
const { binding, returned } = observe(
`function read() { try { run(); } catch (x) { x ${operator} 2; return x; } }`,
);
expect(binding?.status).toBe("unknown");
expect(returned?.status).toBe("unknown");
},
);
it.each([
["let", "||="],
["let", "??="],
["var", "||="],
["var", "??="],
])(
"keeps an uninitialized %s local exact after %s",
(declaration, operator) => {
const { binding, returned } = observe(
`function read() { ${declaration} x; x ${operator} 2; return x; }`,
);
expect(binding).toEqual({ status: "literal", value: 2 });
expect(returned).toEqual({ status: "literal", value: 2 });
},
);
it("keeps an unconditional parameter assignment exact", () => {
const { binding, returned } = observe(
"function read(x) { x = 2; return x; }",
);
expect(binding).toEqual({ status: "literal", value: 2 });
expect(returned).toEqual({ status: "literal", value: 2 });
});
it("keeps an unchanged parameter unknown", () => {
expect(observe("function read(x) { return x; }").returned?.status).toBe(
"unknown",
);
});
it("reproduces the issue #2646 inputs without an exact literal", () => {
for (const operator of ["||=", "??="]) {
const { binding, returned } = observe(
`function read(x) { x ${operator} 2; return x; }`,
);
expect(binding).not.toEqual({ status: "literal", value: 2 });
expect(returned).not.toEqual({ status: "literal", value: 2 });
}
const readOr = (x: number) => {
x ||= 2;
return x;
};
const readNullish = (x: number) => {
x ??= 2;
return x;
};
expect(readOr(5)).toBe(5);
expect(readNullish(5)).toBe(5);
});
});