mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 20:32:10 +00:00
refactor(parser): improve parse_simple_arrow_function_expression (#3349)
This commit is contained in:
parent
d2e1dbc82a
commit
e818fba21c
9 changed files with 104 additions and 122 deletions
|
|
@ -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());
|
||||||
|
|
|
||||||
|
|
@ -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()])
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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>>> {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)?;
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 │ }
|
||||||
╰────
|
╰────
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue