fix(parser): reject initialized lexical declarations in for-in (#25700)

Rejects initialized `let` and `const` declarations in `for...in` during parsing while preserving Annex B's non-strict `var` form.

Adds misc coverage for valid and invalid initializer forms.

Verified with `just ready` and parser conformance.

Closes #25149.

AI-assisted.
This commit is contained in:
Boshen committed 2026-08-15 06:58:08 +00:00
1 parent ad963b689d
commit fca2e0cfed
14 files changed
+73 -14

No files matched your search

+5
View File
@@ -759,6 +759,11 @@ parser_diagnostics! {
.with_label(span)
};
initializer_in_for_in_lexical_declaration(span: Span) => {
OxcDiagnostic::error("for-in loop variable declaration may not have an initializer")
.with_label(span)
};
using_declarations_must_be_initialized(span: Span) => {
OxcDiagnostic::error("Using declarations must have an initializer.")
.with_label(span)
+14
View File
@@ -599,6 +599,20 @@ impl<'a, C: Config> ParserImpl<'a, C> {
is_for_in,
declaration.span,
));
return;
}
// Annex B.3.5 only permits `var BindingIdentifier Initializer` in non-strict code:
// https://tc39.es/ecma262/#sec-initializers-in-forin-statement-heads
// Reject lexical declarations here; semantic analysis checks strict mode and patterns.
if is_for_in
&& matches!(
declaration.kind,
VariableDeclarationKind::Let | VariableDeclarationKind::Const
)
&& declaration.has_init()
{
self.error(diagnostics::initializer_in_for_in_lexical_declaration(declaration.span));
}
}
@@ -912,7 +912,8 @@ pub fn check_for_statement_left(
) {
let ForStatementLeft::VariableDeclaration(decl) = left else { return };
// initializer is not allowed for for-in / for-of
// The parser checks initialized lexical declarations and multiple declarations. The
// remaining cases depend on strict mode or the binding form.
if decl.declarations.len() > 1 {
return;
}
@@ -920,9 +921,8 @@ pub fn check_for_statement_left(
let strict_mode = ctx.strict_mode();
for declarator in &decl.declarations {
if declarator.init.is_some()
&& (strict_mode
&& ((strict_mode && decl.kind.is_var())
|| !is_for_in
|| decl.kind.is_lexical()
|| !matches!(declarator.id, BindingPattern::BindingIdentifier(_)))
{
ctx.error(diagnostics::unexpected_initializer_in_for_loop_head(
@@ -0,0 +1 @@
for (const a = 1 in b);
@@ -0,0 +1 @@
for (let a = 1 in b);
@@ -0,0 +1,3 @@
// Annex B.3.5 disallows a `var` initializer in a for-in head in strict mode.
"use strict";
for (var a = 1 in b);
@@ -0,0 +1 @@
for (var [a] = [] in b);
@@ -0,0 +1 @@
for (var { a } = {} in b);
@@ -0,0 +1,2 @@
// Annex B.3.5 allows a `var` initializer in a for-in head in non-strict code.
for (var a = 1 in b);
+2 -2
View File
@@ -1,3 +1,3 @@
codegen_misc Summary:
AST Parsed : 78/78 (100.00%)
Positive Passed: 78/78 (100.00%)
AST Parsed : 79/79 (100.00%)
Positive Passed: 79/79 (100.00%)
+2 -2
View File
@@ -1,3 +1,3 @@
formatter_misc Summary:
AST Parsed : 78/78 (100.00%)
Positive Passed: 78/78 (100.00%)
AST Parsed : 79/79 (100.00%)
Positive Passed: 79/79 (100.00%)
+34 -3
View File
@@ -1,7 +1,7 @@
parser_misc Summary:
AST Parsed : 78/78 (100.00%)
Positive Passed: 78/78 (100.00%)
Negative Passed: 162/162 (100.00%)
AST Parsed : 79/79 (100.00%)
Positive Passed: 79/79 (100.00%)
Negative Passed: 167/167 (100.00%)
× Cannot assign to 'arguments' in strict mode
╭─[misc/fail/arguments-eval-ambient.ts:2:13]
@@ -3941,6 +3941,37 @@ Negative Passed: 162/162 (100.00%)
╰────
help: Allowed modifiers are: private, protected, public, static, abstract, override
× for-in loop variable declaration may not have an initializer
╭─[misc/fail/oxc-25149-const-initializer.js:1:6]
1 │ for (const a = 1 in b);
· ───────────
╰────
× for-in loop variable declaration may not have an initializer
╭─[misc/fail/oxc-25149-let-initializer.js:1:6]
1 │ for (let a = 1 in b);
· ─────────
╰────
× for-in loop variable declaration may not have an initializer
╭─[misc/fail/oxc-25149-strict-var-initializer.cjs:3:6]
2 │ "use strict";
3 │ for (var a = 1 in b);
· ─────────
╰────
× for-in loop variable declaration may not have an initializer
╭─[misc/fail/oxc-25149-var-array-pattern-initializer.js:1:6]
1 │ for (var [a] = [] in b);
· ────────────
╰────
× for-in loop variable declaration may not have an initializer
╭─[misc/fail/oxc-25149-var-object-pattern-initializer.js:1:6]
1 │ for (var { a } = {} in b);
· ──────────────
╰────
× TS(1092): Type parameters cannot appear on a constructor declaration
╭─[misc/fail/oxc-25692.ts:2:16]
1 │ class A {
+2 -2
View File
@@ -1,6 +1,6 @@
semantic_misc Summary:
AST Parsed : 78/78 (100.00%)
Positive Passed: 74/78 (94.87%)
AST Parsed : 79/79 (100.00%)
Positive Passed: 75/79 (94.94%)
semantic Error: tasks/coverage/misc/pass/declare-let-private.ts
Bindings mismatch:
after transform: ScopeId(0): ["private"]
@@ -1,3 +1,3 @@
transformer_misc Summary:
AST Parsed : 78/78 (100.00%)
Positive Passed: 78/78 (100.00%)
AST Parsed : 79/79 (100.00%)
Positive Passed: 79/79 (100.00%)