diff --git a/crates/oxc_parser/examples/parser.rs b/crates/oxc_parser/examples/parser.rs index c789513ca..2d217065c 100644 --- a/crates/oxc_parser/examples/parser.rs +++ b/crates/oxc_parser/examples/parser.rs @@ -18,7 +18,7 @@ fn main() -> Result<(), String> { let now = std::time::Instant::now(); let ret = Parser::new(&allocator, &source_text, source_type).parse(); let elapsed_time = now.elapsed(); - println!("{} seconds.", elapsed_time.as_millis()); + println!("{}ms.", elapsed_time.as_millis()); println!("AST:"); println!("{}", serde_json::to_string_pretty(&ret.program).unwrap()); diff --git a/crates/oxc_parser/src/diagnostics.rs b/crates/oxc_parser/src/diagnostics.rs index 7150a2247..d25e8ef74 100644 --- a/crates/oxc_parser/src/diagnostics.rs +++ b/crates/oxc_parser/src/diagnostics.rs @@ -402,8 +402,3 @@ pub fn static_constructor(span0: Span) -> OxcDiagnostic { OxcDiagnostic::error("TS1089: `static` modifier cannot appear on a constructor declaration.") .with_labels([span0.into()]) } - -#[cold] -pub fn no_line_break_is_allowed_before_arrow(span0: Span) -> OxcDiagnostic { - OxcDiagnostic::error("No line break is allowed before '=>'.").with_labels([span0.into()]) -} diff --git a/crates/oxc_parser/src/js/arrow.rs b/crates/oxc_parser/src/js/arrow.rs index 487e82ce8..57b95fa43 100644 --- a/crates/oxc_parser/src/js/arrow.rs +++ b/crates/oxc_parser/src/js/arrow.rs @@ -29,26 +29,16 @@ impl<'a> ParserImpl<'a> { pub(super) fn try_parse_async_simple_arrow_function_expression( &mut self, ) -> Result>> { - let span = self.start_span(); - if self.cur_kind().is_binding_identifier() - && self.peek_at(Kind::Arrow) - && !self.peek_token().is_on_new_line + if self.at(Kind::Async) + && self.is_un_parenthesized_async_arrow_function_worker() == Tristate::True { - self.parse_single_param_function_expression(span, false, false).map(Some) - } else if self.at_async_no_new_line() - && self.peek_kind().is_binding_identifier() - && !self.peek_token().is_on_new_line - && self.nth_at(2, Kind::Arrow) - { - self.bump_any(); // bump async - let arrow_token = self.peek_token(); - if arrow_token.is_on_new_line { - self.error(diagnostics::no_line_break_is_allowed_before_arrow(arrow_token.span())); - } - self.parse_single_param_function_expression(span, true, false).map(Some) - } else { - Ok(None) + let span = self.start_span(); + self.bump_any(); // bump `async` + return self + .parse_simple_arrow_function_expression(span, /* is_async */ true) + .map(Some); } + Ok(None) } fn is_parenthesized_arrow_function_expression(&mut self) -> Tristate { @@ -56,10 +46,6 @@ impl<'a> ParserImpl<'a> { Kind::LParen | Kind::LAngle | Kind::Async => { self.is_parenthesized_arrow_function_expression_worker() } - // ERROR RECOVERY TWEAK: - // If we see a standalone => try to parse it as an arrow function expression as that's - // likely what the user intended to write. - Kind::Arrow => Tristate::True, _ => Tristate::False, } } @@ -189,6 +175,68 @@ impl<'a> ParserImpl<'a> { } } + fn is_un_parenthesized_async_arrow_function_worker(&mut self) -> Tristate { + if self.at(Kind::Async) { + let first_token = self.peek_token(); + let first = first_token.kind; + // If the "async" is followed by "=>" token then it is not a beginning of an async arrow-function + // but instead a simple arrow-function which will be parsed inside "parseAssignmentExpressionOrHigher" + if first_token.is_on_new_line || first == Kind::Arrow { + return Tristate::False; + } + // Check for un-parenthesized AsyncArrowFunction + if first.is_binding_identifier() { + // Arrow before newline is checkedin `parse_simple_arrow_function_expression` + if self.nth_at(2, Kind::Arrow) { + return Tristate::True; + } + } + } + Tristate::False + } + + pub(crate) fn parse_simple_arrow_function_expression( + &mut self, + span: Span, + r#async: bool, + ) -> Result> { + let has_await = self.ctx.has_await(); + self.ctx = self.ctx.union_await_if(r#async); + + let params = { + let params_span = self.start_span(); + let param = self.parse_binding_identifier()?; + let ident = self.ast.binding_pattern_identifier(param); + let params_span = self.end_span(params_span); + let formal_parameter = self.ast.formal_parameter( + params_span, + self.ast.binding_pattern(ident, None, false), + None, + false, + false, + AstBuilder::new_vec(&self.ast), + ); + self.ast.formal_parameters( + params_span, + FormalParameterKind::ArrowFormalParameters, + self.ast.new_vec_single(formal_parameter), + None, + ) + }; + + self.ctx = self.ctx.and_await(has_await); + + if self.cur_token().is_on_new_line { + self.error(diagnostics::lineterminator_before_arrow(self.cur_token().span())); + } + + self.expect(Kind::Arrow)?; + + self.parse_arrow_function_body( + span, /* type_parameters */ None, params, /* return_type */ None, r#async, + ) + } + fn parse_parenthesized_arrow_function_head(&mut self) -> Result> { let span = self.start_span(); let r#async = self.eat(Kind::Async); @@ -269,61 +317,6 @@ impl<'a> ParserImpl<'a> { .map(Some) } - fn parse_single_param_function_expression( - &mut self, - span: Span, - r#async: bool, - generator: bool, - ) -> Result> { - let has_await = self.ctx.has_await(); - let has_yield = self.ctx.has_yield(); - - self.ctx = self.ctx.union_await_if(r#async).union_yield_if(generator); - let params_span = self.start_span(); - let param = self.parse_binding_identifier()?; - let ident = self.ast.binding_pattern_identifier(param); - let pattern = self.ast.binding_pattern(ident, None, false); - let params_span = self.end_span(params_span); - let formal_parameter = self.ast.formal_parameter( - params_span, - pattern, - None, - false, - false, - AstBuilder::new_vec(&self.ast), - ); - let params = self.ast.formal_parameters( - params_span, - FormalParameterKind::ArrowFormalParameters, - self.ast.new_vec_single(formal_parameter), - None, - ); - - self.expect(Kind::Arrow)?; - - self.ctx = self.ctx.and_await(r#async).and_yield(generator); - let expression = !self.at(Kind::LCurly); - let body = if expression { - let expr = self.parse_assignment_expression_or_higher()?; - let span = expr.span(); - let expr_stmt = self.ast.expression_statement(span, expr); - self.ast.function_body(span, self.ast.new_vec(), self.ast.new_vec_single(expr_stmt)) - } else { - self.parse_function_body()? - }; - self.ctx = self.ctx.and_await(has_await).and_yield(has_yield); - - Ok(self.ast.arrow_function_expression( - self.end_span(span), - expression, - r#async, - params, - body, - None, - None, - )) - } - fn parse_possible_parenthesized_arrow_function_expression( &mut self, ) -> Result>> { diff --git a/crates/oxc_parser/src/js/expression.rs b/crates/oxc_parser/src/js/expression.rs index 5e042c40a..1e7f2c2bf 100644 --- a/crates/oxc_parser/src/js/expression.rs +++ b/crates/oxc_parser/src/js/expression.rs @@ -932,25 +932,25 @@ impl<'a> ParserImpl<'a> { Ok(self.ast.conditional_expression(self.end_span(span), lhs, consequent, alternate)) } - pub(crate) fn parse_assignment_expression_or_higher(&mut self) -> Result> { - if let Some(arrow_expr) = self.try_parse_parenthesized_arrow_function_expression()? { - return Ok(arrow_expr); - } - if let Some(arrow_expr) = self.try_parse_async_simple_arrow_function_expression()? { - return Ok(arrow_expr); - } - self.parse_assignment_expression() - } - /// `AssignmentExpression`[In, Yield, Await] : - pub(crate) fn parse_assignment_expression(&mut self) -> Result> { + pub(crate) fn parse_assignment_expression_or_higher(&mut self) -> Result> { // [+Yield] YieldExpression if self.is_yield_expression() { return self.parse_yield_expression(); } - + // `(x) => {}` + if let Some(arrow_expr) = self.try_parse_parenthesized_arrow_function_expression()? { + return Ok(arrow_expr); + } + // `async x => {}` + if let Some(arrow_expr) = self.try_parse_async_simple_arrow_function_expression()? { + return Ok(arrow_expr); + } let span = self.start_span(); - + // `x => {}` + if self.cur_kind().is_binding_identifier() && self.peek_at(Kind::Arrow) { + return self.parse_simple_arrow_function_expression(span, /* r#async */ false); + } let lhs = self.parse_conditional_expression()?; self.parse_assignment_expression_recursive(span, lhs) } @@ -1044,10 +1044,13 @@ impl<'a> ParserImpl<'a> { fn is_yield_expression(&mut self) -> bool { if self.at(Kind::Yield) { + let peek_token = self.peek_token(); + if peek_token.kind == Kind::Arrow { + return false; + } if self.ctx.has_yield() { return true; } - let peek_token = self.peek_token(); return peek_token.kind.is_after_await_or_yield() && !peek_token.is_on_new_line; } false diff --git a/crates/oxc_parser/src/js/function.rs b/crates/oxc_parser/src/js/function.rs index f62f8e1fb..e3d7bd22e 100644 --- a/crates/oxc_parser/src/js/function.rs +++ b/crates/oxc_parser/src/js/function.rs @@ -27,10 +27,6 @@ impl<'a> ParserImpl<'a> { && !self.peek_token().is_on_new_line } - pub(crate) fn at_async_no_new_line(&mut self) -> bool { - self.at(Kind::Async) && !self.cur_token().escaped() && !self.peek_token().is_on_new_line - } - pub(crate) fn parse_function_body(&mut self) -> Result>> { let span = self.start_span(); self.expect(Kind::LCurly)?; diff --git a/crates/oxc_parser/src/js/mod.rs b/crates/oxc_parser/src/js/mod.rs index 229577973..28bb73a26 100644 --- a/crates/oxc_parser/src/js/mod.rs +++ b/crates/oxc_parser/src/js/mod.rs @@ -16,7 +16,7 @@ mod object; mod operator; mod statement; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum Tristate { True, False, @@ -31,14 +31,14 @@ pub enum FunctionKind { TSDeclaration, } -#[derive(Clone, Debug, Copy, Eq, PartialEq)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum VariableDeclarationParent { For, Statement, Clause, } -#[derive(Clone, Debug, Copy, Eq, PartialEq)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct VariableDeclarationContext { pub parent: VariableDeclarationParent, } diff --git a/tasks/coverage/parser_babel.snap b/tasks/coverage/parser_babel.snap index 4d56fba76..eb3223105 100644 --- a/tasks/coverage/parser_babel.snap +++ b/tasks/coverage/parser_babel.snap @@ -4997,18 +4997,16 @@ Expect to Parse: "typescript/types/const-type-parameters-babel-7/input.ts" · ────────── ╰──── - × Expected a semicolon or an implicit semicolon after a statement, but found none - ╭─[es2017/async-functions/invalid-escape-sequence-arrow/input.js:1:11] + × Keywords cannot contain escape characters + ╭─[es2017/async-functions/invalid-escape-sequence-arrow/input.js:1:1] 1 │ \u0061sync x => { await x } - · ─ + · ────────── ╰──── - help: Try insert a semicolon here - × Expected `,` but found `Identifier` - ╭─[es2017/async-functions/invalid-escape-sequence-arrow-list/input.js:1:13] + × Keywords cannot contain escape characters + ╭─[es2017/async-functions/invalid-escape-sequence-arrow-list/input.js:1:2] 1 │ (\u0061sync x => { await x }) - · ┬ - · ╰── `,` expected + · ────────── ╰──── × Keywords cannot contain escape characters @@ -5090,7 +5088,7 @@ Expect to Parse: "typescript/types/const-type-parameters-babel-7/input.ts" ╰──── help: Try insert a semicolon here - × No line break is allowed before '=>'. + × Line terminator not permitted before arrow ╭─[es2017/async-functions/newline-before-arrow/input.js:2:1] 1 │ async x 2 │ => x diff --git a/tasks/coverage/parser_test262.snap b/tasks/coverage/parser_test262.snap index 7a153734d..e4f2aee88 100644 --- a/tasks/coverage/parser_test262.snap +++ b/tasks/coverage/parser_test262.snap @@ -2319,20 +2319,18 @@ Expect Syntax Error: "language/import/import-attributes/json-named-bindings.js" · ───── ╰──── - × Expected `(` but found `=>` + × Line terminator not permitted before arrow ╭─[language/expressions/arrow-function/syntax/early-errors/asi-restriction-invalid-parenless-parameters-expression-body.js:16:1] 15 │ var af = x 16 │ => x; - · ─┬ - · ╰── `(` expected + · ── ╰──── - × Expected `(` but found `=>` + × Line terminator not permitted before arrow ╭─[language/expressions/arrow-function/syntax/early-errors/asi-restriction-invalid-parenless-parameters.js:18:1] 17 │ var af = x 18 │ => {}; - · ─┬ - · ╰── `(` expected + · ── ╰──── × Line terminator not permitted before arrow diff --git a/tasks/coverage/parser_typescript.snap b/tasks/coverage/parser_typescript.snap index e311c1f7e..f57a532e7 100644 --- a/tasks/coverage/parser_typescript.snap +++ b/tasks/coverage/parser_typescript.snap @@ -12407,12 +12407,11 @@ Expect to Parse: "conformance/salsa/plainJSRedeclare3.ts" 69 │ } ╰──── - × Expected `(` but found `=>` + × Line terminator not permitted before arrow ╭─[conformance/es6/arrowFunction/disallowLineTerminatorBeforeArrow.ts:72:9] 71 │ export var v = x 72 │ => new City(Enum.claw); - · ─┬ - · ╰── `(` expected + · ── 73 │ } ╰────