3 edit::AstNodeEdit, make, AstNode, BlockExpr, Condition, ElseBranch, Expr, IfExpr, MatchArm,
6 SyntaxKind::WHITESPACE,
9 use crate::{AssistContext, AssistId, AssistKind, Assists};
11 // Assist: move_guard_to_arm_body
13 // Moves match guard into match arm body.
16 // enum Action { Move { distance: u32 }, Stop }
18 // fn handle(action: Action) {
20 // Action::Move { distance } $0if distance > 10 => foo(),
27 // enum Action { Move { distance: u32 }, Stop }
29 // fn handle(action: Action) {
31 // Action::Move { distance } => if distance > 10 {
38 pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
39 let match_arm = ctx.find_node_at_offset::<MatchArm>()?;
40 let guard = match_arm.guard()?;
41 if ctx.offset() > guard.syntax().text_range().end() {
42 cov_mark::hit!(move_guard_unapplicable_in_arm_body);
45 let space_before_guard = guard.syntax().prev_sibling_or_token();
47 // FIXME: support `if let` guards too
48 if guard.let_token().is_some() {
51 let guard_condition = guard.expr()?;
52 let arm_expr = match_arm.expr()?;
53 let if_expr = make::expr_if(
54 make::condition(guard_condition, None),
55 make::block_expr(None, Some(arm_expr.clone())),
58 .indent(arm_expr.indent_level());
60 let target = guard.syntax().text_range();
62 AssistId("move_guard_to_arm_body", AssistKind::RefactorRewrite),
63 "Move guard to arm body",
66 match space_before_guard {
67 Some(element) if element.kind() == WHITESPACE => {
68 edit.delete(element.text_range());
73 edit.delete(guard.syntax().text_range());
74 edit.replace_ast(arm_expr, if_expr);
79 // Assist: move_arm_cond_to_match_guard
81 // Moves if expression from match arm body into a guard.
84 // enum Action { Move { distance: u32 }, Stop }
86 // fn handle(action: Action) {
88 // Action::Move { distance } => $0if distance > 10 { foo() },
95 // enum Action { Move { distance: u32 }, Stop }
97 // fn handle(action: Action) {
99 // Action::Move { distance } if distance > 10 => foo(),
104 pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
105 let match_arm: MatchArm = ctx.find_node_at_offset::<MatchArm>()?;
106 let match_pat = match_arm.pat()?;
107 let arm_body = match_arm.expr()?;
109 let mut replace_node = None;
110 let if_expr: IfExpr = IfExpr::cast(arm_body.syntax().clone()).or_else(|| {
111 let block_expr = BlockExpr::cast(arm_body.syntax().clone())?;
112 if let Expr::IfExpr(e) = block_expr.tail_expr()? {
113 replace_node = Some(block_expr.syntax().clone());
119 let replace_node = replace_node.unwrap_or_else(|| if_expr.syntax().clone());
120 let needs_dedent = replace_node != *if_expr.syntax();
121 let (conds_blocks, tail) = parse_if_chain(if_expr)?;
124 AssistId("move_arm_cond_to_match_guard", AssistKind::RefactorRewrite),
125 "Move condition to match guard",
126 replace_node.text_range(),
128 edit.delete(match_arm.syntax().text_range());
129 // Dedent if if_expr is in a BlockExpr
130 let dedent = if needs_dedent {
131 cov_mark::hit!(move_guard_ifelse_in_block);
134 cov_mark::hit!(move_guard_ifelse_else_block);
137 let then_arm_end = match_arm.syntax().text_range().end();
138 let indent_level = match_arm.indent_level();
139 let spaces = " ".repeat(indent_level.0 as _);
141 let mut first = true;
142 for (cond, block) in conds_blocks {
144 edit.insert(then_arm_end, format!("\n{}", spaces));
148 let guard = format!("{} if {} => ", match_pat, cond.syntax().text());
149 edit.insert(then_arm_end, guard);
150 let only_expr = block.statements().next().is_none();
151 match &block.tail_expr() {
152 Some(then_expr) if only_expr => {
153 edit.insert(then_arm_end, then_expr.syntax().text());
154 edit.insert(then_arm_end, ",");
157 let to_insert = block.dedent(dedent.into()).syntax().text();
158 edit.insert(then_arm_end, to_insert)
162 if let Some(e) = tail {
163 cov_mark::hit!(move_guard_ifelse_else_tail);
164 let guard = format!("\n{}{} => ", spaces, match_pat);
165 edit.insert(then_arm_end, guard);
166 let only_expr = e.statements().next().is_none();
167 match &e.tail_expr() {
168 Some(expr) if only_expr => {
169 cov_mark::hit!(move_guard_ifelse_expr_only);
170 edit.insert(then_arm_end, expr.syntax().text());
171 edit.insert(then_arm_end, ",");
174 let to_insert = e.dedent(dedent.into()).syntax().text();
175 edit.insert(then_arm_end, to_insert)
179 // There's no else branch. Add a pattern without guard, unless the following match
181 cov_mark::hit!(move_guard_ifelse_notail);
182 match match_arm.syntax().next_sibling().and_then(MatchArm::cast) {
184 if matches!(next_arm.pat(), Some(Pat::WildcardPat(_)))
185 && next_arm.guard().is_none() =>
187 cov_mark::hit!(move_guard_ifelse_has_wildcard);
189 _ => edit.insert(then_arm_end, format!("\n{}{} => {{}}", spaces, match_pat)),
196 // Parses an if-else-if chain to get the conditons and the then branches until we encounter an else
197 // branch or the end.
198 fn parse_if_chain(if_expr: IfExpr) -> Option<(Vec<(Condition, BlockExpr)>, Option<BlockExpr>)> {
199 let mut conds_blocks = Vec::new();
200 let mut curr_if = if_expr;
202 let cond = curr_if.condition()?;
203 // Not support moving if let to arm guard
204 if cond.is_pattern_cond() {
207 conds_blocks.push((cond, curr_if.then_branch()?));
208 match curr_if.else_branch() {
209 Some(ElseBranch::IfExpr(e)) => {
212 Some(ElseBranch::Block(b)) => {
218 Some((conds_blocks, tail))
225 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
228 fn move_guard_to_arm_body_range() {
229 cov_mark::check!(move_guard_unapplicable_in_arm_body);
230 check_assist_not_applicable(
231 move_guard_to_arm_body,
235 x if x > 10 => $0false,
243 fn move_guard_to_arm_body_target() {
245 move_guard_to_arm_body,
249 x $0if x > 10 => false,
259 fn move_guard_to_arm_body_works() {
261 move_guard_to_arm_body,
265 x $0if x > 10 => false,
284 fn move_guard_to_arm_body_works_complex_match() {
286 move_guard_to_arm_body,
290 $0x @ 4 | x @ 5 if x > 5 => true,
298 x @ 4 | x @ 5 => if x > 5 {
309 fn move_arm_cond_to_match_guard_works() {
311 move_arm_cond_to_match_guard,
315 x => if x > 10 { $0false },
323 x if x > 10 => false,
332 fn move_arm_cond_in_block_to_match_guard_works() {
333 cov_mark::check!(move_guard_ifelse_has_wildcard);
335 move_arm_cond_to_match_guard,
351 x if x > 10 => false,
360 fn move_arm_cond_in_block_to_match_guard_no_wildcard_works() {
361 cov_mark::check_count!(move_guard_ifelse_has_wildcard, 0);
363 move_arm_cond_to_match_guard,
378 x if x > 10 => false,
387 fn move_arm_cond_in_block_to_match_guard_wildcard_guard_works() {
388 cov_mark::check_count!(move_guard_ifelse_has_wildcard, 0);
390 move_arm_cond_to_match_guard,
406 x if x > 10 => false,
416 fn move_arm_cond_in_block_to_match_guard_add_comma_works() {
418 move_arm_cond_to_match_guard,
434 x if x > 10 => false,
443 fn move_arm_cond_to_match_guard_if_let_not_works() {
444 check_assist_not_applicable(
445 move_arm_cond_to_match_guard,
449 x => if let 62 = x { $0false },
458 fn move_arm_cond_to_match_guard_if_empty_body_works() {
460 move_arm_cond_to_match_guard,
464 x => if x > 10 { $0 },
481 fn move_arm_cond_to_match_guard_if_multiline_body_works() {
483 move_arm_cond_to_match_guard,
510 fn move_arm_cond_in_block_to_match_guard_if_multiline_body_works() {
512 move_arm_cond_to_match_guard,
541 fn move_arm_cond_to_match_guard_with_else_works() {
543 move_arm_cond_to_match_guard,
559 x if x > 10 => false,
569 fn move_arm_cond_to_match_guard_with_else_block_works() {
570 cov_mark::check!(move_guard_ifelse_expr_only);
572 move_arm_cond_to_match_guard,
590 x if x > 10 => false,
600 fn move_arm_cond_to_match_guard_else_if_empty_body_works() {
602 move_arm_cond_to_match_guard,
606 x => if x > 10 { $0 } else { },
624 fn move_arm_cond_to_match_guard_with_else_multiline_works() {
626 move_arm_cond_to_match_guard,
656 fn move_arm_cond_to_match_guard_with_else_multiline_else_works() {
657 cov_mark::check!(move_guard_ifelse_else_block);
659 move_arm_cond_to_match_guard,
676 x if x > 10 => false,
689 fn move_arm_cond_to_match_guard_with_else_multiline_else_block_works() {
690 cov_mark::check!(move_guard_ifelse_in_block);
692 move_arm_cond_to_match_guard,
711 x if x > 10 => false,
724 fn move_arm_cond_to_match_guard_with_else_last_arm_works() {
726 move_arm_cond_to_match_guard,
746 x if x > 10 => false,
758 fn move_arm_cond_to_match_guard_with_else_comma_works() {
760 move_arm_cond_to_match_guard,
778 x if x > 10 => false,
790 fn move_arm_cond_to_match_guard_elseif() {
792 move_arm_cond_to_match_guard,
813 x if x > 10 => false,
824 fn move_arm_cond_to_match_guard_elseif_in_block() {
825 cov_mark::check!(move_guard_ifelse_in_block);
827 move_arm_cond_to_match_guard,
850 x if x > 10 => false,
861 fn move_arm_cond_to_match_guard_elseif_chain() {
862 cov_mark::check!(move_guard_ifelse_else_tail);
864 move_arm_cond_to_match_guard,
900 fn move_arm_cond_to_match_guard_elseif_iflet() {
901 check_assist_not_applicable(
902 move_arm_cond_to_match_guard,
911 } else if let 4 = 4 {
924 fn move_arm_cond_to_match_guard_elseif_notail() {
925 cov_mark::check!(move_guard_ifelse_notail);
927 move_arm_cond_to_match_guard,