feat(parser): missing initializer in destructuring declaration inside for loop head (#8222)

closes #8220
This commit is contained in:
Boshen 2025-01-02 14:02:48 +00:00
parent 9c1afa4729
commit 2da4365fbe
6 changed files with 71 additions and 46 deletions

View file

@ -3,7 +3,7 @@ use oxc_ast::{ast::*, NONE};
use oxc_diagnostics::Result;
use oxc_span::{GetSpan, Span};
use super::{VariableDeclarationContext, VariableDeclarationParent};
use super::VariableDeclarationParent;
use crate::{
diagnostics,
lexer::Kind,
@ -45,7 +45,7 @@ impl<'a> ParserImpl<'a> {
pub(crate) fn parse_variable_declaration(
&mut self,
start_span: Span,
decl_ctx: VariableDeclarationContext,
decl_parent: VariableDeclarationParent,
modifiers: &Modifiers<'a>,
) -> Result<Box<'a, VariableDeclaration<'a>>> {
let kind = match self.cur_kind() {
@ -58,17 +58,14 @@ impl<'a> ParserImpl<'a> {
let mut declarations = self.ast.vec();
loop {
let declaration = self.parse_variable_declarator(decl_ctx, kind)?;
let declaration = self.parse_variable_declarator(decl_parent, kind)?;
declarations.push(declaration);
if !self.eat(Kind::Comma) {
break;
}
}
if matches!(
decl_ctx.parent,
VariableDeclarationParent::Statement | VariableDeclarationParent::Clause
) {
if matches!(decl_parent, VariableDeclarationParent::Statement) {
self.asi()?;
}
@ -88,7 +85,7 @@ impl<'a> ParserImpl<'a> {
fn parse_variable_declarator(
&mut self,
decl_ctx: VariableDeclarationContext,
decl_parent: VariableDeclarationParent,
kind: VariableDeclarationKind,
) -> Result<VariableDeclarator<'a>> {
let span = self.start_span();
@ -115,27 +112,24 @@ impl<'a> ParserImpl<'a> {
} else {
(self.ast.binding_pattern(binding_kind, NONE, false), false)
};
let init =
self.eat(Kind::Eq).then(|| self.parse_assignment_expression_or_higher()).transpose()?;
let decl = self.ast.variable_declarator(self.end_span(span), kind, id, init, definite);
if decl_parent == VariableDeclarationParent::Statement {
self.check_missing_initializer(&decl);
}
Ok(decl)
}
if init.is_none()
&& !self.ctx.has_ambient()
&& decl_ctx.parent == VariableDeclarationParent::Statement
{
// LexicalBinding[In, Yield, Await] :
// BindingIdentifier[?Yield, ?Await] Initializer[?In, ?Yield, ?Await] opt
// BindingPattern[?Yield, ?Await] Initializer[?In, ?Yield, ?Await]
// the grammar forbids `let []`, `let {}`
if !matches!(id.kind, BindingPatternKind::BindingIdentifier(_)) {
self.error(diagnostics::invalid_destrucuring_declaration(id.span()));
} else if kind == VariableDeclarationKind::Const {
pub(crate) fn check_missing_initializer(&mut self, decl: &VariableDeclarator<'a>) {
if decl.init.is_none() && !self.ctx.has_ambient() {
if !matches!(decl.id.kind, BindingPatternKind::BindingIdentifier(_)) {
self.error(diagnostics::invalid_destrucuring_declaration(decl.id.span()));
} else if decl.kind == VariableDeclarationKind::Const {
// It is a Syntax Error if Initializer is not present and IsConstantDeclaration of the LexicalDeclaration containing this LexicalBinding is true.
self.error(diagnostics::missinginitializer_in_const(id.span()));
self.error(diagnostics::missinginitializer_in_const(decl.id.span()));
}
}
Ok(self.ast.variable_declarator(self.end_span(span), kind, id, init, definite))
}
/// Section 14.3.1 Let, Const, and Using Declarations
@ -168,7 +162,7 @@ impl<'a> ParserImpl<'a> {
let mut declarations: oxc_allocator::Vec<'_, VariableDeclarator<'_>> = self.ast.vec();
loop {
let declaration = self.parse_variable_declarator(
VariableDeclarationContext::new(VariableDeclarationParent::Statement),
VariableDeclarationParent::Statement,
VariableDeclarationKind::Var,
)?;

View file

@ -34,16 +34,4 @@ pub enum FunctionKind {
pub enum VariableDeclarationParent {
For,
Statement,
Clause,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct VariableDeclarationContext {
pub parent: VariableDeclarationParent,
}
impl VariableDeclarationContext {
pub(crate) fn new(parent: VariableDeclarationParent) -> Self {
Self { parent }
}
}

View file

@ -354,13 +354,15 @@ impl<'a> ParserImpl<'a> {
// For tc39/proposal-decorators
// For more information, please refer to <https://babeljs.io/docs/babel-plugin-proposal-decorators#decoratorsbeforeexport>
self.eat_decorators()?;
let reserved_ctx = self.ctx;
let modifiers =
if self.is_ts { self.eat_modifiers_before_declaration()? } else { Modifiers::empty() };
self.ctx = self.ctx.union_ambient_if(modifiers.contains_declare());
let declaration = self.parse_declaration(decl_span, &modifiers)?;
let span = self.end_span(span);
self.ctx = reserved_ctx;
Ok(self.ast.alloc_export_named_declaration(
span,
self.end_span(span),
Some(declaration),
self.ast.vec(),
None,

View file

@ -3,7 +3,7 @@ use oxc_ast::ast::*;
use oxc_diagnostics::Result;
use oxc_span::{Atom, GetSpan, Span};
use super::{grammar::CoverGrammar, VariableDeclarationContext, VariableDeclarationParent};
use super::{grammar::CoverGrammar, VariableDeclarationParent};
use crate::{
diagnostics, lexer::Kind, modifiers::Modifiers, Context, ParserImpl, StatementContext,
};
@ -174,7 +174,7 @@ impl<'a> ParserImpl<'a> {
let start_span = self.start_span();
let decl = self.parse_variable_declaration(
start_span,
VariableDeclarationContext::new(VariableDeclarationParent::Statement),
VariableDeclarationParent::Statement,
&Modifiers::empty(),
)?;
@ -309,7 +309,7 @@ impl<'a> ParserImpl<'a> {
) -> Result<Statement<'a>> {
let start_span = self.start_span();
let init_declaration = self.context(Context::empty(), Context::In, |p| {
let decl_ctx = VariableDeclarationContext::new(VariableDeclarationParent::For);
let decl_ctx = VariableDeclarationParent::For;
p.parse_variable_declaration(start_span, decl_ctx, &Modifiers::empty())
})?;
@ -358,6 +358,11 @@ impl<'a> ParserImpl<'a> {
r#await: bool,
) -> Result<Statement<'a>> {
self.expect(Kind::Semicolon)?;
if let Some(ForStatementInit::VariableDeclaration(decl)) = &init {
for d in &decl.declarations {
self.check_missing_initializer(d);
}
}
let test = if !self.at(Kind::Semicolon) && !self.at(Kind::RParen) {
Some(self.context(Context::In, Context::empty(), ParserImpl::parse_expr)?)
} else {

View file

@ -5,7 +5,7 @@ use oxc_span::{GetSpan, Span};
use crate::{
diagnostics,
js::{FunctionKind, VariableDeclarationContext, VariableDeclarationParent},
js::{FunctionKind, VariableDeclarationParent},
lexer::Kind,
modifiers::{ModifierFlags, ModifierKind, Modifiers},
ParserImpl,
@ -379,7 +379,7 @@ impl<'a> ParserImpl<'a> {
kind if kind.is_variable_declaration() => self
.parse_variable_declaration(
start_span,
VariableDeclarationContext::new(VariableDeclarationParent::Clause),
VariableDeclarationParent::Statement,
modifiers,
)
.map(Declaration::VariableDeclaration),

View file

@ -3,7 +3,7 @@ commit: d85767ab
parser_typescript Summary:
AST Parsed : 6494/6503 (99.86%)
Positive Passed: 6483/6503 (99.69%)
Negative Passed: 1275/5747 (22.19%)
Negative Passed: 1277/5747 (22.22%)
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ClassDeclaration24.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ExportAssignment7.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ExportAssignment8.ts
@ -3182,7 +3182,6 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/externalM
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/externalModules/exportClassNameWithObjectSystem.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/externalModules/exportClassNameWithObjectUMD.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/externalModules/exportDefaultClassNameWithObject.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/externalModules/exportNonInitializedVariablesInIfThenStatementNoCrash1.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/externalModules/exportNonLocalDeclarations.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/externalModules/importNonExternalModule.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/externalModules/importTsBeforeDTs.ts
@ -4080,7 +4079,6 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/statement
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/statements/for-ofStatements/ES5For-ofTypeCheck9.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/statements/forStatements/forStatementsMultipleInvalidDecl.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/statements/ifDoWhileStatements/ifDoWhileStatements.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/statements/labeledStatements/labeledStatementExportDeclarationNoCrash1.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/statements/returnStatements/invalidReturnStatements.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/statements/switchStatements/switchStatements.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/any/anyAsConstructor.ts
@ -6772,6 +6770,21 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private
5 │
╰────
× Missing initializer in const declaration
╭─[typescript/tests/cases/compiler/constDeclarations-errors.ts:12:11]
11 │ // error, can not be unintalized
12 │ for(const c9; c9 < 1;) { }
· ──
13 │
╰────
× Missing initializer in const declaration
╭─[typescript/tests/cases/compiler/constDeclarations-errors.ts:15:20]
14 │ // error, can not be unintalized
15 │ for(const c10 = 0, c11; c10 < 1;) { }
· ───
╰────
× Lexical declaration cannot appear in a single-statement context
╭─[typescript/tests/cases/compiler/constDeclarations-invalidContexts.ts:3:5]
2 │ if (true)
@ -19457,6 +19470,14 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private
2 │ let;
╰────
× Missing initializer in const declaration
╭─[typescript/tests/cases/conformance/externalModules/exportNonInitializedVariablesInIfThenStatementNoCrash1.ts:4:14]
3 │ if (true)
4 │ export const cssExports: CssExports;
· ──────────────────────
5 │ export default cssExports;
╰────
× Unexpected token
╭─[typescript/tests/cases/conformance/externalModules/exportNonInitializedVariablesSystem.ts:1:4]
1 │ var;
@ -24001,6 +24022,21 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private
14 │ `height: var foo`,
╰────
× Missing initializer in const declaration
╭─[typescript/tests/cases/conformance/statements/labeledStatements/labeledStatementExportDeclarationNoCrash1.ts:3:14]
2 │
3 │ export const box: string
· ───────────
4 │ subTitle:
╰────
× Missing initializer in const declaration
╭─[typescript/tests/cases/conformance/statements/labeledStatements/labeledStatementExportDeclarationNoCrash1.ts:5:14]
4 │ subTitle:
5 │ export const title: string
· ─────────────
╰────
× Generators can only be declared at the top level or inside a block
╭─[typescript/tests/cases/conformance/statements/labeledStatements/labeledStatementWithLabel.ts:2:8]
1 │ label: function fn() { }