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()?;
71 let then_block = then_block.stmt_list()?;
73 let parent_block = if_expr.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?;
75 if parent_block.tail_expr()? != if_expr.clone().into() {
79 // FIXME: This relies on untyped syntax tree and casts to much. It should be
80 // rewritten to use strongly-typed APIs.
82 // check for early return and continue
83 let first_in_then_block = then_block.syntax().first_child()?;
84 if ast::ReturnExpr::can_cast(first_in_then_block.kind())
85 || ast::ContinueExpr::can_cast(first_in_then_block.kind())
86 || first_in_then_block
88 .any(|x| ast::ReturnExpr::can_cast(x.kind()) || ast::ContinueExpr::can_cast(x.kind()))
93 let parent_container = parent_block.syntax().parent()?;
95 let early_expression: ast::Expr = match parent_container.kind() {
96 WHILE_EXPR | LOOP_EXPR => make::expr_continue(),
97 FN => make::expr_return(None),
101 if then_block.syntax().first_child_or_token().map(|t| t.kind() == T!['{']).is_none() {
105 then_block.syntax().last_child_or_token().filter(|t| t.kind() == T!['}'])?;
107 let target = if_expr.syntax().text_range();
109 AssistId("convert_to_guarded_return", AssistKind::RefactorRewrite),
110 "Convert to guarded return",
113 let if_expr = edit.make_mut(if_expr);
114 let if_indent_level = IndentLevel::from_node(if_expr.syntax());
115 let replacement = match if_let_pat {
120 make::block_expr(once(make::expr_stmt(early_expression).into()), None);
121 let cond = invert_boolean_expression(cond_expr);
122 make::expr_if(make::condition(cond, None), then_branch, None)
123 .indent(if_indent_level)
125 new_expr.syntax().clone_for_update()
127 Some((path, bound_ident)) => {
131 let pat = make::tuple_struct_pat(
133 once(make::ext::simple_ident_pat(make::name("it")).into()),
136 let path = make::ext::ident_path("it");
137 make::expr_path(path)
139 make::match_arm(once(pat.into()), None, expr)
142 let sad_arm = make::match_arm(
143 // FIXME: would be cool to use `None` or `Err(_)` if appropriate
144 once(make::wildcard_pat().into()),
149 make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm]))
152 let let_stmt = make::let_stmt(bound_ident, None, Some(match_expr));
153 let let_stmt = let_stmt.indent(if_indent_level);
154 let_stmt.syntax().clone_for_update()
158 let then_block_items = then_block.dedent(IndentLevel(1)).clone_for_update();
160 let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
162 if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
163 end_of_then.prev_sibling_or_token().unwrap()
168 let then_statements = replacement
169 .children_with_tokens()
173 .children_with_tokens()
175 .take_while(|i| *i != end_of_then),
179 ted::replace_with_many(if_expr.syntax(), then_statements)
186 use crate::tests::{check_assist, check_assist_not_applicable};
191 fn convert_inside_fn() {
193 convert_to_guarded_return,
221 fn convert_let_inside_fn() {
223 convert_to_guarded_return,
225 fn main(n: Option<String>) {
227 if$0 let Some(n) = n {
236 fn main(n: Option<String>) {
252 fn convert_if_let_result() {
254 convert_to_guarded_return,
257 if$0 let Ok(x) = Err(92) {
264 let x = match Err(92) {
275 fn convert_let_ok_inside_fn() {
277 convert_to_guarded_return,
279 fn main(n: Option<String>) {
281 if$0 let Some(n) = n {
290 fn main(n: Option<String>) {
306 fn convert_let_mut_ok_inside_fn() {
308 convert_to_guarded_return,
310 fn main(n: Option<String>) {
312 if$0 let Some(mut n) = n {
321 fn main(n: Option<String>) {
323 let mut n = match n {
337 fn convert_let_ref_ok_inside_fn() {
339 convert_to_guarded_return,
341 fn main(n: Option<&str>) {
343 if$0 let Some(ref n) = n {
352 fn main(n: Option<&str>) {
354 let ref n = match n {
368 fn convert_inside_while() {
370 convert_to_guarded_return,
396 fn convert_let_inside_while() {
398 convert_to_guarded_return,
402 if$0 let Some(n) = n {
425 fn convert_inside_loop() {
427 convert_to_guarded_return,
453 fn convert_let_inside_loop() {
455 convert_to_guarded_return,
459 if$0 let Some(n) = n {
482 fn ignore_already_converted_if() {
483 check_assist_not_applicable(
484 convert_to_guarded_return,
496 fn ignore_already_converted_loop() {
497 check_assist_not_applicable(
498 convert_to_guarded_return,
513 check_assist_not_applicable(
514 convert_to_guarded_return,
526 fn ignore_else_branch() {
527 check_assist_not_applicable(
528 convert_to_guarded_return,
542 fn ignore_statements_aftert_if() {
543 check_assist_not_applicable(
544 convert_to_guarded_return,
557 fn ignore_statements_inside_if() {
558 check_assist_not_applicable(
559 convert_to_guarded_return,