mirror of
https://github.com/aphrody-labs/oxc.git
synced 2026-10-15 06:39:17 +00:00
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:
14 files changed
+73
-14
No files matched your search
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -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 {
|
||||
|
||||
@@ -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%)
|
||||
Reference in new issue
Block a user