Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 91 additions & 20 deletions crates/oxc_parser/src/js/arrow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,28 @@ struct ArrowFunctionHead<'a> {
return_type: Option<Box<'a, TSTypeAnnotation<'a>>>,
r#async: bool,
span: Span,
has_return_colon: bool,
}

impl<'a> ParserImpl<'a> {
pub(super) fn try_parse_parenthesized_arrow_function_expression(
&mut self,
allow_return_type_in_arrow_function: bool,
) -> Result<Option<Expression<'a>>> {
match self.is_parenthesized_arrow_function_expression() {
Tristate::False => Ok(None),
Tristate::True => self.parse_parenthesized_arrow_function(),
Tristate::Maybe => self.parse_possible_parenthesized_arrow_function_expression(),
Tristate::True => self.parse_parenthesized_arrow_function_expression(
/* allow_return_type_in_arrow_function */ true,
),
Tristate::Maybe => self.parse_possible_parenthesized_arrow_function_expression(
allow_return_type_in_arrow_function,
),
}
}

pub(super) fn try_parse_async_simple_arrow_function_expression(
&mut self,
allow_return_type_in_arrow_function: bool,
) -> Result<Option<Expression<'a>>> {
if self.at(Kind::Async)
&& self.is_un_parenthesized_async_arrow_function_worker() == Tristate::True
Expand All @@ -36,7 +43,12 @@ impl<'a> ParserImpl<'a> {
self.bump_any(); // bump `async`
let expr = self.parse_binary_expression_or_higher(Precedence::Comma)?;
return self
.parse_simple_arrow_function_expression(span, expr, /* async */ true)
.parse_simple_arrow_function_expression(
span,
expr,
/* async */ true,
allow_return_type_in_arrow_function,
)
.map(Some);
}
Ok(None)
Expand Down Expand Up @@ -204,6 +216,7 @@ impl<'a> ParserImpl<'a> {
span: Span,
ident: Expression<'a>,
r#async: bool,
allow_return_type_in_arrow_function: bool,
) -> Result<Expression<'a>> {
let has_await = self.ctx.has_await();
self.ctx = self.ctx.union_await_if(r#async);
Expand Down Expand Up @@ -236,13 +249,17 @@ impl<'a> ParserImpl<'a> {

self.expect(Kind::Arrow)?;

self.parse_arrow_function_body(ArrowFunctionHead {
type_parameters: None,
params,
return_type: None,
r#async,
span,
})
self.parse_arrow_function_expression_body(
ArrowFunctionHead {
type_parameters: None,
params,
return_type: None,
r#async,
span,
has_return_colon: false,
},
allow_return_type_in_arrow_function,
)
}

fn parse_parenthesized_arrow_function_head(&mut self) -> Result<ArrowFunctionHead<'a>> {
Expand All @@ -262,6 +279,7 @@ impl<'a> ParserImpl<'a> {
self.error(diagnostics::ts_arrow_function_this_parameter(this_param.span));
}

let has_return_colon = self.is_ts && self.at(Kind::Colon);
let return_type = self.parse_ts_return_type_annotation(Kind::Arrow, false)?;

self.ctx = self.ctx.and_await(has_await);
Expand All @@ -272,27 +290,36 @@ impl<'a> ParserImpl<'a> {

self.expect(Kind::Arrow)?;

Ok(ArrowFunctionHead { type_parameters, params, return_type, r#async, span })
Ok(ArrowFunctionHead {
type_parameters,
params,
return_type,
r#async,
span,
has_return_colon,
})
}

/// [ConciseBody](https://tc39.es/ecma262/#prod-ConciseBody)
/// [lookahead ≠ {] `ExpressionBody`[?In, ~Await]
/// { `FunctionBody`[~Yield, ~Await] }
/// `ExpressionBody`[In, Await] :
/// `AssignmentExpression`[?In, ~Yield, ?Await]
fn parse_arrow_function_body(
fn parse_arrow_function_expression_body(
&mut self,
arrow_function_head: ArrowFunctionHead<'a>,
allow_return_type_in_arrow_function: bool,
) -> Result<Expression<'a>> {
let ArrowFunctionHead { type_parameters, params, return_type, r#async, span } =
let ArrowFunctionHead { type_parameters, params, return_type, r#async, span, .. } =
arrow_function_head;
let has_await = self.ctx.has_await();
let has_yield = self.ctx.has_yield();
self.ctx = self.ctx.and_await(r#async).and_yield(false);

let expression = !self.at(Kind::LCurly);
let body = if expression {
let expr = self.parse_assignment_expression_or_higher()?;
let expr = self
.parse_assignment_expression_or_higher_impl(allow_return_type_in_arrow_function)?;
let span = expr.span();
let expr_stmt = self.ast.statement_expression(span, expr);
self.ast.alloc_function_body(span, self.ast.vec(), self.ast.vec1(expr_stmt))
Expand All @@ -316,22 +343,66 @@ impl<'a> ParserImpl<'a> {
/// Section [Arrow Function](https://tc39.es/ecma262/#sec-arrow-function-definitions)
/// `ArrowFunction`[In, Yield, Await] :
/// `ArrowParameters`[?Yield, ?Await] [no `LineTerminator` here] => `ConciseBody`[?In]
fn parse_parenthesized_arrow_function(&mut self) -> Result<Option<Expression<'a>>> {
fn parse_parenthesized_arrow_function_expression(
&mut self,
allow_return_type_in_arrow_function: bool,
) -> Result<Option<Expression<'a>>> {
let head = self.parse_parenthesized_arrow_function_head()?;
self.parse_arrow_function_body(head).map(Some)
self.parse_arrow_function_expression_body(head, allow_return_type_in_arrow_function)
.map(Some)
}

fn parse_possible_parenthesized_arrow_function_expression(
&mut self,
allow_return_type_in_arrow_function: bool,
) -> Result<Option<Expression<'a>>> {
let pos = self.cur_token().start;
if self.state.not_parenthesized_arrow.contains(&pos) {
return Ok(None);
}
if let Some(head) = self.try_parse(ParserImpl::parse_parenthesized_arrow_function_head) {
return self.parse_arrow_function_body(head).map(Some);

let checkpoint = self.checkpoint();

let Ok(head) = self.parse_parenthesized_arrow_function_head() else {
self.state.not_parenthesized_arrow.insert(pos);
self.rewind(checkpoint);
return Ok(None);
};

let has_return_colon = head.has_return_colon;

let body =
self.parse_arrow_function_expression_body(head, allow_return_type_in_arrow_function)?;

// Given:
// x ? y => ({ y }) : z => ({ z })
// We try to parse the body of the first arrow function by looking at:
// ({ y }) : z => ({ z })
// This is a valid arrow function with "z" as the return type.
//
// But, if we're in the true side of a conditional expression, this colon
// terminates the expression, so we cannot allow a return type if we aren't
// certain whether or not the preceding text was parsed as a parameter list.
//
// For example,
// a() ? (b: number, c?: string): void => d() : e
// is determined by isParenthesizedArrowFunctionExpression to unambiguously
// be an arrow expression, so we allow a return type.
if !allow_return_type_in_arrow_function && has_return_colon {
// However, if the arrow function we were able to parse is followed by another colon
// as in:
// a ? (x): string => x : null
// Then allow the arrow function, and treat the second colon as terminating
// the conditional expression. It's okay to do this because this code would
// be a syntax error in JavaScript (as the second colon shouldn't be there).

if !self.at(Kind::Colon) {
self.state.not_parenthesized_arrow.insert(pos);
self.rewind(checkpoint);
return Ok(None);
}
}
self.state.not_parenthesized_arrow.insert(pos);
Ok(None)

Ok(Some(body))
}
}
52 changes: 39 additions & 13 deletions crates/oxc_parser/src/js/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1076,32 +1076,47 @@ impl<'a> ParserImpl<'a> {
&mut self,
lhs_span: Span,
lhs: Expression<'a>,
allow_return_type_in_arrow_function: bool,
) -> Result<Expression<'a>> {
if !self.eat(Kind::Question) {
return Ok(lhs);
}
let consequent = self.context(
Context::In,
Context::empty(),
Self::parse_assignment_expression_or_higher,
)?;
let consequent = self.context(Context::In, Context::empty(), |p| {
p.parse_assignment_expression_or_higher_impl(
/* allow_return_type_in_arrow_function */ false,
)
})?;
self.expect(Kind::Colon)?;
let alternate = self.parse_assignment_expression_or_higher()?;
let alternate =
self.parse_assignment_expression_or_higher_impl(allow_return_type_in_arrow_function)?;
Ok(self.ast.expression_conditional(self.end_span(lhs_span), lhs, consequent, alternate))
}

/// `AssignmentExpression`[In, Yield, Await] :
pub(crate) fn parse_assignment_expression_or_higher(&mut self) -> Result<Expression<'a>> {
self.parse_assignment_expression_or_higher_impl(
/* allow_return_type_in_arrow_function */ true,
)
}

pub(crate) fn parse_assignment_expression_or_higher_impl(
&mut self,
allow_return_type_in_arrow_function: bool,
) -> Result<Expression<'a>> {
// [+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()? {
// `() => {}`, `(x) => {}`
if let Some(arrow_expr) = self.try_parse_parenthesized_arrow_function_expression(
allow_return_type_in_arrow_function,
)? {
return Ok(arrow_expr);
}
// `async x => {}`
if let Some(arrow_expr) = self.try_parse_async_simple_arrow_function_expression()? {
if let Some(arrow_expr) = self
.try_parse_async_simple_arrow_function_expression(allow_return_type_in_arrow_function)?
{
return Ok(arrow_expr);
}

Expand All @@ -1111,20 +1126,30 @@ impl<'a> ParserImpl<'a> {

// `x => {}`
if lhs.is_identifier_reference() && kind == Kind::Arrow {
return self.parse_simple_arrow_function_expression(span, lhs, /* async */ false);
return self.parse_simple_arrow_function_expression(
span,
lhs,
/* async */ false,
allow_return_type_in_arrow_function,
);
}

if kind.is_assignment_operator() {
return self.parse_assignment_expression_recursive(span, lhs);
return self.parse_assignment_expression_recursive(
span,
lhs,
allow_return_type_in_arrow_function,
);
}

self.parse_conditional_expression_rest(span, lhs)
self.parse_conditional_expression_rest(span, lhs, allow_return_type_in_arrow_function)
}

fn parse_assignment_expression_recursive(
&mut self,
span: Span,
lhs: Expression<'a>,
allow_return_type_in_arrow_function: bool,
) -> Result<Expression<'a>> {
let operator = map_assignment_operator(self.cur_kind());
// 13.15.5 Destructuring Assignment
Expand All @@ -1135,7 +1160,8 @@ impl<'a> ParserImpl<'a> {
// ArrayAssignmentPattern
let left = AssignmentTarget::cover(lhs, self)?;
self.bump_any();
let right = self.parse_assignment_expression_or_higher()?;
let right =
self.parse_assignment_expression_or_higher_impl(allow_return_type_in_arrow_function)?;
Ok(self.ast.expression_assignment(self.end_span(span), operator, left, right))
}

Expand Down
1 change: 1 addition & 0 deletions tasks/coverage/misc/pass/oxc-9215.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a ? b ? (c = 0) : d => 1 : (e = 2) : f => 3;
4 changes: 2 additions & 2 deletions tasks/coverage/snapshots/codegen_misc.snap
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
codegen_misc Summary:
AST Parsed : 31/31 (100.00%)
Positive Passed: 31/31 (100.00%)
AST Parsed : 32/32 (100.00%)
Positive Passed: 32/32 (100.00%)
Loading
Loading