diff --git a/crates/oxc_parser/src/js/declaration.rs b/crates/oxc_parser/src/js/declaration.rs index 25340eac2493c..30ca2eed5b248 100644 --- a/crates/oxc_parser/src/js/declaration.rs +++ b/crates/oxc_parser/src/js/declaration.rs @@ -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>> { 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> { 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, )?; diff --git a/crates/oxc_parser/src/js/mod.rs b/crates/oxc_parser/src/js/mod.rs index 41133dab45867..5348f6b00d2f8 100644 --- a/crates/oxc_parser/src/js/mod.rs +++ b/crates/oxc_parser/src/js/mod.rs @@ -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 } - } } diff --git a/crates/oxc_parser/src/js/module.rs b/crates/oxc_parser/src/js/module.rs index 275cbf6657986..647410ad161d2 100644 --- a/crates/oxc_parser/src/js/module.rs +++ b/crates/oxc_parser/src/js/module.rs @@ -354,13 +354,15 @@ impl<'a> ParserImpl<'a> { // For tc39/proposal-decorators // For more information, please refer to 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, diff --git a/crates/oxc_parser/src/js/statement.rs b/crates/oxc_parser/src/js/statement.rs index 4add19cd8203c..7e4f64d9a193f 100644 --- a/crates/oxc_parser/src/js/statement.rs +++ b/crates/oxc_parser/src/js/statement.rs @@ -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> { 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> { 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 { diff --git a/crates/oxc_parser/src/ts/statement.rs b/crates/oxc_parser/src/ts/statement.rs index 12a629d062ba1..1e0d5b8e9b9e0 100644 --- a/crates/oxc_parser/src/ts/statement.rs +++ b/crates/oxc_parser/src/ts/statement.rs @@ -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), diff --git a/tasks/coverage/snapshots/parser_typescript.snap b/tasks/coverage/snapshots/parser_typescript.snap index 9cbe6b1320bce..0e82bef31bc52 100644 --- a/tasks/coverage/snapshots/parser_typescript.snap +++ b/tasks/coverage/snapshots/parser_typescript.snap @@ -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() { }