Skip to content

Commit

Permalink
fix(es/parser): Fix parsing of arrow in cond (#6067)
Browse files Browse the repository at this point in the history
**Related issue:**

 - Closes #4911.
  • Loading branch information
kdy1 committed Oct 17, 2022
1 parent ec5782e commit 0ca5ded
Show file tree
Hide file tree
Showing 11 changed files with 2,375 additions and 14 deletions.
2 changes: 1 addition & 1 deletion crates/swc_ecma_parser/src/lib.rs
Expand Up @@ -361,7 +361,7 @@ pub struct Context {

/// If true, `:` should not be treated as a type annotation.
in_cond_expr: bool,
is_direct_child_of_cond: bool,
will_expect_colon_for_cond: bool,

in_class: bool,

Expand Down
1 change: 1 addition & 0 deletions crates/swc_ecma_parser/src/parser/class_and_fn.rs
Expand Up @@ -1105,6 +1105,7 @@ impl<I: Tokens> Parser<I> {
self.with_ctx(Context {
allow_direct_super: false,
in_class_field: false,
will_expect_colon_for_cond: false,
..self.ctx()
})
.parse_with(|p| {
Expand Down
52 changes: 40 additions & 12 deletions crates/swc_ecma_parser/src/parser/expr.rs
Expand Up @@ -37,6 +37,7 @@ impl<I: Tokens> Parser<I> {
}

///`parseMaybeAssign` (overridden)
#[cfg_attr(feature = "debug", tracing::instrument(skip_all))]
pub(super) fn parse_assignment_expr(&mut self) -> PResult<Box<Expr>> {
trace_cur!(self, parse_assignment_expr);

Expand Down Expand Up @@ -78,6 +79,7 @@ impl<I: Tokens> Parser<I> {
/// operators like `+=`.
///
/// `parseMaybeAssign`
#[cfg_attr(feature = "debug", tracing::instrument(skip_all))]
fn parse_assignment_expr_base(&mut self) -> PResult<Box<Expr>> {
trace_cur!(self, parse_assignment_expr_base);

Expand All @@ -86,7 +88,7 @@ impl<I: Tokens> Parser<I> {
&& (peeked_is!(self, IdentName) || peeked_is!(self, JSXName))
{
let ctx = Context {
is_direct_child_of_cond: false,
will_expect_colon_for_cond: false,
..self.ctx()
};
let res = self.with_ctx(ctx).try_parse_ts(|p| {
Expand Down Expand Up @@ -195,6 +197,7 @@ impl<I: Tokens> Parser<I> {
}

/// Spec: 'ConditionalExpression'
#[cfg_attr(feature = "debug", tracing::instrument(skip_all))]
fn parse_cond_expr(&mut self) -> PResult<Box<Expr>> {
trace_cur!(self, parse_cond_expr);

Expand All @@ -206,15 +209,16 @@ impl<I: Tokens> Parser<I> {
if eat!(self, '?') {
let ctx = Context {
in_cond_expr: true,
is_direct_child_of_cond: true,
will_expect_colon_for_cond: true,
include_in_expr: true,
..self.ctx()
};
let cons = self.with_ctx(ctx).parse_assignment_expr()?;
expect!(self, ':');
let ctx = Context {
in_cond_expr: true,
is_direct_child_of_cond: true,
will_expect_colon_for_cond: false,
dont_parse_colon_as_type_ann: false,
..self.ctx()
};
let alt = self.with_ctx(ctx).parse_assignment_expr()?;
Expand All @@ -231,6 +235,7 @@ impl<I: Tokens> Parser<I> {
}

/// Parse a primary expression or arrow function
#[cfg_attr(feature = "debug", tracing::instrument(skip_all))]
pub(super) fn parse_primary_expr(&mut self) -> PResult<Box<Expr>> {
trace_cur!(self, parse_primary_expr);

Expand Down Expand Up @@ -300,7 +305,7 @@ impl<I: Tokens> Parser<I> {

tok!('[') => {
let ctx = Context {
is_direct_child_of_cond: false,
will_expect_colon_for_cond: false,
dont_parse_colon_as_type_ann: false,
..self.ctx()
};
Expand Down Expand Up @@ -479,6 +484,7 @@ impl<I: Tokens> Parser<I> {
)
}

#[cfg_attr(feature = "debug", tracing::instrument(skip_all))]
fn parse_array_lit(&mut self) -> PResult<Box<Expr>> {
trace_cur!(self, parse_array_lit);

Expand Down Expand Up @@ -536,6 +542,7 @@ impl<I: Tokens> Parser<I> {
}

/// `is_new_expr`: true iff we are parsing production 'NewExpression'.
#[cfg_attr(feature = "debug", tracing::instrument(skip_all))]
fn parse_member_expr_or_new_expr(&mut self, is_new_expr: bool) -> PResult<Box<Expr>> {
trace_cur!(self, parse_member_expr_or_new_expr);

Expand Down Expand Up @@ -666,18 +673,20 @@ impl<I: Tokens> Parser<I> {

/// Parse `NewExpression`.
/// This includes `MemberExpression`.
#[cfg_attr(feature = "debug", tracing::instrument(skip_all))]
pub(super) fn parse_new_expr(&mut self) -> PResult<Box<Expr>> {
trace_cur!(self, parse_new_expr);

self.parse_member_expr_or_new_expr(true)
}

/// Parse `Arguments[Yield, Await]`
#[cfg_attr(feature = "debug", tracing::instrument(skip_all))]
pub(super) fn parse_args(&mut self, is_dynamic_import: bool) -> PResult<Vec<ExprOrSpread>> {
trace_cur!(self, parse_args);

let ctx = Context {
is_direct_child_of_cond: false,
will_expect_colon_for_cond: false,
..self.ctx()
};

Expand Down Expand Up @@ -734,6 +743,7 @@ impl<I: Tokens> Parser<I> {
}

/// Parse paren expression or arrow function expression.
#[cfg_attr(feature = "debug", tracing::instrument(skip_all))]
fn parse_paren_expr_or_arrow_fn(
&mut self,
can_be_arrow: bool,
Expand All @@ -749,7 +759,7 @@ impl<I: Tokens> Parser<I> {
// expressions, we can parse both as expression.

let ctx = Context {
is_direct_child_of_cond: false,
will_expect_colon_for_cond: false,
..self.ctx()
};

Expand All @@ -762,7 +772,7 @@ impl<I: Tokens> Parser<I> {
.iter()
.any(|item| matches!(item, PatOrExprOrSpread::Pat(..)));

let is_direct_child_of_cond = self.ctx().is_direct_child_of_cond;
let will_expect_colon_for_cond = self.ctx().will_expect_colon_for_cond;
// This is slow path. We handle arrow in conditional expression.
if self.syntax().typescript() && self.ctx().in_cond_expr && is!(self, ':') {
// TODO: Remove clone
Expand All @@ -784,7 +794,7 @@ impl<I: Tokens> Parser<I> {
params.is_simple_parameter_list(),
)?;

if is_direct_child_of_cond && !is_one_of!(p, ':', ';', ',', ')') {
if will_expect_colon_for_cond && !is!(p, ':') {
trace_cur!(p, parse_arrow_in_cond__fail);
unexpected!(p, "fail")
}
Expand All @@ -803,12 +813,20 @@ impl<I: Tokens> Parser<I> {
}
}

let return_type = if !(self.ctx().in_cond_expr && self.ctx().is_direct_child_of_cond)
let return_type = if !self.ctx().will_expect_colon_for_cond
&& self.input.syntax().typescript()
&& is!(self, ':')
&& !self.ctx().dont_parse_colon_as_type_ann
{
Some(self.parse_ts_type_or_type_predicate_ann(&tok!(':'))?)
self.try_parse_ts(|p| {
let return_type = p.parse_ts_type_or_type_predicate_ann(&tok!(':'))?;

if !is!(p, "=>") {
unexpected!(p, "fail")
}

Ok(Some(return_type))
})
} else {
None
};
Expand All @@ -822,6 +840,7 @@ impl<I: Tokens> Parser<I> {
SyntaxError::LineBreakBeforeArrow
);
}

if !can_be_arrow {
syntax_error!(self, span!(self, expr_start), SyntaxError::ArrowNotAllowed);
}
Expand Down Expand Up @@ -1568,7 +1587,16 @@ impl<I: Tokens> Parser<I> {
}

// Returns (args_or_pats, trailing_comma)
#[cfg_attr(feature = "debug", tracing::instrument(skip_all))]
pub(super) fn parse_args_or_pats(&mut self) -> PResult<(Vec<PatOrExprOrSpread>, Option<Span>)> {
self.with_ctx(Context {
will_expect_colon_for_cond: false,
..self.ctx()
})
.parse_args_or_pats_inner()
}

fn parse_args_or_pats_inner(&mut self) -> PResult<(Vec<PatOrExprOrSpread>, Option<Span>)> {
trace_cur!(self, parse_args_or_pats);

expect!(self, '(');
Expand Down Expand Up @@ -1651,15 +1679,15 @@ impl<I: Tokens> Parser<I> {
let test = arg.expr;
let ctx = Context {
in_cond_expr: true,
is_direct_child_of_cond: true,
will_expect_colon_for_cond: true,
include_in_expr: true,
..self.ctx()
};
let cons = self.with_ctx(ctx).parse_assignment_expr()?;
expect!(self, ':');
let ctx = Context {
in_cond_expr: true,
is_direct_child_of_cond: true,
will_expect_colon_for_cond: false,
..self.ctx()
};
let alt = self.with_ctx(ctx).parse_assignment_expr()?;
Expand Down
2 changes: 1 addition & 1 deletion crates/swc_ecma_parser/src/parser/object.rs
Expand Up @@ -13,7 +13,7 @@ impl<I: Tokens> Parser<I> {
Self: ParseObject<T>,
{
let ctx = Context {
is_direct_child_of_cond: false,
will_expect_colon_for_cond: false,
dont_parse_colon_as_type_ann: false,
..self.ctx()
};
Expand Down
2 changes: 2 additions & 0 deletions crates/swc_ecma_parser/src/parser/typescript.rs
Expand Up @@ -623,6 +623,7 @@ impl<I: Tokens> Parser<I> {
}
}

#[cfg_attr(feature = "debug", tracing::instrument(skip_all))]
pub(super) fn parse_ts_type_ann(
&mut self,
eat_colon: bool,
Expand Down Expand Up @@ -1954,6 +1955,7 @@ impl<I: Tokens> Parser<I> {
}

/// `tsTryParseTypeAnnotation`
#[cfg_attr(feature = "debug", tracing::instrument(skip_all))]
pub(super) fn try_parse_ts_type_ann(&mut self) -> PResult<Option<Box<TsTypeAnn>>> {
if !cfg!(feature = "typescript") {
return Ok(None);
Expand Down
@@ -0,0 +1 @@
const r=n=>{return true?(true):t=>{}}

1 comment on commit 0ca5ded

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Benchmark suite Current: 0ca5ded Previous: 9d93b6c Ratio
es/full/minify/libraries/antd 1877710906 ns/iter (± 15230011) 2073234894 ns/iter (± 19848985) 0.91
es/full/minify/libraries/d3 394421264 ns/iter (± 12443572) 398366787 ns/iter (± 15265870) 0.99
es/full/minify/libraries/echarts 1544585256 ns/iter (± 20932888) 1734209858 ns/iter (± 37477870) 0.89
es/full/minify/libraries/jquery 102436632 ns/iter (± 3869078) 118882879 ns/iter (± 8210495) 0.86
es/full/minify/libraries/lodash 115512377 ns/iter (± 3269359) 131580810 ns/iter (± 3308061) 0.88
es/full/minify/libraries/moment 58426556 ns/iter (± 1380743) 72251477 ns/iter (± 2517363) 0.81
es/full/minify/libraries/react 19682343 ns/iter (± 256091) 24814295 ns/iter (± 1582547) 0.79
es/full/minify/libraries/terser 298550547 ns/iter (± 7199902) 328828116 ns/iter (± 6827056) 0.91
es/full/minify/libraries/three 549069538 ns/iter (± 12579683) 607045653 ns/iter (± 18637696) 0.90
es/full/minify/libraries/typescript 3370116841 ns/iter (± 61007265) 4214543205 ns/iter (± 674186858) 0.80
es/full/minify/libraries/victory 784070492 ns/iter (± 13919686) 894493886 ns/iter (± 47348797) 0.88
es/full/minify/libraries/vue 137489440 ns/iter (± 2479108) 174329292 ns/iter (± 8921974) 0.79
es/full/codegen/es3 32437 ns/iter (± 1201) 41616 ns/iter (± 2311) 0.78
es/full/codegen/es5 31936 ns/iter (± 1124) 43094 ns/iter (± 4383) 0.74
es/full/codegen/es2015 32362 ns/iter (± 1521) 42807 ns/iter (± 3496) 0.76
es/full/codegen/es2016 32083 ns/iter (± 1067) 41620 ns/iter (± 1933) 0.77
es/full/codegen/es2017 31832 ns/iter (± 1110) 42127 ns/iter (± 1868) 0.76
es/full/codegen/es2018 31907 ns/iter (± 933) 42478 ns/iter (± 3888) 0.75
es/full/codegen/es2019 32178 ns/iter (± 1712) 42874 ns/iter (± 3762) 0.75
es/full/codegen/es2020 31838 ns/iter (± 1726) 42183 ns/iter (± 972) 0.75
es/full/all/es3 183616382 ns/iter (± 6511652) 238705941 ns/iter (± 12483700) 0.77
es/full/all/es5 182926440 ns/iter (± 6946150) 229914990 ns/iter (± 19648778) 0.80
es/full/all/es2015 148225525 ns/iter (± 3888354) 183577697 ns/iter (± 15615173) 0.81
es/full/all/es2016 144336525 ns/iter (± 3331956) 182865052 ns/iter (± 10149410) 0.79
es/full/all/es2017 144447381 ns/iter (± 2907477) 181004709 ns/iter (± 11920910) 0.80
es/full/all/es2018 142635694 ns/iter (± 3407625) 177253820 ns/iter (± 8044092) 0.80
es/full/all/es2019 143659007 ns/iter (± 4569967) 180342817 ns/iter (± 15732890) 0.80
es/full/all/es2020 139013622 ns/iter (± 3374917) 174461485 ns/iter (± 20908388) 0.80
es/full/parser 744748 ns/iter (± 26916) 925317 ns/iter (± 39110) 0.80
es/full/base/fixer 27146 ns/iter (± 1000) 34658 ns/iter (± 1279) 0.78
es/full/base/resolver_and_hygiene 95692 ns/iter (± 3072) 115437 ns/iter (± 5441) 0.83
serialization of ast node 212 ns/iter (± 13) 253 ns/iter (± 6) 0.84
serialization of serde 213 ns/iter (± 3) 254 ns/iter (± 17) 0.84

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.