diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_type_error.rs b/crates/oxc_linter/src/rules/unicorn/prefer_type_error.rs index 5db061b9e4301..29c4b71667405 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_type_error.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_type_error.rs @@ -447,15 +447,16 @@ fn test() { // throw new TypeError; // } // "#, - r#" + r" if (typeof foo == 'Foo' || 'Foo' === typeof foo) { throw new Error(); } - r#" + ", + r" if (Number.isFinite(foo) && Number.isSafeInteger(foo) && Number.isInteger(foo)) { throw new Error(); } - "#, + ", r" if (wrapper.n.isFinite(foo) && wrapper.n.isSafeInteger(foo) && wrapper.n.isInteger(foo)) { throw new Error(); diff --git a/crates/oxc_linter/src/snapshots/unicorn_prefer_type_error.snap b/crates/oxc_linter/src/snapshots/unicorn_prefer_type_error.snap index d9f811ffed7bd..1ba8db0e968d5 100644 --- a/crates/oxc_linter/src/snapshots/unicorn_prefer_type_error.snap +++ b/crates/oxc_linter/src/snapshots/unicorn_prefer_type_error.snap @@ -64,22 +64,23 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Change to `throw new TypeError(...)` - × Invalid Character `"` - ╭─[prefer_type_error.tsx:5:11] + ⚠ eslint-plugin-unicorn(prefer-type-error): Prefer throwing a `TypeError` over a generic `Error` after a type checking if-statement + ╭─[prefer_type_error.tsx:3:27] + 2 │ if (typeof foo == 'Foo' || 'Foo' === typeof foo) { + 3 │ throw new Error(); + · ───── 4 │ } - 5 │ r#" - · ─ - 6 │ if (Number.isFinite(foo) && Number.isSafeInteger(foo) && Number.isInteger(foo)) { ╰──── + help: Change to `throw new TypeError(...)` - × Expected a semicolon or an implicit semicolon after a statement, but found none - ╭─[prefer_type_error.tsx:5:10] + ⚠ eslint-plugin-unicorn(prefer-type-error): Prefer throwing a `TypeError` over a generic `Error` after a type checking if-statement + ╭─[prefer_type_error.tsx:3:27] + 2 │ if (Number.isFinite(foo) && Number.isSafeInteger(foo) && Number.isInteger(foo)) { + 3 │ throw new Error(); + · ───── 4 │ } - 5 │ r#" - · ▲ - 6 │ if (Number.isFinite(foo) && Number.isSafeInteger(foo) && Number.isInteger(foo)) { ╰──── - help: Try insert a semicolon here + help: Change to `throw new TypeError(...)` ⚠ eslint-plugin-unicorn(prefer-type-error): Prefer throwing a `TypeError` over a generic `Error` after a type checking if-statement ╭─[prefer_type_error.tsx:3:27] diff --git a/crates/oxc_parser/examples/parser.rs b/crates/oxc_parser/examples/parser.rs index 9952fec454712..be13f0fe2dd8e 100644 --- a/crates/oxc_parser/examples/parser.rs +++ b/crates/oxc_parser/examples/parser.rs @@ -60,8 +60,8 @@ fn main() -> Result<(), String> { for error in ret.errors { let error = error.with_source_code(source_text.clone()); println!("{error:?}"); - println!("Parsed with Errors."); } + println!("Parsed with Errors."); } Ok(()) diff --git a/crates/oxc_parser/src/cursor.rs b/crates/oxc_parser/src/cursor.rs index 8c9a94de605ee..9626412a2c21b 100644 --- a/crates/oxc_parser/src/cursor.rs +++ b/crates/oxc_parser/src/cursor.rs @@ -7,15 +7,17 @@ use oxc_span::{GetSpan, Span}; use crate::{ Context, ParserImpl, diagnostics, + error_handler::FatalError, lexer::{Kind, LexerCheckpoint, LexerContext, Token}, }; -#[derive(Clone, Copy)] +#[derive(Clone)] pub struct ParserCheckpoint<'a> { lexer: LexerCheckpoint<'a>, cur_token: Token, prev_span_end: u32, errors_pos: usize, + fatal_error: Option, } impl<'a> ParserImpl<'a> { @@ -169,7 +171,8 @@ impl<'a> ParserImpl<'a> { pub(crate) fn asi(&mut self) -> Result<()> { if !self.can_insert_semicolon() { let span = Span::new(self.prev_token_end, self.prev_token_end); - return Err(diagnostics::auto_semicolon_insertion(span)); + let error = diagnostics::auto_semicolon_insertion(span); + return Err(self.set_fatal_error(error)); } if self.at(Kind::Semicolon) { self.advance(Kind::Semicolon); @@ -186,10 +189,11 @@ impl<'a> ParserImpl<'a> { } /// # Errors - pub(crate) fn expect_without_advance(&self, kind: Kind) -> Result<()> { + pub(crate) fn expect_without_advance(&mut self, kind: Kind) -> Result<()> { if !self.at(kind) { let range = self.cur_token().span(); - return Err(diagnostics::expect_token(kind.to_str(), self.cur_kind().to_str(), range)); + let error = diagnostics::expect_token(kind.to_str(), self.cur_kind().to_str(), range); + return Err(self.set_fatal_error(error)); } Ok(()) } @@ -271,22 +275,25 @@ impl<'a> ParserImpl<'a> { } } - pub(crate) fn checkpoint(&self) -> ParserCheckpoint<'a> { + pub(crate) fn checkpoint(&mut self) -> ParserCheckpoint<'a> { ParserCheckpoint { lexer: self.lexer.checkpoint(), cur_token: self.token, prev_span_end: self.prev_token_end, errors_pos: self.errors.len(), + fatal_error: self.fatal_error.take(), } } pub(crate) fn rewind(&mut self, checkpoint: ParserCheckpoint<'a>) { - let ParserCheckpoint { lexer, cur_token, prev_span_end, errors_pos } = checkpoint; + let ParserCheckpoint { lexer, cur_token, prev_span_end, errors_pos, fatal_error } = + checkpoint; self.lexer.rewind(lexer); self.token = cur_token; self.prev_token_end = prev_span_end; self.errors.truncate(errors_pos); + self.fatal_error = fatal_error; } /// # Errors @@ -343,7 +350,7 @@ impl<'a> ParserImpl<'a> { let mut list = self.ast.vec(); loop { let kind = self.cur_kind(); - if kind == close || kind == Kind::Eof { + if kind == close || self.has_fatal_error() { break; } match f(self)? { @@ -373,7 +380,7 @@ impl<'a> ParserImpl<'a> { let mut first = true; loop { let kind = self.cur_kind(); - if kind == close || kind == Kind::Eof { + if kind == close || self.has_fatal_error() { break; } if first { @@ -408,7 +415,7 @@ impl<'a> ParserImpl<'a> { let mut first = true; loop { let kind = self.cur_kind(); - if kind == close || kind == Kind::Eof { + if kind == close || self.has_fatal_error() { break; } if first { diff --git a/crates/oxc_parser/src/diagnostics.rs b/crates/oxc_parser/src/diagnostics.rs index 37295eee822f8..4def7d94467b0 100644 --- a/crates/oxc_parser/src/diagnostics.rs +++ b/crates/oxc_parser/src/diagnostics.rs @@ -520,6 +520,11 @@ pub fn a_rest_element_cannot_have_an_initializer(span: Span) -> OxcDiagnostic { OxcDiagnostic::error("A rest element cannot have an initializer.").with_label(span) } +#[cold] +pub fn import_requires_a_specifier(span: Span) -> OxcDiagnostic { + OxcDiagnostic::error("import() requires a specifier.").with_label(span) +} + // ================================= MODIFIERS ================================= #[cold] diff --git a/crates/oxc_parser/src/error_handler.rs b/crates/oxc_parser/src/error_handler.rs new file mode 100644 index 0000000000000..3c515314a4f41 --- /dev/null +++ b/crates/oxc_parser/src/error_handler.rs @@ -0,0 +1,70 @@ +//! Code related to error handling. + +use oxc_allocator::Dummy; +use oxc_diagnostics::OxcDiagnostic; + +use crate::{ParserImpl, diagnostics, lexer::Kind}; + +/// Fatal parsing error. +#[derive(Debug, Clone)] +pub struct FatalError { + /// The fatal error + pub error: OxcDiagnostic, + /// Length of `errors` at time fatal error is recorded + #[expect(unused)] + pub errors_len: usize, +} + +impl<'a> ParserImpl<'a> { + pub(crate) fn set_unexpected(&mut self) -> OxcDiagnostic { + // The lexer should have reported a more meaningful diagnostic + // when it is a undetermined kind. + if matches!(self.cur_kind(), Kind::Eof | Kind::Undetermined) { + if let Some(error) = self.lexer.errors.pop() { + return self.set_fatal_error(error); + } + } + let error = diagnostics::unexpected_token(self.cur_token().span()); + self.set_fatal_error(error) + } + + /// Return error info at current token + /// + /// # Panics + /// + /// * The lexer did not push a diagnostic when `Kind::Undetermined` is returned + pub(crate) fn unexpected(&mut self) -> OxcDiagnostic { + self.set_unexpected() + // Dummy::dummy(self.ast.allocator) + } + + /// Push a Syntax Error + pub(crate) fn error(&mut self, error: OxcDiagnostic) { + self.errors.push(error); + } + + /// Count of all parser and lexer errors. + pub(crate) fn errors_count(&self) -> usize { + self.errors.len() + self.lexer.errors.len() + } + + /// Advance lexer's cursor to end of file. + pub(crate) fn set_fatal_error(&mut self, error: OxcDiagnostic) -> OxcDiagnostic { + if self.fatal_error.is_none() { + self.lexer.advance_to_end(); + self.fatal_error = + Some(FatalError { error: error.clone(), errors_len: self.errors.len() }); + } + error + } + + #[expect(unused)] + pub(crate) fn fatal_error>(&mut self, error: OxcDiagnostic) -> T { + let _ = self.set_fatal_error(error); + Dummy::dummy(self.ast.allocator) + } + + pub(crate) fn has_fatal_error(&self) -> bool { + matches!(self.cur_kind(), Kind::Eof | Kind::Undetermined) || self.fatal_error.is_some() + } +} diff --git a/crates/oxc_parser/src/js/binding.rs b/crates/oxc_parser/src/js/binding.rs index 3cdc1a1e7e79d..6002ba1652348 100644 --- a/crates/oxc_parser/src/js/binding.rs +++ b/crates/oxc_parser/src/js/binding.rs @@ -53,7 +53,8 @@ impl<'a> ParserImpl<'a> { )?; if let Some(rest) = &rest { if !matches!(&rest.argument.kind, BindingPatternKind::BindingIdentifier(_)) { - return Err(diagnostics::invalid_binding_rest_element(rest.argument.span())); + let error = diagnostics::invalid_binding_rest_element(rest.argument.span()); + return Err(self.set_fatal_error(error)); } } self.expect(Kind::RCurly)?; diff --git a/crates/oxc_parser/src/js/expression.rs b/crates/oxc_parser/src/js/expression.rs index 612173dd52c56..2cfe37c9ed629 100644 --- a/crates/oxc_parser/src/js/expression.rs +++ b/crates/oxc_parser/src/js/expression.rs @@ -73,12 +73,13 @@ impl<'a> ParserImpl<'a> { pub(crate) fn parse_binding_identifier(&mut self) -> Result> { let cur = self.cur_kind(); if !cur.is_binding_identifier() { - let err = if cur.is_reserved_keyword() { - diagnostics::identifier_reserved_word(self.cur_token().span(), cur.to_str()) + return Err(if cur.is_reserved_keyword() { + let error = + diagnostics::identifier_reserved_word(self.cur_token().span(), cur.to_str()); + self.set_fatal_error(error) } else { self.unexpected() - }; - return Err(err); + }); } let (span, name) = self.parse_identifier_kind(Kind::Ident); self.check_identifier(span, &name); @@ -211,7 +212,8 @@ impl<'a> ParserImpl<'a> { if expressions.is_empty() { self.expect(Kind::RParen)?; - return Err(diagnostics::empty_parenthesized_expression(self.end_span(span))); + let error = diagnostics::empty_parenthesized_expression(self.end_span(span)); + return Err(self.set_fatal_error(error)); } let expr_span = self.end_span(expr_span); @@ -477,7 +479,6 @@ impl<'a> ParserImpl<'a> { self.re_lex_template_substitution_tail(); loop { match self.cur_kind() { - Kind::Eof => self.expect(Kind::TemplateTail)?, Kind::TemplateTail => { quasis.push(self.parse_template_element(tagged)); break; @@ -485,6 +486,7 @@ impl<'a> ParserImpl<'a> { Kind::TemplateMiddle => { quasis.push(self.parse_template_element(tagged)); } + _ if self.has_fatal_error() => self.expect(Kind::TemplateTail)?, _ => { // TemplateMiddle Expression[+In, ?Yield, ?Await] let expr = @@ -737,7 +739,7 @@ impl<'a> ParserImpl<'a> { | Kind::TemplateHead | Kind::LBrack => break, _ => { - return Err(diagnostics::unexpected_token(self.cur_token().span())); + return Err(self.unexpected()); } } } @@ -1050,7 +1052,8 @@ impl<'a> ParserImpl<'a> { self.expect(Kind::In)?; let right = self.parse_binary_expression_or_higher(Precedence::Lowest)?; if let Expression::PrivateInExpression(private_in_expr) = right { - return Err(diagnostics::private_in_private(private_in_expr.span)); + let error = diagnostics::private_in_private(private_in_expr.span); + return Err(self.set_fatal_error(error)); } self.ast.expression_private_in(self.end_span(lhs_span), left, right) } else { diff --git a/crates/oxc_parser/src/js/grammar.rs b/crates/oxc_parser/src/js/grammar.rs index 1919028f1c335..9eea43e0118c1 100644 --- a/crates/oxc_parser/src/js/grammar.rs +++ b/crates/oxc_parser/src/js/grammar.rs @@ -85,7 +85,8 @@ impl<'a> CoverGrammar<'a, ArrayExpression<'a>> for ArrayAssignmentTarget<'a> { p.error(diagnostics::binding_rest_element_trailing_comma(*span)); } } else { - return Err(diagnostics::spread_last_element(elem.span)); + let error = diagnostics::spread_last_element(elem.span); + return Err(p.set_fatal_error(error)); } } ArrayExpressionElement::Elision(_) => elements.push(None), diff --git a/crates/oxc_parser/src/js/module.rs b/crates/oxc_parser/src/js/module.rs index 9ee2af3fb1fe1..cb75cff2de0bf 100644 --- a/crates/oxc_parser/src/js/module.rs +++ b/crates/oxc_parser/src/js/module.rs @@ -17,8 +17,8 @@ impl<'a> ParserImpl<'a> { ) -> Result> { self.expect(Kind::LParen)?; if self.eat(Kind::RParen) { - return Err(oxc_diagnostics::OxcDiagnostic::error("import() requires a specifier.") - .with_label(self.end_span(span))); + let error = diagnostics::import_requires_a_specifier(self.end_span(span)); + return Err(self.set_fatal_error(error)); } let has_in = self.ctx.has_in(); self.ctx = self.ctx.and_in(true); @@ -31,7 +31,8 @@ impl<'a> ParserImpl<'a> { // Allow trailing comma self.bump(Kind::Comma); if !self.eat(Kind::RParen) { - return Err(diagnostics::import_arguments(self.end_span(span))); + let error = diagnostics::import_arguments(self.end_span(span)); + return Err(self.set_fatal_error(error)); } self.ctx = self.ctx.and_in(has_in); let expr = diff --git a/crates/oxc_parser/src/js/statement.rs b/crates/oxc_parser/src/js/statement.rs index 5e53f12ec8678..59208fb88acc0 100644 --- a/crates/oxc_parser/src/js/statement.rs +++ b/crates/oxc_parser/src/js/statement.rs @@ -36,7 +36,7 @@ impl<'a> ParserImpl<'a> { let mut statements = self.ast.vec(); let mut expecting_directives = true; - while !self.at(Kind::Eof) { + while !self.has_fatal_error() { if !is_top_level && self.at(Kind::RCurly) { break; } @@ -220,7 +220,7 @@ impl<'a> ParserImpl<'a> { let span = self.start_span(); self.expect(Kind::LCurly)?; let mut body = self.ast.vec(); - while !self.at(Kind::RCurly) && !self.at(Kind::Eof) { + while !self.at(Kind::RCurly) && !self.has_fatal_error() { let stmt = self.parse_statement_list_item(StatementContext::StatementList)?; body.push(stmt); } @@ -553,7 +553,9 @@ impl<'a> ParserImpl<'a> { }; self.expect(Kind::Colon)?; let mut consequent = self.ast.vec(); - while !matches!(self.cur_kind(), Kind::Case | Kind::Default | Kind::RCurly | Kind::Eof) { + while !matches!(self.cur_kind(), Kind::Case | Kind::Default | Kind::RCurly) + && !self.has_fatal_error() + { let stmt = self.parse_statement_list_item(StatementContext::StatementList)?; consequent.push(stmt); } diff --git a/crates/oxc_parser/src/jsx/mod.rs b/crates/oxc_parser/src/jsx/mod.rs index 51fa939ff269d..8665fe9ddd2fb 100644 --- a/crates/oxc_parser/src/jsx/mod.rs +++ b/crates/oxc_parser/src/jsx/mod.rs @@ -195,7 +195,7 @@ impl<'a> ParserImpl<'a> { let mut span = Span::new(span, 0); let mut property = None; - while self.eat(Kind::Dot) && !self.at(Kind::Eof) { + while self.eat(Kind::Dot) && !self.has_fatal_error() { // if let Some(prop) = property { object = @@ -206,7 +206,8 @@ impl<'a> ParserImpl<'a> { let ident = self.parse_jsx_identifier()?; // `` is a syntax error. if ident.name.contains('-') { - return Err(diagnostics::unexpected_token(ident.span)); + let error = diagnostics::unexpected_token(ident.span); + return Err(self.set_fatal_error(error)); } property = Some(ident); span = self.end_span(span.start); @@ -227,7 +228,7 @@ impl<'a> ParserImpl<'a> { /// `JSXChild` `JSXChildren_opt` fn parse_jsx_children(&mut self) -> Result>> { let mut children = self.ast.vec(); - while !self.at(Kind::Eof) { + while !self.has_fatal_error() { if let Some(child) = self.parse_jsx_child()? { children.push(child); } else { @@ -306,7 +307,8 @@ impl<'a> ParserImpl<'a> { self.context(Context::default().and_await(self.ctx.has_await()), self.ctx, |p| { let expr = p.parse_expr(); if let Ok(Expression::SequenceExpression(seq)) = &expr { - return Err(diagnostics::jsx_expressions_may_not_use_the_comma_operator(seq.span)); + let error = diagnostics::jsx_expressions_may_not_use_the_comma_operator(seq.span); + return Err(p.set_fatal_error(error)); } expr }) @@ -328,7 +330,9 @@ impl<'a> ParserImpl<'a> { /// `JSXAttribute` `JSXAttributes_opt` fn parse_jsx_attributes(&mut self) -> Result>> { let mut attributes = self.ast.vec(); - while !matches!(self.cur_kind(), Kind::Eof | Kind::LAngle | Kind::RAngle | Kind::Slash) { + while !matches!(self.cur_kind(), Kind::LAngle | Kind::RAngle | Kind::Slash) + && !self.has_fatal_error() + { let attribute = match self.cur_kind() { Kind::LCurly => { self.parse_jsx_spread_attribute().map(JSXAttributeItem::SpreadAttribute) diff --git a/crates/oxc_parser/src/lexer/identifier.rs b/crates/oxc_parser/src/lexer/identifier.rs index 08757492b40a7..689621e41b6ff 100644 --- a/crates/oxc_parser/src/lexer/identifier.rs +++ b/crates/oxc_parser/src/lexer/identifier.rs @@ -293,6 +293,7 @@ impl<'a> Lexer<'a> { let start = self.offset(); let c = self.consume_char(); self.error(diagnostics::invalid_character(c, Span::new(start, self.offset()))); - Kind::Undetermined + self.advance_to_end(); + Kind::Eof } } diff --git a/crates/oxc_parser/src/lexer/jsx.rs b/crates/oxc_parser/src/lexer/jsx.rs index 518e943e90cce..2f86adb4e8c99 100644 --- a/crates/oxc_parser/src/lexer/jsx.rs +++ b/crates/oxc_parser/src/lexer/jsx.rs @@ -49,7 +49,7 @@ impl Lexer<'_> { } else { self.source.advance_to_end(); self.error(diagnostics::unterminated_string(self.unterminated_range())); - Kind::Undetermined + Kind::Eof } } @@ -80,7 +80,7 @@ impl Lexer<'_> { lexer: self, table: JSX_CHILD_END_TABLE, handle_eof: { - return Kind::Undetermined; + return Kind::Eof; }, }; @@ -94,7 +94,7 @@ impl Lexer<'_> { next_byte as char, if next_byte == b'}' { "rbrace" } else { "gt" }, )); - Kind::Undetermined + Kind::Eof }) } } diff --git a/crates/oxc_parser/src/lexer/mod.rs b/crates/oxc_parser/src/lexer/mod.rs index cd67a2c7b3300..5117278e66ad8 100644 --- a/crates/oxc_parser/src/lexer/mod.rs +++ b/crates/oxc_parser/src/lexer/mod.rs @@ -224,6 +224,12 @@ impl<'a> Lexer<'a> { token } + /// Advance source cursor to end of file. + #[inline] + pub fn advance_to_end(&mut self) { + self.source.advance_to_end(); + } + // ---------- Private Methods ---------- // fn error(&mut self, error: OxcDiagnostic) { self.errors.push(error); diff --git a/crates/oxc_parser/src/lexer/numeric.rs b/crates/oxc_parser/src/lexer/numeric.rs index 462f10b550af3..d0abaef080548 100644 --- a/crates/oxc_parser/src/lexer/numeric.rs +++ b/crates/oxc_parser/src/lexer/numeric.rs @@ -51,7 +51,8 @@ impl Lexer<'_> { self.consume_char(); } else { self.unexpected_err(); - return Kind::Undetermined; + self.advance_to_end(); + return Kind::Eof; } while let Some(b) = self.peek_byte() { @@ -67,7 +68,8 @@ impl Lexer<'_> { self.consume_char(); } else { self.unexpected_err(); - return Kind::Undetermined; + self.advance_to_end(); + return Kind::Eof; } } b if kind.matches_number_byte(b) => { @@ -222,6 +224,7 @@ impl Lexer<'_> { } } self.error(diagnostics::invalid_number_end(Span::new(offset, self.offset()))); - Kind::Undetermined + self.advance_to_end(); + Kind::Eof } } diff --git a/crates/oxc_parser/src/lib.rs b/crates/oxc_parser/src/lib.rs index f5fcf0df88e04..4f387477f8cf8 100644 --- a/crates/oxc_parser/src/lib.rs +++ b/crates/oxc_parser/src/lib.rs @@ -68,6 +68,7 @@ mod context; mod cursor; +mod error_handler; mod modifiers; mod module_record; mod state; @@ -96,7 +97,8 @@ use oxc_syntax::module_record::ModuleRecord; use crate::{ context::{Context, StatementContext}, - lexer::{Kind, Lexer, Token}, + error_handler::FatalError, + lexer::{Lexer, Token}, module_record::ModuleRecordBuilder, state::ParserState, }; @@ -367,6 +369,8 @@ struct ParserImpl<'a> { /// Note: favor adding to `Diagnostics` instead of raising Err errors: Vec, + fatal_error: Option, + /// The current parsing token token: Token, @@ -408,6 +412,7 @@ impl<'a> ParserImpl<'a> { source_type, source_text, errors: vec![], + fatal_error: None, token: Token::default(), prev_token_end: 0, state: ParserState::new(allocator), @@ -424,11 +429,21 @@ impl<'a> ParserImpl<'a> { /// Recoverable errors are stored inside `errors`. #[inline] pub fn parse(mut self) -> ParserReturn<'a> { - let (mut program, panicked) = match self.parse_program() { - Ok(program) => (program, false), + let mut panicked = false; + let mut program = match self.parse_program() { + Ok(program) => program, Err(error) => { - self.error(self.overlong_error().unwrap_or(error)); - let program = self.ast.program( + panicked = true; + if let Some(fatal_error) = self.fatal_error.take() { + if !self.lexer.errors.is_empty() && self.cur_kind().is_eof() { + // Noop + } else { + self.error(fatal_error.error); + } + } else { + self.error(error); + } + self.ast.program( Span::default(), self.source_type, self.source_text, @@ -436,12 +451,19 @@ impl<'a> ParserImpl<'a> { None, self.ast.vec(), self.ast.vec(), - ); - (program, true) + ) } }; self.check_unfinished_errors(); + + if let Some(overlong_error) = self.overlong_error() { + panicked = true; + self.lexer.errors.clear(); + self.errors.clear(); + self.error(overlong_error); + } + let mut is_flow_language = false; let mut errors = vec![]; // only check for `@flow` if the file failed to parse. @@ -562,29 +584,6 @@ impl<'a> ParserImpl<'a> { None } - /// Return error info at current token - /// # Panics - /// * The lexer did not push a diagnostic when `Kind::Undetermined` is returned - fn unexpected(&mut self) -> OxcDiagnostic { - // The lexer should have reported a more meaningful diagnostic - // when it is a undetermined kind. - if self.cur_kind() == Kind::Undetermined { - if let Some(error) = self.lexer.errors.pop() { - return error; - } - } - diagnostics::unexpected_token(self.cur_token().span()) - } - - /// Push a Syntax Error - fn error(&mut self, error: OxcDiagnostic) { - self.errors.push(error); - } - - fn errors_count(&self) -> usize { - self.errors.len() + self.lexer.errors.len() - } - #[inline] fn alloc(&self, value: T) -> ArenaBox<'a, T> { self.ast.alloc(value) diff --git a/tasks/coverage/snapshots/parser_babel.snap b/tasks/coverage/snapshots/parser_babel.snap index 03492d2f27fb0..7d3dad3ca0e94 100644 --- a/tasks/coverage/snapshots/parser_babel.snap +++ b/tasks/coverage/snapshots/parser_babel.snap @@ -10244,13 +10244,6 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc · ─ ╰──── - × Expected a semicolon or an implicit semicolon after a statement, but found none - ╭─[babel/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0065/input.js:1:2] - 1 │ i #= 42 - · ▲ - ╰──── - help: Try insert a semicolon here - × Cannot assign to this expression ╭─[babel/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0066/input.js:1:1] 1 │ i + 2 = 42 diff --git a/tasks/coverage/snapshots/parser_test262.snap b/tasks/coverage/snapshots/parser_test262.snap index 88f2bb12d3358..35be6915eaee6 100644 --- a/tasks/coverage/snapshots/parser_test262.snap +++ b/tasks/coverage/snapshots/parser_test262.snap @@ -12720,15 +12720,6 @@ Negative Passed: 4519/4519 (100.00%) 30 │ }; ╰──── - × Expected a semicolon or an implicit semicolon after a statement, but found none - ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-accessor-get-meth.js:29:6] - 28 │ var C = class { - 29 │ get # m() {} - · ▲ - 30 │ }; - ╰──── - help: Try insert a semicolon here - × Invalid Character ` ` ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-accessor-set-meth.js:29:8] 28 │ var C = class { @@ -12737,15 +12728,6 @@ Negative Passed: 4519/4519 (100.00%) 30 │ }; ╰──── - × Expected a semicolon or an implicit semicolon after a statement, but found none - ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-accessor-set-meth.js:29:6] - 28 │ var C = class { - 29 │ set # m(_) {} - · ▲ - 30 │ }; - ╰──── - help: Try insert a semicolon here - × Invalid Character ` ` ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-async-gen-meth.js:29:12] 28 │ var C = class { @@ -12762,15 +12744,6 @@ Negative Passed: 4519/4519 (100.00%) 30 │ }; ╰──── - × Expected a semicolon or an implicit semicolon after a statement, but found none - ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-async-meth.js:29:8] - 28 │ var C = class { - 29 │ async # m() {} - · ▲ - 30 │ }; - ╰──── - help: Try insert a semicolon here - × Invalid Character ` ` ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-call-expr.js:38:15] 37 │ m() { @@ -12827,15 +12800,6 @@ Negative Passed: 4519/4519 (100.00%) 30 │ }; ╰──── - × Expected a semicolon or an implicit semicolon after a statement, but found none - ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-static-accessor-get-meth.js:29:13] - 28 │ var C = class { - 29 │ static get # m() {} - · ▲ - 30 │ }; - ╰──── - help: Try insert a semicolon here - × Invalid Character ` ` ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-static-accessor-set-meth.js:29:15] 28 │ var C = class { @@ -12844,15 +12808,6 @@ Negative Passed: 4519/4519 (100.00%) 30 │ }; ╰──── - × Expected a semicolon or an implicit semicolon after a statement, but found none - ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-static-accessor-set-meth.js:29:13] - 28 │ var C = class { - 29 │ static set # m(_) {} - · ▲ - 30 │ }; - ╰──── - help: Try insert a semicolon here - × Invalid Character ` ` ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-static-async-gen-meth.js:29:19] 28 │ var C = class { @@ -12869,15 +12824,6 @@ Negative Passed: 4519/4519 (100.00%) 30 │ }; ╰──── - × Expected a semicolon or an implicit semicolon after a statement, but found none - ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-static-async-meth.js:29:15] - 28 │ var C = class { - 29 │ static async # m() {} - · ▲ - 30 │ }; - ╰──── - help: Try insert a semicolon here - × Invalid Character ` ` ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-static-field-init.js:29:11] 28 │ var C = class { @@ -12886,15 +12832,6 @@ Negative Passed: 4519/4519 (100.00%) 30 │ }; ╰──── - × Expected a semicolon or an implicit semicolon after a statement, but found none - ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-static-field-init.js:29:9] - 28 │ var C = class { - 29 │ static # x = 1; - · ▲ - 30 │ }; - ╰──── - help: Try insert a semicolon here - × Invalid Character ` ` ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-static-field.js:29:11] 28 │ var C = class { @@ -12903,15 +12840,6 @@ Negative Passed: 4519/4519 (100.00%) 30 │ }; ╰──── - × Expected a semicolon or an implicit semicolon after a statement, but found none - ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-static-field.js:29:9] - 28 │ var C = class { - 29 │ static # x; - · ▲ - 30 │ }; - ╰──── - help: Try insert a semicolon here - × Invalid Character ` ` ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-static-gen-meth.js:29:13] 28 │ var C = class { @@ -12928,15 +12856,6 @@ Negative Passed: 4519/4519 (100.00%) 30 │ }; ╰──── - × Expected a semicolon or an implicit semicolon after a statement, but found none - ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-static-meth.js:29:9] - 28 │ var C = class { - 29 │ static # m() {} - · ▲ - 30 │ }; - ╰──── - help: Try insert a semicolon here - × Expected a semicolon or an implicit semicolon after a statement, but found none ╭─[test262/test/language/expressions/class/elements/syntax/early-errors/grammar-privatenames-same-line-error.js:36:5] 35 │ var C = class { @@ -31113,15 +31032,6 @@ Negative Passed: 4519/4519 (100.00%) 30 │ } ╰──── - × Expected a semicolon or an implicit semicolon after a statement, but found none - ╭─[test262/test/language/statements/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-accessor-get-meth.js:29:6] - 28 │ class C { - 29 │ get # m() {} - · ▲ - 30 │ } - ╰──── - help: Try insert a semicolon here - × Invalid Character ` ` ╭─[test262/test/language/statements/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-accessor-set-meth.js:29:8] 28 │ class C { @@ -31130,15 +31040,6 @@ Negative Passed: 4519/4519 (100.00%) 30 │ } ╰──── - × Expected a semicolon or an implicit semicolon after a statement, but found none - ╭─[test262/test/language/statements/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-accessor-set-meth.js:29:6] - 28 │ class C { - 29 │ set # m(_) {} - · ▲ - 30 │ } - ╰──── - help: Try insert a semicolon here - × Invalid Character ` ` ╭─[test262/test/language/statements/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-async-gen-meth.js:29:12] 28 │ class C { @@ -31155,15 +31056,6 @@ Negative Passed: 4519/4519 (100.00%) 30 │ } ╰──── - × Expected a semicolon or an implicit semicolon after a statement, but found none - ╭─[test262/test/language/statements/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-async-meth.js:29:8] - 28 │ class C { - 29 │ async # m() {} - · ▲ - 30 │ } - ╰──── - help: Try insert a semicolon here - × Invalid Character ` ` ╭─[test262/test/language/statements/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-call-expr.js:38:15] 37 │ m() { @@ -31220,15 +31112,6 @@ Negative Passed: 4519/4519 (100.00%) 30 │ } ╰──── - × Expected a semicolon or an implicit semicolon after a statement, but found none - ╭─[test262/test/language/statements/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-static-accessor-get-meth.js:29:13] - 28 │ class C { - 29 │ static get # m() {} - · ▲ - 30 │ } - ╰──── - help: Try insert a semicolon here - × Invalid Character ` ` ╭─[test262/test/language/statements/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-static-accessor-set-meth.js:29:15] 28 │ class C { @@ -31237,15 +31120,6 @@ Negative Passed: 4519/4519 (100.00%) 30 │ } ╰──── - × Expected a semicolon or an implicit semicolon after a statement, but found none - ╭─[test262/test/language/statements/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-static-accessor-set-meth.js:29:13] - 28 │ class C { - 29 │ static set # m(_) {} - · ▲ - 30 │ } - ╰──── - help: Try insert a semicolon here - × Invalid Character ` ` ╭─[test262/test/language/statements/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-static-async-gen-meth.js:29:19] 28 │ class C { @@ -31262,15 +31136,6 @@ Negative Passed: 4519/4519 (100.00%) 30 │ } ╰──── - × Expected a semicolon or an implicit semicolon after a statement, but found none - ╭─[test262/test/language/statements/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-static-async-meth.js:29:15] - 28 │ class C { - 29 │ static async # m() {} - · ▲ - 30 │ } - ╰──── - help: Try insert a semicolon here - × Invalid Character ` ` ╭─[test262/test/language/statements/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-static-field-init.js:29:11] 28 │ class C { @@ -31279,15 +31144,6 @@ Negative Passed: 4519/4519 (100.00%) 30 │ } ╰──── - × Expected a semicolon or an implicit semicolon after a statement, but found none - ╭─[test262/test/language/statements/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-static-field-init.js:29:9] - 28 │ class C { - 29 │ static # x = 1; - · ▲ - 30 │ } - ╰──── - help: Try insert a semicolon here - × Invalid Character ` ` ╭─[test262/test/language/statements/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-static-field.js:29:11] 28 │ class C { @@ -31296,15 +31152,6 @@ Negative Passed: 4519/4519 (100.00%) 30 │ } ╰──── - × Expected a semicolon or an implicit semicolon after a statement, but found none - ╭─[test262/test/language/statements/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-static-field.js:29:9] - 28 │ class C { - 29 │ static # x; - · ▲ - 30 │ } - ╰──── - help: Try insert a semicolon here - × Invalid Character ` ` ╭─[test262/test/language/statements/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-static-gen-meth.js:29:13] 28 │ class C { @@ -31321,15 +31168,6 @@ Negative Passed: 4519/4519 (100.00%) 30 │ } ╰──── - × Expected a semicolon or an implicit semicolon after a statement, but found none - ╭─[test262/test/language/statements/class/elements/syntax/early-errors/grammar-privatename-whitespace-error-static-meth.js:29:9] - 28 │ class C { - 29 │ static # m() {} - · ▲ - 30 │ } - ╰──── - help: Try insert a semicolon here - × Expected a semicolon or an implicit semicolon after a statement, but found none ╭─[test262/test/language/statements/class/elements/syntax/early-errors/grammar-privatenames-same-line-error.js:36:5] 35 │ class C { @@ -34453,11 +34291,11 @@ Negative Passed: 4519/4519 (100.00%) · ───── ╰──── - × Unexpected token - ╭─[test262/test/language/statements/for-in/dstr/array-rest-before-element.js:33:6] + × Spread must be last element + ╭─[test262/test/language/statements/for-in/dstr/array-rest-before-element.js:33:7] 32 │ 33 │ for ([...x, y] in [[]]) ; - · ───────── + · ──── ╰──── × Unexpected trailing comma after rest element @@ -34467,11 +34305,11 @@ Negative Passed: 4519/4519 (100.00%) · ─ ╰──── - × Unexpected token - ╭─[test262/test/language/statements/for-in/dstr/array-rest-before-rest.js:33:6] + × Spread must be last element + ╭─[test262/test/language/statements/for-in/dstr/array-rest-before-rest.js:33:7] 32 │ 33 │ for ([...x, ...y] in [[]]) ; - · ──────────── + · ──── ╰──── × Unexpected trailing comma after rest element @@ -34959,11 +34797,11 @@ Negative Passed: 4519/4519 (100.00%) · ───── ╰──── - × Unexpected token - ╭─[test262/test/language/statements/for-of/dstr/array-rest-before-element.js:33:6] + × Spread must be last element + ╭─[test262/test/language/statements/for-of/dstr/array-rest-before-element.js:33:7] 32 │ 33 │ for ([...x, y] of [[]]) ; - · ───────── + · ──── ╰──── × Unexpected trailing comma after rest element @@ -34973,11 +34811,11 @@ Negative Passed: 4519/4519 (100.00%) · ─ ╰──── - × Unexpected token - ╭─[test262/test/language/statements/for-of/dstr/array-rest-before-rest.js:33:6] + × Spread must be last element + ╭─[test262/test/language/statements/for-of/dstr/array-rest-before-rest.js:33:7] 32 │ 33 │ for ([...x, ...y] of [[]]) ; - · ──────────── + · ──── ╰──── × Unexpected trailing comma after rest element diff --git a/tasks/coverage/snapshots/parser_typescript.snap b/tasks/coverage/snapshots/parser_typescript.snap index cf738f3c61ae9..b23e476e32bd4 100644 --- a/tasks/coverage/snapshots/parser_typescript.snap +++ b/tasks/coverage/snapshots/parser_typescript.snap @@ -1,7 +1,7 @@ commit: 15392346 parser_typescript Summary: -AST Parsed : 6522/6531 (99.86%) +AST Parsed : 6523/6531 (99.88%) Positive Passed: 6511/6531 (99.69%) Negative Passed: 1308/5754 (22.73%) Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ClassDeclaration24.ts @@ -12316,11 +12316,12 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private ╰──── help: Try insert a semicolon here - × Unexpected token + × Expected `<` but found `EOF` ╭─[typescript/tests/cases/compiler/errorSpanForUnclosedJsxTag.tsx:11:18] 10 │ 11 │ let y = < Baz >Hello - · ───── + · ──┬── + · ╰── `<` expected ╰──── × 'with' statements are not allowed @@ -15433,18 +15434,20 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private · ─ ╰──── - × Unexpected token + × Expected `<` but found `EOF` ╭─[typescript/tests/cases/compiler/parseUnaryExpressionNoTypeAssertionInJsx1.ts:2:21] 1 │ const x = "oops"; 2 │ const y = + x; - · ─── + · ─┬─ + · ╰── `<` expected ╰──── - × Unexpected token + × Expected `<` but found `EOF` ╭─[typescript/tests/cases/compiler/parseUnaryExpressionNoTypeAssertionInJsx2.ts:2:15] 1 │ const x = "oops"; 2 │ const y = + <> x; - · ─── + · ─┬─ + · ╰── `<` expected ╰──── × Unexpected token @@ -25901,6 +25904,15 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private 3 │ return; ╰──── + × Expected `}` but found `Unknown` + ╭─[typescript/tests/cases/conformance/parser/ecmascript5/ErrorRecovery/Blocks/parserErrorRecovery_Block2.ts:2:5] + 1 │ function f() { + 2 │ ¬ + · ┬ + · ╰── `}` expected + 3 │ return; + ╰──── + × Expected a semicolon or an implicit semicolon after a statement, but found none ╭─[typescript/tests/cases/conformance/parser/ecmascript5/ErrorRecovery/Blocks/parserErrorRecovery_Block3.ts:4:12] 3 │ @@ -25936,6 +25948,15 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private 3 │ class C { ╰──── + × Expected `}` but found `Unknown` + ╭─[typescript/tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ClassElements/parserErrorRecovery_ClassElement3.ts:2:4] + 1 │ module M { + 2 │ ¬ + · ┬ + · ╰── `}` expected + 3 │ class C { + ╰──── + × Empty parenthesized expression ╭─[typescript/tests/cases/conformance/parser/ecmascript5/ErrorRecovery/Expressions/parserErrorRecovery_Expression1.ts:1:9] 1 │ var v = ()({}); @@ -27212,11 +27233,6 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private 1 │ foo(a, \ ╰──── - × Expected `)` but found `EOF` - ╭─[typescript/tests/cases/conformance/parser/ecmascript5/SkippedTokens/parserSkippedTokens17.ts:1:9] - 1 │ foo(a, \ - ╰──── - × Invalid Unicode escape sequence ╭─[typescript/tests/cases/conformance/parser/ecmascript5/SkippedTokens/parserSkippedTokens18.ts:1:8] 1 │ foo(a \