Skip to main content

ide_assists/handlers/
convert_to_guarded_return.rs

1use std::iter::once;
2
3use either::Either;
4use hir::Semantics;
5use ide_db::{RootDatabase, ty_filter::TryEnum};
6use syntax::{
7    AstNode,
8    SyntaxKind::WHITESPACE,
9    SyntaxNode, T,
10    ast::{
11        self,
12        edit::{AstNodeEdit, IndentLevel},
13        syntax_factory::SyntaxFactory,
14    },
15    match_ast,
16    syntax_editor::SyntaxEditor,
17};
18
19use crate::{
20    AssistId,
21    assist_context::{AssistContext, Assists},
22    utils::{invert_boolean_expression, is_never_block},
23};
24
25// Assist: convert_to_guarded_return
26//
27// Replace a large conditional with a guarded return.
28//
29// ```
30// fn main() {
31//     $0if cond {
32//         foo();
33//         bar();
34//     }
35// }
36// ```
37// ->
38// ```
39// fn main() {
40//     if !cond {
41//         return;
42//     }
43//     foo();
44//     bar();
45// }
46// ```
47// ---
48// ```
49// //- minicore: option
50// fn foo() -> Option<i32> { None }
51// fn main() {
52//     $0let x = foo();
53// }
54// ```
55// ->
56// ```
57// fn foo() -> Option<i32> { None }
58// fn main() {
59//     let Some(x) = foo() else { return };
60// }
61// ```
62pub(crate) fn convert_to_guarded_return(
63    acc: &mut Assists,
64    ctx: &AssistContext<'_, '_>,
65) -> Option<()> {
66    match ctx.find_node_at_offset::<Either<ast::LetStmt, ast::IfExpr>>()? {
67        Either::Left(let_stmt) => let_stmt_to_guarded_return(let_stmt, acc, ctx),
68        Either::Right(if_expr) => if_expr_to_guarded_return(if_expr, acc, ctx),
69    }
70}
71
72fn if_expr_to_guarded_return(
73    if_expr: ast::IfExpr,
74    acc: &mut Assists,
75    ctx: &AssistContext<'_, '_>,
76) -> Option<()> {
77    let make = SyntaxFactory::without_mappings();
78    let cond = if_expr.condition()?;
79
80    let if_token_range = if_expr.if_token()?.text_range();
81    let if_cond_range = cond.syntax().text_range();
82
83    let cursor_in_range =
84        if_token_range.cover(if_cond_range).contains_range(ctx.selection_trimmed());
85    if !cursor_in_range {
86        return None;
87    }
88
89    let let_chains = flat_let_chain(cond, &make);
90
91    let then_branch = if_expr.then_branch()?;
92    let then_block = then_branch.stmt_list()?;
93
94    let parent_block = if_expr.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?;
95
96    // check for early return and continue
97    if is_early_block(&then_block) || is_never_block(&ctx.sema, &then_branch) {
98        return None;
99    }
100
101    let container = container_of(&parent_block)?;
102    let else_block = ElseBlock::new(&ctx.sema, if_expr.else_branch(), &container)?;
103
104    if parent_block.tail_expr() != Some(if_expr.clone().into())
105        && !(else_block.is_never_block
106            && ast::ExprStmt::can_cast(if_expr.syntax().parent()?.kind()))
107    {
108        return None;
109    }
110
111    then_block.syntax().first_child_or_token().map(|t| t.kind() == T!['{'])?;
112
113    then_block.syntax().last_child_or_token().filter(|t| t.kind() == T!['}'])?;
114
115    let then_block_items = then_block.dedent(IndentLevel(1));
116
117    let end_of_then = then_block_items.syntax().last_child_or_token()?;
118    let end_of_then = if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
119        end_of_then.prev_sibling_or_token()?
120    } else {
121        end_of_then
122    };
123
124    let target = if_expr.syntax().text_range();
125    acc.add(
126        AssistId::refactor_rewrite("convert_to_guarded_return"),
127        "Convert to guarded return",
128        target,
129        |edit| {
130            let editor = edit.make_editor(if_expr.syntax());
131            let make = editor.make();
132            let if_indent_level = IndentLevel::from_node(if_expr.syntax());
133            let early_expression = else_block.make_early_block(&ctx.sema, make);
134            let replacement = let_chains.into_iter().map(|expr| {
135                if let ast::Expr::LetExpr(let_expr) = &expr
136                    && let (Some(pat), Some(expr)) = (let_expr.pat(), let_expr.expr())
137                {
138                    // If-let.
139                    let let_else_stmt =
140                        make.let_else_stmt(pat, None, expr, early_expression.clone());
141                    let let_else_stmt = let_else_stmt.indent(if_indent_level);
142                    let_else_stmt.syntax().clone()
143                } else {
144                    // If.
145                    let new_expr = {
146                        let then_branch = clean_stmt_block(&early_expression, make);
147                        let cond = invert_boolean_expression(make, expr);
148                        make.expr_if(cond, then_branch, None).indent(if_indent_level)
149                    };
150                    new_expr.syntax().clone()
151                }
152            });
153
154            let newline = &format!("\n{if_indent_level}");
155            let then_statements = replacement
156                .enumerate()
157                .flat_map(|(i, node)| {
158                    (i != 0)
159                        .then(|| make.whitespace(newline).into())
160                        .into_iter()
161                        .chain(node.children_with_tokens())
162                })
163                .chain(
164                    then_block_items
165                        .syntax()
166                        .children_with_tokens()
167                        .skip(1)
168                        .take_while(|i| *i != end_of_then),
169                )
170                .collect();
171            editor.replace_with_many(if_expr.syntax(), then_statements);
172            edit.add_file_edits(ctx.vfs_file_id(), editor);
173        },
174    )
175}
176
177fn let_stmt_to_guarded_return(
178    let_stmt: ast::LetStmt,
179    acc: &mut Assists,
180    ctx: &AssistContext<'_, '_>,
181) -> Option<()> {
182    let pat = let_stmt.pat()?;
183    let expr = let_stmt.initializer()?;
184
185    let let_token_range = let_stmt.let_token()?.text_range();
186    let let_pattern_range = pat.syntax().text_range();
187    let cursor_in_range =
188        let_token_range.cover(let_pattern_range).contains_range(ctx.selection_trimmed());
189
190    if !cursor_in_range || let_stmt.let_else().is_some() {
191        return None;
192    }
193
194    let try_enum =
195        ctx.sema.type_of_expr(&expr).and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty.adjusted()))?;
196
197    let happy_pattern = try_enum.happy_pattern(pat);
198    let target = let_stmt.syntax().text_range();
199
200    let parent_block = let_stmt.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?;
201    let container = container_of(&parent_block)?;
202    let else_block = ElseBlock::new(&ctx.sema, None, &container)?;
203
204    acc.add(
205        AssistId::refactor_rewrite("convert_to_guarded_return"),
206        "Convert to guarded return",
207        target,
208        |edit| {
209            let editor = edit.make_editor(let_stmt.syntax());
210            let make = editor.make();
211            let let_indent_level = IndentLevel::from_node(let_stmt.syntax());
212
213            let replacement = {
214                let let_else_stmt = make.let_else_stmt(
215                    happy_pattern,
216                    let_stmt.ty(),
217                    expr.reset_indent(),
218                    else_block.make_early_block(&ctx.sema, make),
219                );
220                let let_else_stmt = let_else_stmt.indent(let_indent_level);
221                let_else_stmt.syntax().clone()
222            };
223            editor.replace(let_stmt.syntax(), replacement);
224            edit.add_file_edits(ctx.vfs_file_id(), editor);
225        },
226    )
227}
228
229fn container_of(block: &ast::BlockExpr) -> Option<SyntaxNode> {
230    if block.label().is_some() {
231        return Some(block.syntax().clone());
232    }
233    block.syntax().parent()
234}
235
236struct ElseBlock<'db> {
237    exist_else_branch: Option<ast::ElseBranch>,
238    is_never_block: bool,
239    kind: EarlyKind<'db>,
240}
241
242impl<'db> ElseBlock<'db> {
243    fn new(
244        sema: &Semantics<'db, RootDatabase>,
245        exist_else_branch: Option<ast::ElseBranch>,
246        parent_container: &SyntaxNode,
247    ) -> Option<Self> {
248        let is_never_block =
249            exist_else_branch.as_ref().is_some_and(|it| is_never_else_branch(sema, it));
250        let kind = EarlyKind::from_node(parent_container, sema)?;
251
252        Some(Self { exist_else_branch, is_never_block, kind })
253    }
254
255    fn make_else_block_from_exist_branch(&self, make: &SyntaxFactory) -> Option<ast::BlockExpr> {
256        match self.exist_else_branch.as_ref()? {
257            ast::ElseBranch::Block(block_expr) => Some(block_expr.reset_indent()),
258            ast::ElseBranch::IfExpr(if_expr) => {
259                Some(make.block_expr(None, Some(if_expr.reset_indent().indent(1.into()).into())))
260            }
261        }
262    }
263
264    fn make_early_block(
265        self,
266        sema: &Semantics<'_, RootDatabase>,
267        make: &SyntaxFactory,
268    ) -> ast::BlockExpr {
269        let Some(block_expr) = self.make_else_block_from_exist_branch(make) else {
270            return make.tail_only_block_expr(self.kind.make_early_expr(sema, make, None));
271        };
272
273        if self.is_never_block {
274            return block_expr;
275        }
276
277        let (editor, block_expr) = SyntaxEditor::with_ast_node(&block_expr);
278        let make = editor.make();
279
280        let last_stmt = block_expr.statements().last().map(|it| it.syntax().clone());
281        let tail_expr = block_expr.tail_expr().map(|it| it.syntax().clone());
282        let Some(last_element) = tail_expr.clone().or(last_stmt.clone()) else {
283            return make.tail_only_block_expr(self.kind.make_early_expr(sema, make, None));
284        };
285        let whitespace = last_element.prev_sibling_or_token().filter(|it| it.kind() == WHITESPACE);
286
287        if let Some(tail_expr) = block_expr.tail_expr()
288            && !self.kind.is_unit()
289        {
290            let early_expr = self.kind.make_early_expr(sema, make, Some(tail_expr.clone()));
291            editor.replace(tail_expr.syntax(), early_expr.syntax());
292        } else {
293            let last_stmt = match block_expr.tail_expr() {
294                Some(expr) if !expr.is_block_like() => make.expr_stmt(expr).syntax().clone(),
295                _ => last_element.clone(),
296            };
297            let whitespace =
298                make.whitespace(&whitespace.map_or(String::new(), |it| it.to_string()));
299            let early_expr = self.kind.make_early_expr(sema, make, None).syntax().clone().into();
300            editor.replace_with_many(
301                last_element,
302                vec![last_stmt.into(), whitespace.into(), early_expr],
303            );
304        }
305
306        ast::BlockExpr::cast(editor.finish().new_root().clone()).unwrap()
307    }
308}
309
310enum EarlyKind<'db> {
311    Continue,
312    Break(ast::Lifetime, hir::Type<'db>),
313    Return(hir::Type<'db>),
314}
315
316impl<'db> EarlyKind<'db> {
317    fn from_node(
318        parent_container: &SyntaxNode,
319        sema: &Semantics<'db, RootDatabase>,
320    ) -> Option<Self> {
321        match_ast! {
322            match parent_container {
323                ast::Fn(it) => Some(Self::Return(sema.to_def(&it)?.ret_type(sema.db))),
324                ast::ClosureExpr(it) => Some(Self::Return(sema.type_of_expr(&it.body()?)?.original)),
325                ast::BlockExpr(it) => Some(Self::Break(it.label()?.lifetime()?, sema.type_of_expr(&it.into())?.original)),
326                ast::WhileExpr(_) => Some(Self::Continue),
327                ast::LoopExpr(_) => Some(Self::Continue),
328                ast::ForExpr(_) => Some(Self::Continue),
329                _ => None
330            }
331        }
332    }
333
334    fn make_early_expr(
335        &self,
336        sema: &Semantics<'_, RootDatabase>,
337        make: &SyntaxFactory,
338        ret: Option<ast::Expr>,
339    ) -> ast::Expr {
340        match self {
341            EarlyKind::Continue => make.expr_continue(None).into(),
342            EarlyKind::Break(label, _) => make.expr_break(Some(label.clone()), ret).into(),
343            EarlyKind::Return(ty) => {
344                let expr = match TryEnum::from_ty(sema, ty) {
345                    Some(TryEnum::Option) => {
346                        ret.or_else(|| Some(make.expr_path(make.ident_path("None"))))
347                    }
348                    _ => ret,
349                };
350                make.expr_return(expr).into()
351            }
352        }
353    }
354
355    fn is_unit(&self) -> bool {
356        match self {
357            EarlyKind::Continue => true,
358            EarlyKind::Break(_, ty) => ty.is_unit(),
359            EarlyKind::Return(ty) => ty.is_unit(),
360        }
361    }
362}
363
364fn flat_let_chain(mut expr: ast::Expr, make: &SyntaxFactory) -> Vec<ast::Expr> {
365    let mut chains = vec![];
366    let mut reduce_cond = |rhs| {
367        if !matches!(rhs, ast::Expr::LetExpr(_))
368            && let Some(last) = chains.pop_if(|last| !matches!(last, ast::Expr::LetExpr(_)))
369        {
370            chains.push(make.expr_bin_op(rhs, ast::BinaryOp::LogicOp(ast::LogicOp::And), last));
371        } else {
372            chains.push(rhs);
373        }
374    };
375
376    while let ast::Expr::BinExpr(bin_expr) = &expr
377        && bin_expr.op_kind() == Some(ast::BinaryOp::LogicOp(ast::LogicOp::And))
378        && let (Some(lhs), Some(rhs)) = (bin_expr.lhs(), bin_expr.rhs())
379    {
380        reduce_cond(rhs.reset_indent());
381        expr = lhs;
382    }
383
384    reduce_cond(expr.reset_indent());
385    chains.reverse();
386    chains
387}
388
389fn clean_stmt_block(block: &ast::BlockExpr, make: &SyntaxFactory) -> ast::BlockExpr {
390    if block.statements().next().is_none()
391        && let Some(tail_expr) = block.tail_expr()
392        && block.modifier().is_none()
393    {
394        make.block_expr(once(make.expr_stmt(tail_expr).into()), None)
395    } else {
396        block.clone()
397    }
398}
399
400fn is_early_block(then_block: &ast::StmtList) -> bool {
401    let is_early_expr =
402        |expr| matches!(expr, ast::Expr::ReturnExpr(_) | ast::Expr::ContinueExpr(_));
403    let into_expr = |stmt| match stmt {
404        ast::Stmt::ExprStmt(expr_stmt) => expr_stmt.expr(),
405        _ => None,
406    };
407    then_block.tail_expr().is_some_and(is_early_expr)
408        || then_block.statements().filter_map(into_expr).any(is_early_expr)
409}
410
411fn is_never_else_branch(sema: &Semantics<'_, RootDatabase>, it: &ast::ElseBranch) -> bool {
412    match it {
413        ast::ElseBranch::Block(block_expr) => is_never_block(sema, block_expr),
414        ast::ElseBranch::IfExpr(if_expr) => {
415            let mut if_exprs =
416                std::iter::successors(Some(if_expr.clone()), |it| match it.else_branch()? {
417                    ast::ElseBranch::Block(_) => None,
418                    ast::ElseBranch::IfExpr(if_expr) => Some(if_expr),
419                });
420            if_exprs.all(|it| {
421                let else_is_never = match it.else_branch() {
422                    None => false,
423                    Some(ast::ElseBranch::IfExpr(_)) => true,
424                    Some(ast::ElseBranch::Block(block)) => is_never_block(sema, &block),
425                };
426                else_is_never && it.then_branch().is_some_and(|it| is_never_block(sema, &it))
427            })
428        }
429    }
430}
431
432#[cfg(test)]
433mod tests {
434    use crate::tests::{check_assist, check_assist_not_applicable};
435
436    use super::*;
437
438    #[test]
439    fn convert_inside_fn() {
440        check_assist(
441            convert_to_guarded_return,
442            r#"
443fn main() {
444    bar();
445    if$0 true {
446        foo();
447
448        // comment
449        bar();
450    }
451}
452"#,
453            r#"
454fn main() {
455    bar();
456    if false {
457        return;
458    }
459    foo();
460
461    // comment
462    bar();
463}
464"#,
465        );
466    }
467
468    #[test]
469    fn convert_inside_fn_return_option() {
470        check_assist(
471            convert_to_guarded_return,
472            r#"
473//- minicore: option
474fn ret_option() -> Option<()> {
475    bar();
476    if$0 true {
477        foo();
478
479        // comment
480        bar();
481    }
482}
483"#,
484            r#"
485fn ret_option() -> Option<()> {
486    bar();
487    if false {
488        return None;
489    }
490    foo();
491
492    // comment
493    bar();
494}
495"#,
496        );
497    }
498
499    #[test]
500    fn convert_inside_closure() {
501        check_assist(
502            convert_to_guarded_return,
503            r#"
504fn main() {
505    let _f = || {
506        bar();
507        if$0 true {
508            foo();
509
510            // comment
511            bar();
512        }
513    }
514}
515"#,
516            r#"
517fn main() {
518    let _f = || {
519        bar();
520        if false {
521            return;
522        }
523        foo();
524
525        // comment
526        bar();
527    }
528}
529"#,
530        );
531    }
532
533    #[test]
534    fn convert_let_inside_fn() {
535        check_assist(
536            convert_to_guarded_return,
537            r#"
538fn main(n: Option<String>) {
539    bar();
540    if$0 let Some(n) = n {
541        foo(n);
542
543        // comment
544        bar();
545    }
546}
547"#,
548            r#"
549fn main(n: Option<String>) {
550    bar();
551    let Some(n) = n else { return };
552    foo(n);
553
554    // comment
555    bar();
556}
557"#,
558        );
559    }
560
561    #[test]
562    fn convert_if_let_result() {
563        check_assist(
564            convert_to_guarded_return,
565            r#"
566fn main() {
567    if$0 let Ok(x) = Err(92) {
568        foo(x);
569    }
570}
571"#,
572            r#"
573fn main() {
574    let Ok(x) = Err(92) else { return };
575    foo(x);
576}
577"#,
578        );
579    }
580
581    #[test]
582    fn convert_if_let_has_else_block() {
583        check_assist(
584            convert_to_guarded_return,
585            r#"
586fn main() -> i32 {
587    if$0 true {
588        foo();
589    } else {
590        bar()
591    }
592}
593"#,
594            r#"
595fn main() -> i32 {
596    if false {
597        return bar();
598    }
599    foo();
600}
601"#,
602        );
603
604        check_assist(
605            convert_to_guarded_return,
606            r#"
607fn main() {
608    if$0 true {
609        foo();
610    } else {
611        bar()
612    }
613}
614"#,
615            r#"
616fn main() {
617    if false {
618        bar();
619        return
620    }
621    foo();
622}
623"#,
624        );
625
626        check_assist(
627            convert_to_guarded_return,
628            r#"
629fn main() {
630    if$0 true {
631        foo();
632    } else {
633        bar();
634    }
635}
636"#,
637            r#"
638fn main() {
639    if false {
640        bar();
641        return
642    }
643    foo();
644}
645"#,
646        );
647    }
648
649    #[test]
650    fn convert_if_let_has_never_type_else_block() {
651        check_assist(
652            convert_to_guarded_return,
653            r#"
654fn main() {
655    if$0 let Ok(x) = Err(92) {
656        foo(x);
657    } else {
658        // needless comment
659        return;
660    }
661}
662"#,
663            r#"
664fn main() {
665    let Ok(x) = Err(92) else {
666        // needless comment
667        return;
668    };
669    foo(x);
670}
671"#,
672        );
673
674        check_assist(
675            convert_to_guarded_return,
676            r#"
677fn main() {
678    if$0 let Ok(x) = Err(92) {
679        foo(x);
680    } else {
681        return
682    }
683}
684"#,
685            r#"
686fn main() {
687    let Ok(x) = Err(92) else {
688        return
689    };
690    foo(x);
691}
692"#,
693        );
694    }
695
696    #[test]
697    fn convert_if_let_has_never_type_else_block_in_statement() {
698        check_assist(
699            convert_to_guarded_return,
700            r#"
701fn main() {
702    some_statements();
703    if$0 let Ok(x) = Err(92) {
704        foo(x);
705    } else {
706        // needless comment
707        return;
708    }
709    some_statements();
710}
711"#,
712            r#"
713fn main() {
714    some_statements();
715    let Ok(x) = Err(92) else {
716        // needless comment
717        return;
718    };
719    foo(x);
720    some_statements();
721}
722"#,
723        );
724    }
725
726    #[test]
727    fn convert_if_let_result_inside_let() {
728        check_assist(
729            convert_to_guarded_return,
730            r#"
731fn main() {
732    let _x = loop {
733        if$0 let Ok(x) = Err(92) {
734            foo(x);
735        }
736    };
737}
738"#,
739            r#"
740fn main() {
741    let _x = loop {
742        let Ok(x) = Err(92) else { continue };
743        foo(x);
744    };
745}
746"#,
747        );
748    }
749
750    #[test]
751    fn convert_if_let_chain_result() {
752        check_assist(
753            convert_to_guarded_return,
754            r#"
755fn main() {
756    if$0 let Ok(x) = Err(92)
757        && x < 30
758        && let Some(y) = Some(8)
759    {
760        foo(x, y);
761    }
762}
763"#,
764            r#"
765fn main() {
766    let Ok(x) = Err(92) else { return };
767    if x >= 30 {
768        return;
769    }
770    let Some(y) = Some(8) else { return };
771    foo(x, y);
772}
773"#,
774        );
775
776        check_assist(
777            convert_to_guarded_return,
778            r#"
779fn main() {
780    if$0 let Ok(x) = Err(92)
781        && x < 30
782        && y < 20
783        && let Some(y) = Some(8)
784    {
785        foo(x, y);
786    }
787}
788"#,
789            r#"
790fn main() {
791    let Ok(x) = Err(92) else { return };
792    if !(x < 30 && y < 20) {
793        return;
794    }
795    let Some(y) = Some(8) else { return };
796    foo(x, y);
797}
798"#,
799        );
800
801        check_assist(
802            convert_to_guarded_return,
803            r#"
804fn main() {
805    if$0 let Ok(x) = Err(92)
806        && let Ok(y) = Ok(37)
807        && x < 30
808        && let Some(y) = Some(8)
809    {
810        foo(x, y);
811    }
812}
813"#,
814            r#"
815fn main() {
816    let Ok(x) = Err(92) else { return };
817    let Ok(y) = Ok(37) else { return };
818    if x >= 30 {
819        return;
820    }
821    let Some(y) = Some(8) else { return };
822    foo(x, y);
823}
824"#,
825        );
826
827        check_assist(
828            convert_to_guarded_return,
829            r#"
830fn main() {
831    if$0 cond
832        && let Ok(x) = Err(92)
833        && let Ok(y) = Ok(37)
834        && x < 30
835        && let Some(y) = Some(8)
836    {
837        foo(x, y);
838    }
839}
840"#,
841            r#"
842fn main() {
843    if !cond {
844        return;
845    }
846    let Ok(x) = Err(92) else { return };
847    let Ok(y) = Ok(37) else { return };
848    if x >= 30 {
849        return;
850    }
851    let Some(y) = Some(8) else { return };
852    foo(x, y);
853}
854"#,
855        );
856
857        check_assist(
858            convert_to_guarded_return,
859            r#"
860fn main() {
861    if$0 cond
862        && foo()
863        && let Ok(x) = Err(92)
864        && let Ok(y) = Ok(37)
865        && x < 30
866        && let Some(y) = Some(8)
867    {
868        foo(x, y);
869    }
870}
871"#,
872            r#"
873fn main() {
874    if !(cond && foo()) {
875        return;
876    }
877    let Ok(x) = Err(92) else { return };
878    let Ok(y) = Ok(37) else { return };
879    if x >= 30 {
880        return;
881    }
882    let Some(y) = Some(8) else { return };
883    foo(x, y);
884}
885"#,
886        );
887    }
888
889    #[test]
890    fn convert_let_ok_inside_fn() {
891        check_assist(
892            convert_to_guarded_return,
893            r#"
894fn main(n: Option<String>) {
895    bar();
896    if$0 let Some(n) = n {
897        foo(n);
898
899        // comment
900        bar();
901    }
902}
903"#,
904            r#"
905fn main(n: Option<String>) {
906    bar();
907    let Some(n) = n else { return };
908    foo(n);
909
910    // comment
911    bar();
912}
913"#,
914        );
915    }
916
917    #[test]
918    fn convert_let_mut_ok_inside_fn() {
919        check_assist(
920            convert_to_guarded_return,
921            r#"
922fn main(n: Option<String>) {
923    bar();
924    if$0 let Some(mut n) = n {
925        foo(n);
926
927        // comment
928        bar();
929    }
930}
931"#,
932            r#"
933fn main(n: Option<String>) {
934    bar();
935    let Some(mut n) = n else { return };
936    foo(n);
937
938    // comment
939    bar();
940}
941"#,
942        );
943    }
944
945    #[test]
946    fn convert_let_ref_ok_inside_fn() {
947        check_assist(
948            convert_to_guarded_return,
949            r#"
950fn main(n: Option<&str>) {
951    bar();
952    if$0 let Some(ref n) = n {
953        foo(n);
954
955        // comment
956        bar();
957    }
958}
959"#,
960            r#"
961fn main(n: Option<&str>) {
962    bar();
963    let Some(ref n) = n else { return };
964    foo(n);
965
966    // comment
967    bar();
968}
969"#,
970        );
971    }
972
973    #[test]
974    fn convert_inside_while() {
975        check_assist(
976            convert_to_guarded_return,
977            r#"
978fn main() {
979    while true {
980        if$0 true {
981            foo();
982            bar();
983        }
984    }
985}
986"#,
987            r#"
988fn main() {
989    while true {
990        if false {
991            continue;
992        }
993        foo();
994        bar();
995    }
996}
997"#,
998        );
999    }
1000
1001    #[test]
1002    fn convert_let_inside_while() {
1003        check_assist(
1004            convert_to_guarded_return,
1005            r#"
1006fn main() {
1007    while true {
1008        if$0 let Some(n) = n {
1009            foo(n);
1010            bar();
1011        }
1012    }
1013}
1014"#,
1015            r#"
1016fn main() {
1017    while true {
1018        let Some(n) = n else { continue };
1019        foo(n);
1020        bar();
1021    }
1022}
1023"#,
1024        );
1025    }
1026
1027    #[test]
1028    fn convert_inside_loop() {
1029        check_assist(
1030            convert_to_guarded_return,
1031            r#"
1032fn main() {
1033    loop {
1034        if$0 true {
1035            foo();
1036            bar();
1037        }
1038    }
1039}
1040"#,
1041            r#"
1042fn main() {
1043    loop {
1044        if false {
1045            continue;
1046        }
1047        foo();
1048        bar();
1049    }
1050}
1051"#,
1052        );
1053    }
1054
1055    #[test]
1056    fn convert_inside_loop_with_else_if() {
1057        check_assist(
1058            convert_to_guarded_return,
1059            r#"
1060fn main() {
1061    loop {
1062        if$0 guard() {
1063            foo();
1064            bar();
1065        } else if cond() {
1066            break;
1067        } else {
1068            return
1069        }
1070    }
1071}
1072"#,
1073            r#"
1074fn main() {
1075    loop {
1076        if !guard() {
1077            if cond() {
1078                break;
1079            } else {
1080                return
1081            }
1082        }
1083        foo();
1084        bar();
1085    }
1086}
1087"#,
1088        );
1089    }
1090
1091    #[test]
1092    fn convert_let_inside_loop() {
1093        check_assist(
1094            convert_to_guarded_return,
1095            r#"
1096fn main() {
1097    loop {
1098        if$0 let Some(n) = n {
1099            foo(n);
1100            bar();
1101        }
1102    }
1103}
1104"#,
1105            r#"
1106fn main() {
1107    loop {
1108        let Some(n) = n else { continue };
1109        foo(n);
1110        bar();
1111    }
1112}
1113"#,
1114        );
1115    }
1116
1117    #[test]
1118    fn convert_let_inside_for() {
1119        check_assist(
1120            convert_to_guarded_return,
1121            r#"
1122//- minicore: iterator
1123fn main() {
1124    for n in ns {
1125        if$0 let Some(n) = n {
1126            foo(n);
1127            bar();
1128        }
1129    }
1130}
1131"#,
1132            r#"
1133fn main() {
1134    for n in ns {
1135        let Some(n) = n else { continue };
1136        foo(n);
1137        bar();
1138    }
1139}
1140"#,
1141        );
1142    }
1143
1144    #[test]
1145    fn convert_let_inside_labeled_block() {
1146        check_assist(
1147            convert_to_guarded_return,
1148            r#"
1149fn main() {
1150    'l: {
1151        if$0 let Some(n) = n {
1152            foo(n);
1153            bar();
1154        }
1155    }
1156}
1157"#,
1158            r#"
1159fn main() {
1160    'l: {
1161        let Some(n) = n else { break 'l };
1162        foo(n);
1163        bar();
1164    }
1165}
1166"#,
1167        );
1168    }
1169
1170    #[test]
1171    fn convert_let_inside_for_with_else() {
1172        check_assist(
1173            convert_to_guarded_return,
1174            r#"
1175//- minicore: iterator
1176fn main() {
1177    for n in ns {
1178        if$0 let Some(n) = n {
1179            foo(n);
1180            bar();
1181        } else {
1182            baz()
1183        }
1184    }
1185}
1186"#,
1187            r#"
1188fn main() {
1189    for n in ns {
1190        let Some(n) = n else {
1191            baz();
1192            continue
1193        };
1194        foo(n);
1195        bar();
1196    }
1197}
1198"#,
1199        );
1200    }
1201
1202    #[test]
1203    fn convert_let_inside_for_with_else_if() {
1204        check_assist(
1205            convert_to_guarded_return,
1206            r#"
1207//- minicore: iterator
1208fn main() {
1209    for n in ns {
1210        if$0 let Some(n) = n {
1211            foo(n);
1212            bar();
1213        } else if cond() {
1214            return
1215        } else {
1216            baz()
1217        }
1218    }
1219}
1220"#,
1221            r#"
1222fn main() {
1223    for n in ns {
1224        let Some(n) = n else {
1225            if cond() {
1226                return
1227            } else {
1228                baz()
1229            }
1230            continue
1231        };
1232        foo(n);
1233        bar();
1234    }
1235}
1236"#,
1237        );
1238
1239        check_assist(
1240            convert_to_guarded_return,
1241            r#"
1242//- minicore: iterator
1243fn main() {
1244    for n in ns {
1245        if$0 let Some(n) = n {
1246            foo(n);
1247            bar();
1248        } else if cond() {
1249            return
1250        }
1251    }
1252}
1253"#,
1254            r#"
1255fn main() {
1256    for n in ns {
1257        let Some(n) = n else {
1258            if cond() {
1259                return
1260            }
1261            continue
1262        };
1263        foo(n);
1264        bar();
1265    }
1266}
1267"#,
1268        );
1269
1270        check_assist(
1271            convert_to_guarded_return,
1272            r#"
1273//- minicore: iterator
1274fn main() {
1275    for n in ns {
1276        if$0 let Some(n) = n {
1277            foo(n);
1278            bar();
1279        } else if cond() {
1280            return
1281        } else {
1282            break
1283        }
1284    }
1285}
1286"#,
1287            r#"
1288fn main() {
1289    for n in ns {
1290        let Some(n) = n else {
1291            if cond() {
1292                return
1293            } else {
1294                break
1295            }
1296        };
1297        foo(n);
1298        bar();
1299    }
1300}
1301"#,
1302        );
1303    }
1304
1305    #[test]
1306    fn convert_let_stmt_inside_fn() {
1307        check_assist(
1308            convert_to_guarded_return,
1309            r#"
1310//- minicore: option
1311fn foo() -> Option<i32> {
1312    None
1313}
1314
1315fn main() {
1316    let x$0 = foo();
1317}
1318"#,
1319            r#"
1320fn foo() -> Option<i32> {
1321    None
1322}
1323
1324fn main() {
1325    let Some(x) = foo() else { return };
1326}
1327"#,
1328        );
1329    }
1330
1331    #[test]
1332    fn convert_let_ref_stmt_inside_fn() {
1333        check_assist(
1334            convert_to_guarded_return,
1335            r#"
1336//- minicore: option
1337fn foo() -> &'static Option<i32> {
1338    &None
1339}
1340
1341fn main() {
1342    let x$0 = foo();
1343}
1344"#,
1345            r#"
1346fn foo() -> &'static Option<i32> {
1347    &None
1348}
1349
1350fn main() {
1351    let Some(x) = foo() else { return };
1352}
1353"#,
1354        );
1355    }
1356
1357    #[test]
1358    fn convert_let_stmt_inside_fn_return_option() {
1359        check_assist(
1360            convert_to_guarded_return,
1361            r#"
1362//- minicore: option
1363fn foo() -> Option<i32> {
1364    None
1365}
1366
1367fn ret_option() -> Option<i32> {
1368    let x$0 = foo();
1369}
1370"#,
1371            r#"
1372fn foo() -> Option<i32> {
1373    None
1374}
1375
1376fn ret_option() -> Option<i32> {
1377    let Some(x) = foo() else { return None };
1378}
1379"#,
1380        );
1381    }
1382
1383    #[test]
1384    fn convert_let_stmt_inside_loop() {
1385        check_assist(
1386            convert_to_guarded_return,
1387            r#"
1388//- minicore: option
1389fn foo() -> Option<i32> {
1390    None
1391}
1392
1393fn main() {
1394    loop {
1395        let x$0 = foo();
1396    }
1397}
1398"#,
1399            r#"
1400fn foo() -> Option<i32> {
1401    None
1402}
1403
1404fn main() {
1405    loop {
1406        let Some(x) = foo() else { continue };
1407    }
1408}
1409"#,
1410        );
1411    }
1412
1413    #[test]
1414    fn convert_arbitrary_if_let_patterns() {
1415        check_assist(
1416            convert_to_guarded_return,
1417            r#"
1418fn main() {
1419    $0if let None = Some(92) {
1420        foo();
1421    }
1422}
1423"#,
1424            r#"
1425fn main() {
1426    let None = Some(92) else { return };
1427    foo();
1428}
1429"#,
1430        );
1431
1432        check_assist(
1433            convert_to_guarded_return,
1434            r#"
1435fn main() {
1436    $0if let [1, x] = [1, 92] {
1437        foo(x);
1438    }
1439}
1440"#,
1441            r#"
1442fn main() {
1443    let [1, x] = [1, 92] else { return };
1444    foo(x);
1445}
1446"#,
1447        );
1448
1449        check_assist(
1450            convert_to_guarded_return,
1451            r#"
1452fn main() {
1453    $0if let (Some(x), None) = (Some(92), None) {
1454        foo(x);
1455    }
1456}
1457"#,
1458            r#"
1459fn main() {
1460    let (Some(x), None) = (Some(92), None) else { return };
1461    foo(x);
1462}
1463"#,
1464        );
1465    }
1466
1467    #[test]
1468    fn indentations() {
1469        check_assist(
1470            convert_to_guarded_return,
1471            r#"
1472mod indent {
1473    fn main() {
1474        $0if let None = Some(
1475            92
1476        ) {
1477            foo(
1478                93
1479            );
1480        }
1481    }
1482}
1483"#,
1484            r#"
1485mod indent {
1486    fn main() {
1487        let None = Some(
1488            92
1489        ) else { return };
1490        foo(
1491            93
1492        );
1493    }
1494}
1495"#,
1496        );
1497
1498        check_assist(
1499            convert_to_guarded_return,
1500            r#"
1501//- minicore: option
1502mod indent {
1503    fn foo(_: i32) -> Option<i32> { None }
1504    fn main() {
1505        $0let x = foo(
1506            2
1507        );
1508    }
1509}
1510"#,
1511            r#"
1512mod indent {
1513    fn foo(_: i32) -> Option<i32> { None }
1514    fn main() {
1515        let Some(x) = foo(
1516            2
1517        ) else { return };
1518    }
1519}
1520"#,
1521        );
1522    }
1523
1524    #[test]
1525    fn ignore_already_converted_if() {
1526        check_assist_not_applicable(
1527            convert_to_guarded_return,
1528            r#"
1529fn main() {
1530    if$0 true {
1531        return;
1532    }
1533}
1534"#,
1535        );
1536    }
1537
1538    #[test]
1539    fn ignore_already_converted_loop() {
1540        check_assist_not_applicable(
1541            convert_to_guarded_return,
1542            r#"
1543fn main() {
1544    loop {
1545        if$0 true {
1546            continue;
1547        }
1548    }
1549}
1550"#,
1551        );
1552    }
1553
1554    #[test]
1555    fn ignore_return() {
1556        check_assist_not_applicable(
1557            convert_to_guarded_return,
1558            r#"
1559fn main() {
1560    if$0 true {
1561        return
1562    }
1563}
1564"#,
1565        );
1566    }
1567
1568    #[test]
1569    fn ignore_else_branch_has_non_never_types_in_statement() {
1570        check_assist_not_applicable(
1571            convert_to_guarded_return,
1572            r#"
1573fn main() {
1574    some_statements();
1575    if$0 true {
1576        foo();
1577    } else {
1578        bar()
1579    }
1580    some_statements();
1581}
1582"#,
1583        );
1584    }
1585
1586    #[test]
1587    fn ignore_on_else_if() {
1588        check_assist_not_applicable(
1589            convert_to_guarded_return,
1590            r#"
1591fn main() {
1592    some_statements();
1593    if cond {
1594        ()
1595    } else if$0 let Ok(x) = Err(92) {
1596        foo(x);
1597    } else {
1598        return;
1599    }
1600    some_statements();
1601}
1602"#,
1603        );
1604    }
1605
1606    #[test]
1607    fn ignore_if_inside_let() {
1608        check_assist_not_applicable(
1609            convert_to_guarded_return,
1610            r#"
1611fn main() {
1612    some_statements();
1613    let _ = if$0 let Ok(x) = Err(92) {
1614        foo(x);
1615    } else {
1616        return;
1617    }
1618    some_statements();
1619}
1620"#,
1621        );
1622    }
1623
1624    #[test]
1625    fn ignore_let_else_branch() {
1626        check_assist_not_applicable(
1627            convert_to_guarded_return,
1628            r#"
1629//- minicore: option
1630fn main() {
1631    let$0 Some(x) = Some(2) else { return };
1632}
1633"#,
1634        );
1635    }
1636
1637    #[test]
1638    fn ignore_statements_after_if() {
1639        check_assist_not_applicable(
1640            convert_to_guarded_return,
1641            r#"
1642fn main() {
1643    if$0 true {
1644        foo();
1645    }
1646    bar();
1647}
1648"#,
1649        );
1650    }
1651
1652    #[test]
1653    fn ignore_statements_inside_if() {
1654        check_assist_not_applicable(
1655            convert_to_guarded_return,
1656            r#"
1657fn main() {
1658    if false {
1659        if$0 true {
1660            foo();
1661        }
1662    }
1663}
1664"#,
1665        );
1666    }
1667
1668    #[test]
1669    fn ignore_inside_if_stmt() {
1670        check_assist_not_applicable(
1671            convert_to_guarded_return,
1672            r#"
1673fn main() {
1674    if false {
1675        foo()$0;
1676    }
1677}
1678"#,
1679        );
1680    }
1681
1682    #[test]
1683    fn ignore_inside_let_initializer() {
1684        check_assist_not_applicable(
1685            convert_to_guarded_return,
1686            r#"
1687//- minicore: option
1688fn foo() -> Option<i32> {
1689    None
1690}
1691
1692fn main() {
1693    let x = foo()$0;
1694}
1695"#,
1696        );
1697    }
1698}