refactor(parser): improve parse_simple_arrow_function_expression (#3349)

This commit is contained in:
Boshen 2024-05-19 06:05:39 +00:00
parent d2e1dbc82a
commit e818fba21c
9 changed files with 104 additions and 122 deletions

View file

@ -18,7 +18,7 @@ fn main() -> Result<(), String> {
let now = std::time::Instant::now(); let now = std::time::Instant::now();
let ret = Parser::new(&allocator, &source_text, source_type).parse(); let ret = Parser::new(&allocator, &source_text, source_type).parse();
let elapsed_time = now.elapsed(); let elapsed_time = now.elapsed();
println!("{} seconds.", elapsed_time.as_millis()); println!("{}ms.", elapsed_time.as_millis());
println!("AST:"); println!("AST:");
println!("{}", serde_json::to_string_pretty(&ret.program).unwrap()); println!("{}", serde_json::to_string_pretty(&ret.program).unwrap());

View file

@ -402,8 +402,3 @@ pub fn static_constructor(span0: Span) -> OxcDiagnostic {
OxcDiagnostic::error("TS1089: `static` modifier cannot appear on a constructor declaration.") OxcDiagnostic::error("TS1089: `static` modifier cannot appear on a constructor declaration.")
.with_labels([span0.into()]) .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()])
}

View file

@ -29,26 +29,16 @@ impl<'a> ParserImpl<'a> {
pub(super) fn try_parse_async_simple_arrow_function_expression( pub(super) fn try_parse_async_simple_arrow_function_expression(
&mut self, &mut self,
) -> Result<Option<Expression<'a>>> { ) -> Result<Option<Expression<'a>>> {
let span = self.start_span(); if self.at(Kind::Async)
if self.cur_kind().is_binding_identifier() && self.is_un_parenthesized_async_arrow_function_worker() == Tristate::True
&& self.peek_at(Kind::Arrow)
&& !self.peek_token().is_on_new_line
{ {
self.parse_single_param_function_expression(span, false, false).map(Some) let span = self.start_span();
} else if self.at_async_no_new_line() self.bump_any(); // bump `async`
&& self.peek_kind().is_binding_identifier() return self
&& !self.peek_token().is_on_new_line .parse_simple_arrow_function_expression(span, /* is_async */ true)
&& self.nth_at(2, Kind::Arrow) .map(Some);
{
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)
} }
Ok(None)
} }
fn is_parenthesized_arrow_function_expression(&mut self) -> Tristate { fn is_parenthesized_arrow_function_expression(&mut self) -> Tristate {
@ -56,10 +46,6 @@ impl<'a> ParserImpl<'a> {
Kind::LParen | Kind::LAngle | Kind::Async => { Kind::LParen | Kind::LAngle | Kind::Async => {
self.is_parenthesized_arrow_function_expression_worker() 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, _ => 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<Expression<'a>> {
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<ArrowFunctionHead<'a>> { fn parse_parenthesized_arrow_function_head(&mut self) -> Result<ArrowFunctionHead<'a>> {
let span = self.start_span(); let span = self.start_span();
let r#async = self.eat(Kind::Async); let r#async = self.eat(Kind::Async);
@ -269,61 +317,6 @@ impl<'a> ParserImpl<'a> {
.map(Some) .map(Some)
} }
fn parse_single_param_function_expression(
&mut self,
span: Span,
r#async: bool,
generator: bool,
) -> Result<Expression<'a>> {
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( fn parse_possible_parenthesized_arrow_function_expression(
&mut self, &mut self,
) -> Result<Option<Expression<'a>>> { ) -> Result<Option<Expression<'a>>> {

View file

@ -932,25 +932,25 @@ impl<'a> ParserImpl<'a> {
Ok(self.ast.conditional_expression(self.end_span(span), lhs, consequent, alternate)) Ok(self.ast.conditional_expression(self.end_span(span), lhs, consequent, alternate))
} }
pub(crate) fn parse_assignment_expression_or_higher(&mut self) -> Result<Expression<'a>> {
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] : /// `AssignmentExpression`[In, Yield, Await] :
pub(crate) fn parse_assignment_expression(&mut self) -> Result<Expression<'a>> { pub(crate) fn parse_assignment_expression_or_higher(&mut self) -> Result<Expression<'a>> {
// [+Yield] YieldExpression // [+Yield] YieldExpression
if self.is_yield_expression() { if self.is_yield_expression() {
return self.parse_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(); 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()?; let lhs = self.parse_conditional_expression()?;
self.parse_assignment_expression_recursive(span, lhs) self.parse_assignment_expression_recursive(span, lhs)
} }
@ -1044,10 +1044,13 @@ impl<'a> ParserImpl<'a> {
fn is_yield_expression(&mut self) -> bool { fn is_yield_expression(&mut self) -> bool {
if self.at(Kind::Yield) { if self.at(Kind::Yield) {
let peek_token = self.peek_token();
if peek_token.kind == Kind::Arrow {
return false;
}
if self.ctx.has_yield() { if self.ctx.has_yield() {
return true; return true;
} }
let peek_token = self.peek_token();
return peek_token.kind.is_after_await_or_yield() && !peek_token.is_on_new_line; return peek_token.kind.is_after_await_or_yield() && !peek_token.is_on_new_line;
} }
false false

View file

@ -27,10 +27,6 @@ impl<'a> ParserImpl<'a> {
&& !self.peek_token().is_on_new_line && !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<Box<'a, FunctionBody<'a>>> { pub(crate) fn parse_function_body(&mut self) -> Result<Box<'a, FunctionBody<'a>>> {
let span = self.start_span(); let span = self.start_span();
self.expect(Kind::LCurly)?; self.expect(Kind::LCurly)?;

View file

@ -16,7 +16,7 @@ mod object;
mod operator; mod operator;
mod statement; mod statement;
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum Tristate { pub enum Tristate {
True, True,
False, False,
@ -31,14 +31,14 @@ pub enum FunctionKind {
TSDeclaration, TSDeclaration,
} }
#[derive(Clone, Debug, Copy, Eq, PartialEq)] #[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum VariableDeclarationParent { pub enum VariableDeclarationParent {
For, For,
Statement, Statement,
Clause, Clause,
} }
#[derive(Clone, Debug, Copy, Eq, PartialEq)] #[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct VariableDeclarationContext { pub struct VariableDeclarationContext {
pub parent: VariableDeclarationParent, pub parent: VariableDeclarationParent,
} }

View file

@ -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 × Keywords cannot contain escape characters
╭─[es2017/async-functions/invalid-escape-sequence-arrow/input.js:1:11] ╭─[es2017/async-functions/invalid-escape-sequence-arrow/input.js:1:1]
1 │ \u0061sync x => { await x } 1 │ \u0061sync x => { await x }
· · ─────────
╰──── ╰────
help: Try insert a semicolon here
× Expected `,` but found `Identifier` × Keywords cannot contain escape characters
╭─[es2017/async-functions/invalid-escape-sequence-arrow-list/input.js:1:13] ╭─[es2017/async-functions/invalid-escape-sequence-arrow-list/input.js:1:2]
1 │ (\u0061sync x => { await x }) 1 │ (\u0061sync x => { await x })
· ┬ · ──────────
· ╰── `,` expected
╰──── ╰────
× Keywords cannot contain escape characters × 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 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] ╭─[es2017/async-functions/newline-before-arrow/input.js:2:1]
1 │ async x 1 │ async x
2 │ => x 2 │ => x

View file

@ -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] ╭─[language/expressions/arrow-function/syntax/early-errors/asi-restriction-invalid-parenless-parameters-expression-body.js:16:1]
15 │ var af = x 15 │ var af = x
16 │ => 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] ╭─[language/expressions/arrow-function/syntax/early-errors/asi-restriction-invalid-parenless-parameters.js:18:1]
17 │ var af = x 17 │ var af = x
18 │ => {}; 18 │ => {};
· ─┬ · ──
· ╰── `(` expected
╰──── ╰────
× Line terminator not permitted before arrow × Line terminator not permitted before arrow

View file

@ -12407,12 +12407,11 @@ Expect to Parse: "conformance/salsa/plainJSRedeclare3.ts"
69 │ } 69 │ }
╰──── ╰────
× Expected `(` but found `=>` × Line terminator not permitted before arrow
╭─[conformance/es6/arrowFunction/disallowLineTerminatorBeforeArrow.ts:72:9] ╭─[conformance/es6/arrowFunction/disallowLineTerminatorBeforeArrow.ts:72:9]
71 │ export var v = x 71 │ export var v = x
72 │ => new City(Enum.claw); 72 │ => new City(Enum.claw);
· ─┬ · ──
· ╰── `(` expected
73 │ } 73 │ }
╰──── ╰────