mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
feat(parser): missing initializer in destructuring declaration inside for loop head (#8222)
closes #8220
This commit is contained in:
parent
9c1afa4729
commit
2da4365fbe
6 changed files with 71 additions and 46 deletions
|
|
@ -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,
|
||||
)?;
|
||||
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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() { }
|
||||
|
|
|
|||
Loading…
Reference in a new issue