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
25pub(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 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 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 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}