6 edit::{AstNodeEdit, IndentLevel},
10 SyntaxKind::{FN, LOOP_EXPR, WHILE_EXPR, WHITESPACE},
15 assist_context::{AssistContext, Assists},
16 utils::invert_boolean_expression,
20 // Assist: convert_to_guarded_return
22 // Replace a large conditional with a guarded return.
42 pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
43 let if_expr: ast::IfExpr = ctx.find_node_at_offset()?;
44 if if_expr.else_branch().is_some() {
48 let cond = if_expr.condition()?;
50 // Check if there is an IfLet that we can handle.
51 let if_let_pat = match cond.pat() {
52 None => None, // No IfLet, supported.
53 Some(ast::Pat::TupleStructPat(pat)) if pat.fields().count() == 1 => {
54 let path = pat.path()?;
55 if path.qualifier().is_some() {
59 let bound_ident = pat.fields().next().unwrap();
60 if !ast::IdentPat::can_cast(bound_ident.syntax().kind()) {
64 Some((path, bound_ident))
66 Some(_) => return None, // Unsupported IfLet.
69 let cond_expr = cond.expr()?;
70 let then_block = if_expr.then_branch()?;
72 let parent_block = if_expr.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?;
74 if parent_block.tail_expr()? != if_expr.clone().into() {
78 // check for early return and continue
79 let first_in_then_block = then_block.syntax().first_child()?;
80 if ast::ReturnExpr::can_cast(first_in_then_block.kind())
81 || ast::ContinueExpr::can_cast(first_in_then_block.kind())
82 || first_in_then_block
84 .any(|x| ast::ReturnExpr::can_cast(x.kind()) || ast::ContinueExpr::can_cast(x.kind()))
89 let parent_container = parent_block.syntax().parent()?;
91 let early_expression: ast::Expr = match parent_container.kind() {
92 WHILE_EXPR | LOOP_EXPR => make::expr_continue(),
93 FN => make::expr_return(None),
97 if then_block.syntax().first_child_or_token().map(|t| t.kind() == T!['{']).is_none() {
101 then_block.syntax().last_child_or_token().filter(|t| t.kind() == T!['}'])?;
103 let target = if_expr.syntax().text_range();
105 AssistId("convert_to_guarded_return", AssistKind::RefactorRewrite),
106 "Convert to guarded return",
109 let if_expr = edit.make_mut(if_expr);
110 let if_indent_level = IndentLevel::from_node(if_expr.syntax());
111 let replacement = match if_let_pat {
116 make::block_expr(once(make::expr_stmt(early_expression).into()), None);
117 let cond = invert_boolean_expression(cond_expr);
118 make::expr_if(make::condition(cond, None), then_branch, None)
119 .indent(if_indent_level)
121 new_expr.syntax().clone_for_update()
123 Some((path, bound_ident)) => {
127 let pat = make::tuple_struct_pat(
129 once(make::ext::simple_ident_pat(make::name("it")).into()),
132 let path = make::ext::ident_path("it");
133 make::expr_path(path)
135 make::match_arm(once(pat.into()), None, expr)
138 let sad_arm = make::match_arm(
139 // FIXME: would be cool to use `None` or `Err(_)` if appropriate
140 once(make::wildcard_pat().into()),
145 make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm]))
148 let let_stmt = make::let_stmt(bound_ident, None, Some(match_expr));
149 let let_stmt = let_stmt.indent(if_indent_level);
150 let_stmt.syntax().clone_for_update()
154 let then_block_items = then_block.dedent(IndentLevel(1)).clone_for_update();
156 let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
158 if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
159 end_of_then.prev_sibling_or_token().unwrap()
164 let then_statements = replacement
165 .children_with_tokens()
169 .children_with_tokens()
171 .take_while(|i| *i != end_of_then),
175 ted::replace_with_many(if_expr.syntax(), then_statements)
182 use crate::tests::{check_assist, check_assist_not_applicable};
187 fn convert_inside_fn() {
189 convert_to_guarded_return,
217 fn convert_let_inside_fn() {
219 convert_to_guarded_return,
221 fn main(n: Option<String>) {
223 if$0 let Some(n) = n {
232 fn main(n: Option<String>) {
248 fn convert_if_let_result() {
250 convert_to_guarded_return,
253 if$0 let Ok(x) = Err(92) {
260 let x = match Err(92) {
271 fn convert_let_ok_inside_fn() {
273 convert_to_guarded_return,
275 fn main(n: Option<String>) {
277 if$0 let Some(n) = n {
286 fn main(n: Option<String>) {
302 fn convert_let_mut_ok_inside_fn() {
304 convert_to_guarded_return,
306 fn main(n: Option<String>) {
308 if$0 let Some(mut n) = n {
317 fn main(n: Option<String>) {
319 let mut n = match n {
333 fn convert_let_ref_ok_inside_fn() {
335 convert_to_guarded_return,
337 fn main(n: Option<&str>) {
339 if$0 let Some(ref n) = n {
348 fn main(n: Option<&str>) {
350 let ref n = match n {
364 fn convert_inside_while() {
366 convert_to_guarded_return,
392 fn convert_let_inside_while() {
394 convert_to_guarded_return,
398 if$0 let Some(n) = n {
421 fn convert_inside_loop() {
423 convert_to_guarded_return,
449 fn convert_let_inside_loop() {
451 convert_to_guarded_return,
455 if$0 let Some(n) = n {
478 fn ignore_already_converted_if() {
479 check_assist_not_applicable(
480 convert_to_guarded_return,
492 fn ignore_already_converted_loop() {
493 check_assist_not_applicable(
494 convert_to_guarded_return,
509 check_assist_not_applicable(
510 convert_to_guarded_return,
522 fn ignore_else_branch() {
523 check_assist_not_applicable(
524 convert_to_guarded_return,
538 fn ignore_statements_aftert_if() {
539 check_assist_not_applicable(
540 convert_to_guarded_return,
553 fn ignore_statements_inside_if() {
554 check_assist_not_applicable(
555 convert_to_guarded_return,