Commit Graph
2 Commits
Author SHA1 Message Date
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
Malte UblandClaude Opus 5.5 b234e06f20 Enforce the ECMAScript early errors the checker does not model
Static semantics reject a program before it runs. Until now the compiler
borrowed that rejection from whatever tsc happened to report, and Test262's
negative-parse suites show where the borrowing stops: programs the spec
makes SyntaxErrors compile and run. This adds the two largest families the
grab-bag misses (plan §7.7 D-4, option c), each under its own preflight
code, so a checker relaxation can never remove the only rejection a
parse-negative program had:

  SC0005  a statement list declares one lexical name twice
          (`{ class f {} async function f() {} }`)
  SC0006  a lexical name is `var`-declared in the same statement list
          (`switch (1) { case 1: let x; case 2: var x; }`)
  SC0007  a declaration where the grammar admits only a Statement
          (`while (false) function f() {}`, `l: class c {}`,
          `if (x) async function f() {}`)

frontend/early-errors.ts is a preflight walk over JavaScript sources, so a
rejected program fails where a tsc parse error fails, before any lowering.
A case block is one lexical scope across its clauses; a list's
VarDeclaredNames reach into nested blocks but never into a function or a
class body; and Annex B's allowances are honored exactly: two plain
function declarations may repeat in a sloppy block (B.3.3.1), and a plain
function may be an if/else body or a labelled statement in sloppy code
(B.3.2) — an async function, a generator or a class never gets either
allowance, and neither does any function as a loop or 'with' body.

Strictness comes from Node's own module classification (isNodeEsmFile7),
not from the checker's external-module bit: moduleDetection=force gives
every file module scope, CommonJS included, and taking that bit as
strictness cost the Annex B exemption on the annexB
function-redeclaration tests. Typed sources are untouched — their checker
diagnostics (TS2451 and friends) remain the gate, and re-reporting a
collision under a second code would only duplicate them.

tests/test262/support.mjs counts one of these codes at a location inside
the test's own source as the parse-negative match, the same standard the
helper already applies to tsc's early errors. The diagnostics fixtures pin
both families, including the strict half of SC0007 where Node refuses the
two Annex B shapes as well.

The pass only reads the AST and only refuses programs, so no typed
program's IR changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 05:34:47 -07:00