2 ast::{edit::AstNodeEdit, make, AstNode, BlockExpr, ElseBranch, Expr, IfExpr, MatchArm, Pat},
3 SyntaxKind::WHITESPACE,
6 use crate::{AssistContext, AssistId, AssistKind, Assists};
8 // Assist: move_guard_to_arm_body
10 // Moves match guard into match arm body.
13 // enum Action { Move { distance: u32 }, Stop }
15 // fn handle(action: Action) {
17 // Action::Move { distance } $0if distance > 10 => foo(),
24 // enum Action { Move { distance: u32 }, Stop }
26 // fn handle(action: Action) {
28 // Action::Move { distance } => if distance > 10 {
35 pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
36 let match_arm = ctx.find_node_at_offset::<MatchArm>()?;
37 let guard = match_arm.guard()?;
38 if ctx.offset() > guard.syntax().text_range().end() {
39 cov_mark::hit!(move_guard_unapplicable_in_arm_body);
42 let space_before_guard = guard.syntax().prev_sibling_or_token();
44 let guard_condition = guard.condition()?;
45 let arm_expr = match_arm.expr()?;
47 make::expr_if(guard_condition, make::block_expr(None, Some(arm_expr.clone())), None)
48 .indent(arm_expr.indent_level());
50 let target = guard.syntax().text_range();
52 AssistId("move_guard_to_arm_body", AssistKind::RefactorRewrite),
53 "Move guard to arm body",
56 match space_before_guard {
57 Some(element) if element.kind() == WHITESPACE => {
58 edit.delete(element.text_range());
63 edit.delete(guard.syntax().text_range());
64 edit.replace_ast(arm_expr, if_expr);
69 // Assist: move_arm_cond_to_match_guard
71 // Moves if expression from match arm body into a guard.
74 // enum Action { Move { distance: u32 }, Stop }
76 // fn handle(action: Action) {
78 // Action::Move { distance } => $0if distance > 10 { foo() },
85 // enum Action { Move { distance: u32 }, Stop }
87 // fn handle(action: Action) {
89 // Action::Move { distance } if distance > 10 => foo(),
94 pub(crate) fn move_arm_cond_to_match_guard(
96 ctx: &AssistContext<'_>,
98 let match_arm: MatchArm = ctx.find_node_at_offset::<MatchArm>()?;
99 let match_pat = match_arm.pat()?;
100 let arm_body = match_arm.expr()?;
102 let mut replace_node = None;
103 let if_expr: IfExpr = IfExpr::cast(arm_body.syntax().clone()).or_else(|| {
104 let block_expr = BlockExpr::cast(arm_body.syntax().clone())?;
105 if let Expr::IfExpr(e) = block_expr.tail_expr()? {
106 replace_node = Some(block_expr.syntax().clone());
112 if ctx.offset() > if_expr.then_branch()?.syntax().text_range().start() {
116 let replace_node = replace_node.unwrap_or_else(|| if_expr.syntax().clone());
117 let needs_dedent = replace_node != *if_expr.syntax();
118 let (conds_blocks, tail) = parse_if_chain(if_expr)?;
121 AssistId("move_arm_cond_to_match_guard", AssistKind::RefactorRewrite),
122 "Move condition to match guard",
123 replace_node.text_range(),
125 edit.delete(match_arm.syntax().text_range());
126 // Dedent if if_expr is in a BlockExpr
127 let dedent = if needs_dedent {
128 cov_mark::hit!(move_guard_ifelse_in_block);
131 cov_mark::hit!(move_guard_ifelse_else_block);
134 let then_arm_end = match_arm.syntax().text_range().end();
135 let indent_level = match_arm.indent_level();
136 let spaces = " ".repeat(indent_level.0 as _);
138 let mut first = true;
139 for (cond, block) in conds_blocks {
141 edit.insert(then_arm_end, format!("\n{}", spaces));
145 let guard = format!("{} if {} => ", match_pat, cond.syntax().text());
146 edit.insert(then_arm_end, guard);
147 let only_expr = block.statements().next().is_none();
148 match &block.tail_expr() {
149 Some(then_expr) if only_expr => {
150 edit.insert(then_arm_end, then_expr.syntax().text());
151 edit.insert(then_arm_end, ",");
154 let to_insert = block.dedent(dedent.into()).syntax().text();
155 edit.insert(then_arm_end, to_insert)
159 if let Some(e) = tail {
160 cov_mark::hit!(move_guard_ifelse_else_tail);
161 let guard = format!("\n{}{} => ", spaces, match_pat);
162 edit.insert(then_arm_end, guard);
163 let only_expr = e.statements().next().is_none();
164 match &e.tail_expr() {
165 Some(expr) if only_expr => {
166 cov_mark::hit!(move_guard_ifelse_expr_only);
167 edit.insert(then_arm_end, expr.syntax().text());
168 edit.insert(then_arm_end, ",");
171 let to_insert = e.dedent(dedent.into()).syntax().text();
172 edit.insert(then_arm_end, to_insert)
176 // There's no else branch. Add a pattern without guard, unless the following match
178 cov_mark::hit!(move_guard_ifelse_notail);
179 match match_arm.syntax().next_sibling().and_then(MatchArm::cast) {
181 if matches!(next_arm.pat(), Some(Pat::WildcardPat(_)))
182 && next_arm.guard().is_none() =>
184 cov_mark::hit!(move_guard_ifelse_has_wildcard);
186 _ => edit.insert(then_arm_end, format!("\n{}{} => {{}}", spaces, match_pat)),
193 // Parses an if-else-if chain to get the conditions and the then branches until we encounter an else
194 // branch or the end.
195 fn parse_if_chain(if_expr: IfExpr) -> Option<(Vec<(Expr, BlockExpr)>, Option<BlockExpr>)> {
196 let mut conds_blocks = Vec::new();
197 let mut curr_if = if_expr;
199 let cond = curr_if.condition()?;
200 conds_blocks.push((cond, curr_if.then_branch()?));
201 match curr_if.else_branch() {
202 Some(ElseBranch::IfExpr(e)) => {
205 Some(ElseBranch::Block(b)) => {
211 Some((conds_blocks, tail))
218 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
221 fn move_guard_to_arm_body_range() {
222 cov_mark::check!(move_guard_unapplicable_in_arm_body);
223 check_assist_not_applicable(
224 move_guard_to_arm_body,
228 x if x > 10 => $0false,
236 fn move_guard_to_arm_body_target() {
238 move_guard_to_arm_body,
242 x $0if x > 10 => false,
252 fn move_guard_to_arm_body_works() {
254 move_guard_to_arm_body,
258 x $0if x > 10 => false,
277 fn move_let_guard_to_arm_body_works() {
279 move_guard_to_arm_body,
283 x $0if (let 1 = x) => false,
291 x => if (let 1 = x) {
302 fn move_guard_to_arm_body_works_complex_match() {
304 move_guard_to_arm_body,
308 $0x @ 4 | x @ 5 if x > 5 => true,
316 x @ 4 | x @ 5 => if x > 5 {
327 fn move_arm_cond_to_match_guard_works() {
329 move_arm_cond_to_match_guard,
333 x => if x > 10$0 { false },
341 x if x > 10 => false,
350 fn move_arm_cond_in_block_to_match_guard_works() {
351 cov_mark::check!(move_guard_ifelse_has_wildcard);
353 move_arm_cond_to_match_guard,
369 x if x > 10 => false,
378 fn move_arm_cond_in_block_to_match_guard_no_wildcard_works() {
379 cov_mark::check_count!(move_guard_ifelse_has_wildcard, 0);
381 move_arm_cond_to_match_guard,
396 x if x > 10 => false,
405 fn move_arm_cond_in_block_to_match_guard_wildcard_guard_works() {
406 cov_mark::check_count!(move_guard_ifelse_has_wildcard, 0);
408 move_arm_cond_to_match_guard,
424 x if x > 10 => false,
434 fn move_arm_cond_in_block_to_match_guard_add_comma_works() {
436 move_arm_cond_to_match_guard,
452 x if x > 10 => false,
461 fn move_arm_cond_to_match_guard_if_let_works() {
463 move_arm_cond_to_match_guard,
467 x => if let 62 = x $0&& true { false },
475 x if let 62 = x && true => false,
484 fn move_arm_cond_to_match_guard_if_empty_body_works() {
486 move_arm_cond_to_match_guard,
490 x => if x $0> 10 { },
507 fn move_arm_cond_to_match_guard_if_multiline_body_works() {
509 move_arm_cond_to_match_guard,
536 fn move_arm_cond_in_block_to_match_guard_if_multiline_body_works() {
538 move_arm_cond_to_match_guard,
567 fn move_arm_cond_to_match_guard_with_else_works() {
569 move_arm_cond_to_match_guard,
585 x if x > 10 => false,
595 fn move_arm_cond_to_match_guard_with_else_block_works() {
596 cov_mark::check!(move_guard_ifelse_expr_only);
598 move_arm_cond_to_match_guard,
616 x if x > 10 => false,
626 fn move_arm_cond_to_match_guard_else_if_empty_body_works() {
628 move_arm_cond_to_match_guard,
632 x => if x > $010 { } else { },
650 fn move_arm_cond_to_match_guard_with_else_multiline_works() {
652 move_arm_cond_to_match_guard,
682 fn move_arm_cond_to_match_guard_with_else_multiline_else_works() {
683 cov_mark::check!(move_guard_ifelse_else_block);
685 move_arm_cond_to_match_guard,
702 x if x > 10 => false,
715 fn move_arm_cond_to_match_guard_with_else_multiline_else_block_works() {
716 cov_mark::check!(move_guard_ifelse_in_block);
718 move_arm_cond_to_match_guard,
737 x if x > 10 => false,
750 fn move_arm_cond_to_match_guard_with_else_last_arm_works() {
752 move_arm_cond_to_match_guard,
772 x if x > 10 => false,
784 fn move_arm_cond_to_match_guard_with_else_comma_works() {
786 move_arm_cond_to_match_guard,
804 x if x > 10 => false,
816 fn move_arm_cond_to_match_guard_elseif() {
818 move_arm_cond_to_match_guard,
839 x if x > 10 => false,
850 fn move_arm_cond_to_match_guard_elseif_in_block() {
851 cov_mark::check!(move_guard_ifelse_in_block);
853 move_arm_cond_to_match_guard,
876 x if x > 10 => false,
887 fn move_arm_cond_to_match_guard_elseif_chain() {
888 cov_mark::check!(move_guard_ifelse_else_tail);
890 move_arm_cond_to_match_guard,
926 fn move_arm_cond_to_match_guard_elseif_iflet() {
928 move_arm_cond_to_match_guard,
937 } else if let 4 = 4 {
962 fn move_arm_cond_to_match_guard_elseif_notail() {
963 cov_mark::check!(move_guard_ifelse_notail);
965 move_arm_cond_to_match_guard,