Skip to content

Commit

Permalink
feat(parser): missing initializer in destructuring declaration inside…
Browse files Browse the repository at this point in the history
… for loop head (#8222)

closes #8220
  • Loading branch information
Boshen committed Jan 2, 2025
1 parent 9c1afa4 commit 2da4365
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 46 deletions.
42 changes: 18 additions & 24 deletions crates/oxc_parser/src/js/declaration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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() {
Expand All @@ -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()?;
}

Expand All @@ -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();
Expand All @@ -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
Expand Down Expand Up @@ -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,
)?;

Expand Down
12 changes: 0 additions & 12 deletions crates/oxc_parser/src/js/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
}
}
6 changes: 4 additions & 2 deletions crates/oxc_parser/src/js/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
11 changes: 8 additions & 3 deletions crates/oxc_parser/src/js/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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(),
)?;

Expand Down Expand Up @@ -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())
})?;

Expand Down Expand Up @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions crates/oxc_parser/src/ts/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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),
Expand Down
42 changes: 39 additions & 3 deletions tasks/coverage/snapshots/parser_typescript.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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() { }
Expand Down

0 comments on commit 2da4365

Please sign in to comment.