ide_assists/handlers/
replace_is_method_with_if_let_method.rs1use either::Either;
2use ide_db::syntax_helpers::suggest_name;
3use syntax::ast::{self, AstNode, syntax_factory::SyntaxFactory};
4
5use crate::{AssistContext, AssistId, Assists, utils::cover_let_chain};
6
7pub(crate) fn replace_is_method_with_if_let_method(
25 acc: &mut Assists,
26 ctx: &AssistContext<'_>,
27) -> Option<()> {
28 let has_cond = ctx.find_node_at_offset::<Either<ast::IfExpr, ast::WhileExpr>>()?;
29
30 let cond = either::for_both!(&has_cond, it => it.condition())?;
31 let cond = cover_let_chain(cond, ctx.selection_trimmed())?;
32 let call_expr = match cond {
33 ast::Expr::MethodCallExpr(call) => call,
34 _ => return None,
35 };
36
37 let name_ref = call_expr.name_ref()?;
38 match name_ref.text().as_str() {
39 "is_some" | "is_ok" => {
40 let receiver = call_expr.receiver()?;
41
42 let mut name_generator = suggest_name::NameGenerator::new_from_scope_locals(
43 ctx.sema.scope(has_cond.syntax()),
44 );
45 let var_name = if let ast::Expr::PathExpr(path_expr) = receiver.clone() {
46 name_generator.suggest_name(&path_expr.path()?.to_string())
47 } else {
48 name_generator.for_variable(&receiver, &ctx.sema)
49 };
50
51 let (assist_id, message, text) = if name_ref.text() == "is_some" {
52 ("replace_is_some_with_if_let_some", "Replace `is_some` with `let Some`", "Some")
53 } else {
54 ("replace_is_ok_with_if_let_ok", "Replace `is_ok` with `let Ok`", "Ok")
55 };
56
57 acc.add(
58 AssistId::refactor_rewrite(assist_id),
59 message,
60 call_expr.syntax().text_range(),
61 |edit| {
62 let make = SyntaxFactory::with_mappings();
63 let mut editor = edit.make_editor(call_expr.syntax());
64
65 let var_pat = make.ident_pat(false, false, make.name(&var_name));
66 let pat = make.tuple_struct_pat(make.ident_path(text), [var_pat.into()]);
67 let let_expr = make.expr_let(pat.into(), receiver);
68
69 if let Some(cap) = ctx.config.snippet_cap
70 && let Some(ast::Pat::TupleStructPat(pat)) = let_expr.pat()
71 && let Some(first_var) = pat.fields().next()
72 {
73 let placeholder = edit.make_placeholder_snippet(cap);
74 editor.add_annotation(first_var.syntax(), placeholder);
75 }
76
77 editor.replace(call_expr.syntax(), let_expr.syntax());
78 editor.add_mappings(make.finish_with_mappings());
79 edit.add_file_edits(ctx.vfs_file_id(), editor);
80 },
81 )
82 }
83 _ => None,
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use crate::tests::{check_assist, check_assist_not_applicable};
90
91 use super::replace_is_method_with_if_let_method;
92
93 #[test]
94 fn replace_is_some_with_if_let_some_works() {
95 check_assist(
96 replace_is_method_with_if_let_method,
97 r#"
98fn main() {
99 let x = Some(1);
100 if x.is_som$0e() {}
101}
102"#,
103 r#"
104fn main() {
105 let x = Some(1);
106 if let Some(${0:x1}) = x {}
107}
108"#,
109 );
110
111 check_assist(
112 replace_is_method_with_if_let_method,
113 r#"
114fn test() -> Option<i32> {
115 Some(1)
116}
117fn main() {
118 if test().is_som$0e() {}
119}
120"#,
121 r#"
122fn test() -> Option<i32> {
123 Some(1)
124}
125fn main() {
126 if let Some(${0:test}) = test() {}
127}
128"#,
129 );
130 }
131
132 #[test]
133 fn replace_is_some_with_if_let_some_not_applicable() {
134 check_assist_not_applicable(
135 replace_is_method_with_if_let_method,
136 r#"
137fn main() {
138 let x = Some(1);
139 if x.is_non$0e() {}
140}
141"#,
142 );
143 }
144
145 #[test]
146 fn replace_is_ok_with_if_let_ok_works() {
147 check_assist(
148 replace_is_method_with_if_let_method,
149 r#"
150fn main() {
151 let x = Ok(1);
152 if x.is_o$0k() {}
153}
154"#,
155 r#"
156fn main() {
157 let x = Ok(1);
158 if let Ok(${0:x1}) = x {}
159}
160"#,
161 );
162
163 check_assist(
164 replace_is_method_with_if_let_method,
165 r#"
166fn test() -> Result<i32> {
167 Ok(1)
168}
169fn main() {
170 if test().is_o$0k() {}
171}
172"#,
173 r#"
174fn test() -> Result<i32> {
175 Ok(1)
176}
177fn main() {
178 if let Ok(${0:test}) = test() {}
179}
180"#,
181 );
182 }
183
184 #[test]
185 fn replace_is_ok_with_if_let_ok_not_applicable() {
186 check_assist_not_applicable(
187 replace_is_method_with_if_let_method,
188 r#"
189fn main() {
190 let x = Ok(1);
191 if x.is_e$0rr() {}
192}
193"#,
194 );
195 }
196
197 #[test]
198 fn replace_is_some_with_if_let_some_in_let_chain() {
199 check_assist(
200 replace_is_method_with_if_let_method,
201 r#"
202fn main() {
203 let x = Some(1);
204 let cond = true;
205 if cond && x.is_som$0e() {}
206}
207"#,
208 r#"
209fn main() {
210 let x = Some(1);
211 let cond = true;
212 if cond && let Some(${0:x1}) = x {}
213}
214"#,
215 );
216
217 check_assist(
218 replace_is_method_with_if_let_method,
219 r#"
220fn main() {
221 let x = Some(1);
222 let cond = true;
223 if x.is_som$0e() && cond {}
224}
225"#,
226 r#"
227fn main() {
228 let x = Some(1);
229 let cond = true;
230 if let Some(${0:x1}) = x && cond {}
231}
232"#,
233 );
234
235 check_assist(
236 replace_is_method_with_if_let_method,
237 r#"
238fn main() {
239 let x = Some(1);
240 let cond = true;
241 if cond && x.is_som$0e() && cond {}
242}
243"#,
244 r#"
245fn main() {
246 let x = Some(1);
247 let cond = true;
248 if cond && let Some(${0:x1}) = x && cond {}
249}
250"#,
251 );
252 }
253
254 #[test]
255 fn replace_is_some_with_while_let_some() {
256 check_assist(
257 replace_is_method_with_if_let_method,
258 r#"
259fn main() {
260 let mut x = Some(1);
261 while x.is_som$0e() { x = None }
262}
263"#,
264 r#"
265fn main() {
266 let mut x = Some(1);
267 while let Some(${0:x1}) = x { x = None }
268}
269"#,
270 );
271 }
272
273 #[test]
274 fn replace_is_some_with_if_let_some_not_applicable_after_l_curly() {
275 check_assist_not_applicable(
276 replace_is_method_with_if_let_method,
277 r#"
278fn main() {
279 let x = Some(1);
280 if x.is_some() {
281 ()$0
282 }
283}
284"#,
285 );
286 }
287}