1 use std::iter::successors;
3 use hir::{TypeInfo, HirDisplay};
4 use itertools::Itertools;
11 use crate::{AssistContext, AssistId, AssistKind, Assists, TextRange};
13 // Assist: merge_match_arms
15 // Merges the current match arm with the following if their bodies are identical.
18 // enum Action { Move { distance: u32 }, Stop }
20 // fn handle(action: Action) {
22 // $0Action::Move(..) => foo(),
23 // Action::Stop => foo(),
29 // enum Action { Move { distance: u32 }, Stop }
31 // fn handle(action: Action) {
33 // Action::Move(..) | Action::Stop => foo(),
37 pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
38 let current_arm = ctx.find_node_at_offset::<ast::MatchArm>()?;
39 // Don't try to handle arms with guards for now - can add support for this later
40 if current_arm.guard().is_some() {
43 let current_expr = current_arm.expr()?;
44 let current_text_range = current_arm.syntax().text_range();
45 let current_arm_types = get_arm_types(&ctx, ¤t_arm);
47 // We check if the following match arms match this one. We could, but don't,
48 // compare to the previous match arm as well.
49 let arms_to_merge = successors(Some(current_arm), |it| neighbor(it, Direction::Next))
50 .take_while(|arm| match arm.expr() {
51 Some(expr) if arm.guard().is_none() && arm.pat().is_some() => {
52 let same_text = expr.syntax().text() == current_expr.syntax().text();
57 let arm_types = get_arm_types(&ctx, &arm);
58 for i in 0..arm_types.len() {
59 let other_arm_type = &arm_types[i].as_ref();
60 let current_arm_type = current_arm_types[i].as_ref();
61 if other_arm_type.is_some() && current_arm_type.is_some() {
62 let other_arm_type = other_arm_type.unwrap().original.clone().as_adt();
63 let current_arm_type = current_arm_type.unwrap().original.clone().as_adt();
64 println!("Same types!");
65 println!("{:?}", other_arm_type);
66 println!("{:?}", current_arm_type);
67 return other_arm_type == current_arm_type;
77 if arms_to_merge.len() <= 1 {
82 AssistId("merge_match_arms", AssistKind::RefactorRewrite),
86 let pats = if arms_to_merge.iter().any(contains_placeholder) {
91 .filter_map(ast::MatchArm::pat)
92 .map(|x| x.syntax().to_string())
93 .collect::<Vec<String>>()
97 let arm = format!("{} => {},", pats, current_expr.syntax().text());
99 if let [first, .., last] = &*arms_to_merge {
100 let start = first.syntax().text_range().start();
101 let end = last.syntax().text_range().end();
103 edit.replace(TextRange::new(start, end), arm);
109 fn contains_placeholder(a: &ast::MatchArm) -> bool {
110 matches!(a.pat(), Some(ast::Pat::WildcardPat(..)))
113 fn get_arm_types(ctx: &AssistContext, arm: &ast::MatchArm) -> Vec<Option<TypeInfo>> {
115 Some(ast::Pat::TupleStructPat(tp)) => tp
119 let pat_type = ctx.sema.type_of_pat(&field);
129 use crate::tests::{check_assist, check_assist_not_applicable};
134 fn merge_match_arms_single_patterns() {
157 X::A | X::B => { 1i32 },
166 fn merge_match_arms_multiple_patterns() {
171 enum X { A, B, C, D, E }
176 X::A | X::B => {$0 1i32 },
177 X::C | X::D => { 1i32 },
184 enum X { A, B, C, D, E }
189 X::A | X::B | X::C | X::D => { 1i32 },
198 fn merge_match_arms_placeholder_pattern() {
203 enum X { A, B, C, D, E }
216 enum X { A, B, C, D, E }
230 fn merges_all_subsequent_arms() {
234 enum X { A, B, C, D, E }
247 enum X { A, B, C, D, E }
251 X::A | X::B | X::C => 92,
261 fn merge_match_arms_rejects_guards() {
262 check_assist_not_applicable(
275 X::A(a) if a > 5 => { $01i32 },
285 fn merge_match_arms_different_type() {
286 check_assist_not_applicable(
290 match Result::<i32, f32>::Ok(0) {
291 Ok(x) => $0x.to_string(),
292 Err(x) => x.to_string()
301 // match Result::<i32, f32>::Ok(0) {
302 // Ok(x) => x.to_string(),
303 // Err(x) => x.to_string()