Skip to content

Commit 6fd4d95

Browse files
committed
fix: handle wrapped IIFEs (ex. (function f() {}()))
Closes #299
1 parent b3c1d7c commit 6fd4d95

File tree

3 files changed

+329
-118
lines changed

3 files changed

+329
-118
lines changed

src/parsing/parser.rs

Lines changed: 189 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1423,104 +1423,111 @@ fn parse_array_expr<'a>(node: &'a ArrayLit, context: &mut Context<'a>) -> PrintI
14231423
}
14241424

14251425
fn parse_arrow_func_expr<'a>(node: &'a ArrowExpr, context: &mut Context<'a>) -> PrintItems {
1426-
let mut items = PrintItems::new();
1427-
let header_start_info = Info::new("arrowFunctionExpressionHeaderStart");
1428-
let should_use_parens = get_should_use_parens(&node, context);
1429-
1430-
items.push_info(header_start_info);
1431-
if node.is_async() {
1432-
items.push_str("async ");
1433-
}
1434-
if let Some(type_params) = node.type_params {
1435-
items.extend(parse_node(type_params.into(), context));
1436-
}
1426+
let items = parse_inner(node, context);
1427+
return if should_add_parens_around_expr(node.into(), context) {
1428+
surround_with_parens(items)
1429+
} else {
1430+
items
1431+
};
14371432

1438-
if should_use_parens {
1439-
// need to check if there are parens because parse_parameters_or_arguments depends on the parens existing
1440-
if has_parens(node, context) {
1441-
items.extend(parse_parameters_or_arguments(
1442-
ParseParametersOrArgumentsOptions {
1443-
node: node.into(),
1444-
span: node.get_parameters_span(context),
1445-
nodes: node.params.iter().map(|node| node.into()).collect(),
1446-
custom_close_paren: |context| {
1447-
Some(parse_close_paren_with_type(
1448-
ParseCloseParenWithTypeOptions {
1449-
start_info: header_start_info,
1450-
type_node: node.return_type.map(|x| x.into()),
1451-
type_node_separator: None,
1452-
param_count: node.params.len(),
1453-
},
1454-
context,
1455-
))
1433+
fn parse_inner<'a>(node: &'a ArrowExpr, context: &mut Context<'a>) -> PrintItems {
1434+
let mut items = PrintItems::new();
1435+
let header_start_info = Info::new("arrowFunctionExpressionHeaderStart");
1436+
let should_use_parens = get_should_use_parens(&node, context);
1437+
1438+
items.push_info(header_start_info);
1439+
if node.is_async() {
1440+
items.push_str("async ");
1441+
}
1442+
if let Some(type_params) = node.type_params {
1443+
items.extend(parse_node(type_params.into(), context));
1444+
}
1445+
1446+
if should_use_parens {
1447+
// need to check if there are parens because parse_parameters_or_arguments depends on the parens existing
1448+
if has_parens(node, context) {
1449+
items.extend(parse_parameters_or_arguments(
1450+
ParseParametersOrArgumentsOptions {
1451+
node: node.into(),
1452+
span: node.get_parameters_span(context),
1453+
nodes: node.params.iter().map(|node| node.into()).collect(),
1454+
custom_close_paren: |context| {
1455+
Some(parse_close_paren_with_type(
1456+
ParseCloseParenWithTypeOptions {
1457+
start_info: header_start_info,
1458+
type_node: node.return_type.map(|x| x.into()),
1459+
type_node_separator: None,
1460+
param_count: node.params.len(),
1461+
},
1462+
context,
1463+
))
1464+
},
1465+
is_parameters: true,
14561466
},
1457-
is_parameters: true,
1458-
},
1459-
context,
1460-
));
1467+
context,
1468+
));
1469+
} else {
1470+
// todo: this should probably use more of the same logic as in parse_parameters_or_arguments
1471+
// there will only be one param in this case
1472+
items.extend(surround_with_parens(parse_node(node.params.first().unwrap().into(), context)));
1473+
}
14611474
} else {
1462-
// todo: this should probably use more of the same logic as in parse_parameters_or_arguments
1463-
// there will only be one param in this case
1464-
items.push_str("(");
14651475
items.extend(parse_node(node.params.first().unwrap().into(), context));
1466-
items.push_str(")");
14671476
}
1468-
} else {
1469-
items.extend(parse_node(node.params.first().unwrap().into(), context));
1470-
}
14711477

1472-
items.push_str(" =>");
1478+
items.push_str(" =>");
14731479

1474-
let parsed_body = parse_node(node.body.into(), context);
1475-
let parsed_body = if use_new_line_group_for_arrow_body(node, context) {
1476-
new_line_group(parsed_body)
1477-
} else {
1478-
parsed_body
1479-
}
1480-
.into_rc_path();
1481-
let open_brace_token = match &node.body {
1482-
BlockStmtOrExpr::BlockStmt(stmt) => context.token_finder.get_first_open_brace_token_within(stmt),
1483-
_ => None,
1484-
};
1485-
1486-
if open_brace_token.is_some() {
1487-
items.extend(parse_brace_separator(
1488-
ParseBraceSeparatorOptions {
1489-
brace_position: context.config.arrow_function_brace_position,
1490-
open_brace_token,
1491-
start_header_info: Some(header_start_info),
1492-
},
1493-
context,
1494-
));
1480+
let parsed_body = parse_node(node.body.into(), context);
1481+
let parsed_body = if use_new_line_group_for_arrow_body(node, context) {
1482+
new_line_group(parsed_body)
1483+
} else {
1484+
parsed_body
1485+
}
1486+
.into_rc_path();
1487+
let open_brace_token = match &node.body {
1488+
BlockStmtOrExpr::BlockStmt(stmt) => context.token_finder.get_first_open_brace_token_within(stmt),
1489+
_ => None,
1490+
};
14951491

1496-
items.extend(parsed_body.into());
1497-
} else {
1498-
let start_body_info = Info::new("startBody");
1499-
let end_body_info = Info::new("endBody");
1500-
items.push_info(start_body_info);
1492+
if open_brace_token.is_some() {
1493+
items.extend(parse_brace_separator(
1494+
ParseBraceSeparatorOptions {
1495+
brace_position: context.config.arrow_function_brace_position,
1496+
open_brace_token,
1497+
start_header_info: Some(header_start_info),
1498+
},
1499+
context,
1500+
));
15011501

1502-
if should_not_newline_after_arrow(&node.body, context) {
1503-
items.push_str(" ");
1502+
items.extend(parsed_body.into());
15041503
} else {
1505-
items.push_condition(conditions::if_above_width_or(
1506-
context.config.indent_width,
1507-
if_true_or(
1508-
"newlineOrSpace",
1509-
move |context| condition_resolvers::is_multiple_lines(context, &start_body_info, &end_body_info),
1510-
Signal::NewLine.into(),
1511-
Signal::SpaceOrNewLine.into(),
1512-
)
1513-
.into(),
1514-
" ".into(),
1515-
));
1504+
let start_body_info = Info::new("startBody");
1505+
let end_body_info = Info::new("endBody");
1506+
items.push_info(start_body_info);
1507+
1508+
if should_not_newline_after_arrow(&node.body, context) {
1509+
items.push_str(" ");
1510+
} else {
1511+
items.push_condition(conditions::if_above_width_or(
1512+
context.config.indent_width,
1513+
if_true_or(
1514+
"newlineOrSpace",
1515+
move |context| condition_resolvers::is_multiple_lines(context, &start_body_info, &end_body_info),
1516+
Signal::NewLine.into(),
1517+
Signal::SpaceOrNewLine.into(),
1518+
)
1519+
.into(),
1520+
" ".into(),
1521+
));
1522+
}
1523+
1524+
items.push_condition(conditions::indent_if_start_of_line(parsed_body.into()));
1525+
items.push_info(end_body_info);
15161526
}
15171527

1518-
items.push_condition(conditions::indent_if_start_of_line(parsed_body.into()));
1519-
items.push_info(end_body_info);
1528+
items
15201529
}
15211530

1522-
return items;
1523-
15241531
fn should_not_newline_after_arrow(body: &BlockStmtOrExpr, context: &Context) -> bool {
15251532
match body {
15261533
BlockStmtOrExpr::BlockStmt(_) => true,
@@ -1898,17 +1905,15 @@ fn parse_call_expr<'a>(node: &'a CallExpr, context: &mut Context<'a>) -> PrintIt
18981905

18991906
fn parse_test_library_arguments<'a>(args: &[&'a ExprOrSpread], context: &mut Context<'a>) -> PrintItems {
19001907
let mut items = PrintItems::new();
1901-
items.push_str("(");
19021908
items.extend(parse_node_with_inner_parse(args[0].into(), context, |items, _| {
19031909
let mut new_items = parser_helpers::with_no_new_lines(items);
19041910
new_items.push_str(",");
19051911
new_items
19061912
}));
19071913
items.push_str(" ");
19081914
items.extend(parse_node(args[1].into(), context));
1909-
items.push_str(")");
19101915

1911-
items
1916+
surround_with_parens(items)
19121917
}
19131918
}
19141919
}
@@ -2161,7 +2166,7 @@ fn parse_expr_with_type_args<'a>(node: &'a TsExprWithTypeArgs, context: &mut Con
21612166
}
21622167

21632168
fn parse_fn_expr<'a>(node: &'a FnExpr, context: &mut Context<'a>) -> PrintItems {
2164-
parse_function_decl_or_expr(
2169+
let items = parse_function_decl_or_expr(
21652170
FunctionDeclOrExprNode {
21662171
node: node.into(),
21672172
is_func_decl: false,
@@ -2170,7 +2175,55 @@ fn parse_fn_expr<'a>(node: &'a FnExpr, context: &mut Context<'a>) -> PrintItems
21702175
func: &node.function,
21712176
},
21722177
context,
2173-
)
2178+
);
2179+
2180+
if should_add_parens_around_expr(node.into(), context) {
2181+
surround_with_parens(items)
2182+
} else {
2183+
items
2184+
}
2185+
}
2186+
2187+
fn should_add_parens_around_expr(node: Node, context: &Context) -> bool {
2188+
let original_node = node;
2189+
for node in node.ancestors() {
2190+
match node {
2191+
Node::ParenExpr(paren_expr) => {
2192+
if !should_skip_paren_expr(paren_expr, context) {
2193+
return false;
2194+
}
2195+
}
2196+
Node::CallExpr(call_expr) => {
2197+
if !call_expr.callee.span().contains(original_node.span()) {
2198+
// it's in an argument, so don't add parens
2199+
return false;
2200+
}
2201+
}
2202+
Node::NewExpr(new_expr) => {
2203+
if !new_expr.callee.span().contains(original_node.span()) {
2204+
// it's in an argument, so don't add parens
2205+
return false;
2206+
}
2207+
}
2208+
Node::ExprStmt(_) => return true,
2209+
Node::MemberExpr(expr) => {
2210+
if expr.computed() && expr.prop.span().contains(original_node.span()) {
2211+
return false;
2212+
}
2213+
}
2214+
Node::CondExpr(cond_expr) => {
2215+
return cond_expr.test.span().contains(original_node.span());
2216+
}
2217+
Node::OptChainExpr(_) | Node::BinExpr(_) => {
2218+
// continue searching
2219+
}
2220+
_ => {
2221+
return false;
2222+
}
2223+
}
2224+
}
2225+
2226+
false
21742227
}
21752228

21762229
fn parse_getter_prop<'a>(node: &'a GetterProp, context: &mut Context<'a>) -> PrintItems {
@@ -2270,7 +2323,7 @@ fn parse_object_lit<'a>(node: &'a ObjectLit, context: &mut Context<'a>) -> Print
22702323
}
22712324

22722325
fn parse_paren_expr<'a>(node: &'a ParenExpr, context: &mut Context<'a>) -> PrintItems {
2273-
if skip_paren_expr(node, context) {
2326+
if should_skip_paren_expr(node, context) {
22742327
return parse_node(node.expr.into(), context);
22752328
}
22762329

@@ -2299,35 +2352,51 @@ fn parse_paren_expr<'a>(node: &'a ParenExpr, context: &mut Context<'a>) -> Print
22992352
true
23002353
}
23012354
}
2355+
}
23022356

2303-
fn skip_paren_expr(node: &ParenExpr, context: &Context) -> bool {
2304-
if has_surrounding_comments(&node.expr.into(), context) {
2305-
return false;
2306-
}
2357+
fn should_skip_paren_expr(node: &ParenExpr, context: &Context) -> bool {
2358+
if has_surrounding_comments(&node.expr.into(), context) {
2359+
return false;
2360+
}
23072361

2308-
// skip over any paren exprs within paren exprs and needless paren exprs
2309-
let parent = node.parent();
2310-
if matches!(
2311-
parent.kind(),
2312-
NodeKind::ParenExpr | NodeKind::ExprStmt | NodeKind::JSXElement | NodeKind::JSXFragment | NodeKind::JSXExprContainer
2313-
) {
2362+
if matches!(node.expr.kind(), NodeKind::ArrayLit) {
2363+
return true;
2364+
}
2365+
2366+
// skip over any paren exprs within paren exprs and needless paren exprs
2367+
let parent = node.parent();
2368+
if matches!(
2369+
parent.kind(),
2370+
NodeKind::ParenExpr | NodeKind::ExprStmt | NodeKind::JSXElement | NodeKind::JSXFragment | NodeKind::JSXExprContainer | NodeKind::UpdateExpr
2371+
) {
2372+
return true;
2373+
}
2374+
2375+
if let Node::AssignExpr(assign_expr) = parent {
2376+
if assign_expr.right.span().contains(node.span()) {
23142377
return true;
23152378
}
2379+
}
23162380

2317-
// skip over an expr or spread if not a spread
2318-
if let Some(expr_or_spread) = parent.to::<ExprOrSpread>() {
2319-
// these should only appear in these nodes
2320-
let is_known_parent = matches!(expr_or_spread.parent().kind(), NodeKind::NewExpr | NodeKind::ArrayLit | NodeKind::CallExpr);
2321-
debug_assert!(is_known_parent);
2322-
if is_known_parent && expr_or_spread.spread().is_none() {
2323-
return true;
2324-
}
2381+
// skip over an expr or spread if not a spread
2382+
if let Some(expr_or_spread) = parent.to::<ExprOrSpread>() {
2383+
// these should only appear in these nodes
2384+
let is_known_parent = matches!(expr_or_spread.parent().kind(), NodeKind::NewExpr | NodeKind::ArrayLit | NodeKind::CallExpr);
2385+
debug_assert!(is_known_parent);
2386+
if is_known_parent && expr_or_spread.spread().is_none() {
2387+
return true;
23252388
}
2389+
}
23262390

2327-
// skip explicitly parsing this as a paren expr as that will be handled
2328-
// in the JSX element/fragment and it might collapse back to not having a paren expr
2329-
is_jsx_paren_expr_handled_node(&node.expr.into(), context)
2391+
if let Node::MemberExpr(member_expr) = parent {
2392+
if member_expr.computed() && member_expr.prop.span().contains(node.span()) {
2393+
return true;
2394+
}
23302395
}
2396+
2397+
// skip explicitly parsing this as a paren expr as that will be handled
2398+
// in the JSX element/fragment and it might collapse back to not having a paren expr
2399+
is_jsx_paren_expr_handled_node(&node.expr.into(), context)
23312400
}
23322401

23332402
fn parse_sequence_expr<'a>(node: &'a SeqExpr, context: &mut Context<'a>) -> PrintItems {
@@ -2962,13 +3031,7 @@ fn handle_jsx_surrounding_parens<'a>(inner_items: PrintItems, context: &mut Cont
29623031
}
29633032
condition_resolvers::is_multiple_lines(context, &start_info, &end_info)
29643033
},
2965-
{
2966-
let mut items = PrintItems::new();
2967-
items.push_str("(");
2968-
items.extend(surround_with_new_lines(with_indent(inner_items_rc.into())));
2969-
items.push_str(")");
2970-
items
2971-
},
3034+
surround_with_parens(surround_with_new_lines(with_indent(inner_items_rc.into()))),
29723035
{
29733036
let mut items = PrintItems::new();
29743037
items.push_signal(Signal::PossibleNewLine);
@@ -8254,6 +8317,14 @@ fn use_new_line_group_for_arrow_body(arrow_expr: &ArrowExpr, context: &Context)
82548317
}
82558318
}
82568319

8320+
fn surround_with_parens(items: PrintItems) -> PrintItems {
8321+
let mut new_items = PrintItems::new();
8322+
new_items.push_str("(");
8323+
new_items.extend(items);
8324+
new_items.push_str(")");
8325+
new_items
8326+
}
8327+
82578328
/* is/has functions */
82588329

82598330
fn is_arrow_function_with_expr_body(node: &Node) -> bool {

0 commit comments

Comments
 (0)