Files
scriptc/packages/compiler/src/frontend/early-errors.test.ts
T
Chris Tate 352842f020 Scope early errors to function bodies and class strictness, and match the upstream compareArray reads
- A function body or class static block treats its top-level function
  declarations as var-scoped: duplicates are legal in strict code, and a
  let/const/class of the same name is SC0006.
- Strictness is the code around a node: a function declaration's own
  "use strict" no longer makes its declaring context strict.
- Every part of a class is strict; object literal methods and accessors
  are strict only by their own prologue or their surroundings, and an
  arrow function's block body can carry "use strict".
- The bare compareArray reads b.length, then a.length, and a.length again
  before every element pair, converting it as the upstream `i < a.length`
  does, so a string length compares its elements.
- Tests that relied on the old handling of undeclared names now use
  constructs that are still refused or still compile: the installed-entry
  diagnostics fixture reports a syntax error (a read of an undeclared name
  is now Node's run-time ReferenceError), and static-objects-order's
  loops.js stores from its for-in head into `undefined` instead of an
  undeclared name (a store into an undeclared name refuses its package with
  SC1091).
2026-10-29 08:37:25 -05:00

44 lines
1.9 KiB
TypeScript

import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { expect, test } from "vitest";
import { checkPreflight, loadProgram } from "./program-node.js";
/** The preflight diagnostics of a CommonJS script (sloppy unless it says
* "use strict"). */
function diagnostics(source: string) {
const directory = mkdtempSync(join(tmpdir(), "scriptc-early-errors-"));
writeFileSync(join(directory, "main.cjs"), source);
const load = loadProgram(join(directory, "main.cjs"));
try {
return checkPreflight(load).map((diagnostic) => diagnostic.code);
} finally {
load.dispose();
rmSync(directory, { recursive: true, force: true });
}
}
test.for([
// A function body's top-level function declarations are var-scoped, so
// duplicates are legal in strict code too.
'function outer() { "use strict"; function d() {} function d() {} }\nouter();',
"class C { static { function s() {} function s() {} } }\nnew C();",
// Object literal methods and accessors are sloppy unless they say otherwise.
"var o = { m() { if (true) function f() {} } };\no.m();",
"var o = { get g() { { function h() {} function h() {} } return 1; } };\no.g;",
// A function's own directive makes its body strict, not the code declaring it.
'if (true) function k() { "use strict"; }',
'{ function z() { "use strict"; } function z() {} }',
])("Node accepts: %s", (source) => {
expect(diagnostics(source)).toEqual([]);
});
test.for([
["function outer() { let d; function d() {} }\nouter();", "SC0006"],
['(() => { "use strict"; if (true) function f() {} })();', "SC0007"],
["class C { m() { if (true) function f() {} } }\nnew C().m();", "SC0007"],
['var o = { m() { "use strict"; { function h() {} function h() {} } } };\no.m();', "SC0005"],
])("Node refuses: %s", ([source, code]) => {
expect(diagnostics(source!)).toContain(code);
});