]> git.lizzy.rs Git - rust.git/blob - crates/ra_assists/src/handlers/replace_if_let_with_match.rs
Merge assits::test_helpers and tests
[rust.git] / crates / ra_assists / src / handlers / replace_if_let_with_match.rs
1 use ra_fmt::unwrap_trivial_block;
2 use ra_syntax::{
3     ast::{self, edit::IndentLevel, make},
4     AstNode,
5 };
6
7 use crate::{utils::TryEnum, Assist, AssistCtx, AssistId};
8
9 // Assist: replace_if_let_with_match
10 //
11 // Replaces `if let` with an else branch with a `match` expression.
12 //
13 // ```
14 // enum Action { Move { distance: u32 }, Stop }
15 //
16 // fn handle(action: Action) {
17 //     <|>if let Action::Move { distance } = action {
18 //         foo(distance)
19 //     } else {
20 //         bar()
21 //     }
22 // }
23 // ```
24 // ->
25 // ```
26 // enum Action { Move { distance: u32 }, Stop }
27 //
28 // fn handle(action: Action) {
29 //     match action {
30 //         Action::Move { distance } => foo(distance),
31 //         _ => bar(),
32 //     }
33 // }
34 // ```
35 pub(crate) fn replace_if_let_with_match(ctx: AssistCtx) -> Option<Assist> {
36     let if_expr: ast::IfExpr = ctx.find_node_at_offset()?;
37     let cond = if_expr.condition()?;
38     let pat = cond.pat()?;
39     let expr = cond.expr()?;
40     let then_block = if_expr.then_branch()?;
41     let else_block = match if_expr.else_branch()? {
42         ast::ElseBranch::Block(it) => it,
43         ast::ElseBranch::IfExpr(_) => return None,
44     };
45
46     let sema = ctx.sema;
47     ctx.add_assist(AssistId("replace_if_let_with_match"), "Replace with match", move |edit| {
48         let match_expr = {
49             let then_arm = {
50                 let then_expr = unwrap_trivial_block(then_block);
51                 make::match_arm(vec![pat.clone()], then_expr)
52             };
53             let else_arm = {
54                 let pattern = sema
55                     .type_of_pat(&pat)
56                     .and_then(|ty| TryEnum::from_ty(sema, &ty))
57                     .map(|it| it.sad_pattern())
58                     .unwrap_or_else(|| make::placeholder_pat().into());
59                 let else_expr = unwrap_trivial_block(else_block);
60                 make::match_arm(vec![pattern], else_expr)
61             };
62             make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm]))
63         };
64
65         let match_expr = IndentLevel::from_node(if_expr.syntax()).increase_indent(match_expr);
66
67         edit.target(if_expr.syntax().text_range());
68         edit.set_cursor(if_expr.syntax().text_range().start());
69         edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr);
70     })
71 }
72
73 #[cfg(test)]
74 mod tests {
75     use super::*;
76
77     use crate::tests::{check_assist, check_assist_target};
78
79     #[test]
80     fn test_replace_if_let_with_match_unwraps_simple_expressions() {
81         check_assist(
82             replace_if_let_with_match,
83             "
84 impl VariantData {
85     pub fn is_struct(&self) -> bool {
86         if <|>let VariantData::Struct(..) = *self {
87             true
88         } else {
89             false
90         }
91     }
92 }           ",
93             "
94 impl VariantData {
95     pub fn is_struct(&self) -> bool {
96         <|>match *self {
97             VariantData::Struct(..) => true,
98             _ => false,
99         }
100     }
101 }           ",
102         )
103     }
104
105     #[test]
106     fn test_replace_if_let_with_match_doesnt_unwrap_multiline_expressions() {
107         check_assist(
108             replace_if_let_with_match,
109             "
110 fn foo() {
111     if <|>let VariantData::Struct(..) = a {
112         bar(
113             123
114         )
115     } else {
116         false
117     }
118 }           ",
119             "
120 fn foo() {
121     <|>match a {
122         VariantData::Struct(..) => {
123             bar(
124                 123
125             )
126         }
127         _ => false,
128     }
129 }           ",
130         )
131     }
132
133     #[test]
134     fn replace_if_let_with_match_target() {
135         check_assist_target(
136             replace_if_let_with_match,
137             "
138 impl VariantData {
139     pub fn is_struct(&self) -> bool {
140         if <|>let VariantData::Struct(..) = *self {
141             true
142         } else {
143             false
144         }
145     }
146 }           ",
147             "if let VariantData::Struct(..) = *self {
148             true
149         } else {
150             false
151         }",
152         );
153     }
154
155     #[test]
156     fn special_case_option() {
157         check_assist(
158             replace_if_let_with_match,
159             r#"
160 enum Option<T> { Some(T), None }
161 use Option::*;
162
163 fn foo(x: Option<i32>) {
164     <|>if let Some(x) = x {
165         println!("{}", x)
166     } else {
167         println!("none")
168     }
169 }
170            "#,
171             r#"
172 enum Option<T> { Some(T), None }
173 use Option::*;
174
175 fn foo(x: Option<i32>) {
176     <|>match x {
177         Some(x) => println!("{}", x),
178         None => println!("none"),
179     }
180 }
181            "#,
182         );
183     }
184
185     #[test]
186     fn special_case_result() {
187         check_assist(
188             replace_if_let_with_match,
189             r#"
190 enum Result<T, E> { Ok(T), Err(E) }
191 use Result::*;
192
193 fn foo(x: Result<i32, ()>) {
194     <|>if let Ok(x) = x {
195         println!("{}", x)
196     } else {
197         println!("none")
198     }
199 }
200            "#,
201             r#"
202 enum Result<T, E> { Ok(T), Err(E) }
203 use Result::*;
204
205 fn foo(x: Result<i32, ()>) {
206     <|>match x {
207         Ok(x) => println!("{}", x),
208         Err(_) => println!("none"),
209     }
210 }
211            "#,
212         );
213     }
214 }