ide_diagnostics/handlers/
remove_unnecessary_else.rs1use hir::{db::ExpandDatabase, diagnostics::RemoveUnnecessaryElse};
2use ide_db::text_edit::TextEdit;
3use ide_db::{assists::Assist, source_change::SourceChange};
4use itertools::Itertools;
5use syntax::{
6 AstNode, SyntaxToken, TextRange,
7 ast::{
8 self,
9 edit::{AstNodeEdit, IndentLevel},
10 },
11};
12
13use crate::{
14 Diagnostic, DiagnosticCode, DiagnosticsContext, Severity, adjusted_display_range, fix,
15};
16
17pub(crate) fn remove_unnecessary_else(
22 ctx: &DiagnosticsContext<'_>,
23 d: &RemoveUnnecessaryElse,
24) -> Option<Diagnostic> {
25 if d.if_expr.file_id.macro_file().is_some() {
26 return None;
28 }
29
30 let display_range = adjusted_display_range(ctx, d.if_expr, &|if_expr| {
31 if_expr.else_token().as_ref().map(SyntaxToken::text_range)
32 });
33 Some(
34 Diagnostic::new(
35 DiagnosticCode::Ra("remove-unnecessary-else", Severity::WeakWarning),
36 "remove unnecessary else block",
37 display_range,
38 )
39 .with_fixes(fixes(ctx, d)),
40 )
41}
42
43fn fixes(ctx: &DiagnosticsContext<'_>, d: &RemoveUnnecessaryElse) -> Option<Vec<Assist>> {
44 let root = ctx.sema.db.parse_or_expand(d.if_expr.file_id);
45 let if_expr = d.if_expr.value.to_node(&root);
46 let if_expr = ctx.sema.original_ast_node(if_expr)?;
47
48 let mut indent = IndentLevel::from_node(if_expr.syntax());
49 let has_parent_if_expr = if_expr.syntax().parent().and_then(ast::IfExpr::cast).is_some();
50 if has_parent_if_expr {
51 indent = indent + 1;
52 }
53 let else_replacement = match if_expr.else_branch()? {
54 ast::ElseBranch::Block(block) => block
55 .statements()
56 .map(|stmt| format!("\n{indent}{stmt}"))
57 .chain(block.tail_expr().map(|tail| format!("\n{indent}{tail}")))
58 .join(""),
59 ast::ElseBranch::IfExpr(mut nested_if_expr) => {
60 if has_parent_if_expr {
61 nested_if_expr = nested_if_expr.indent(IndentLevel(1))
62 }
63 format!("\n{indent}{nested_if_expr}")
64 }
65 };
66 let (replacement, range) = if has_parent_if_expr {
67 let base_indent = IndentLevel::from_node(if_expr.syntax());
68 let then_indent = base_indent + 1;
69 let then_child_indent = then_indent + 1;
70
71 let condition = if_expr.condition()?;
72 let then_stmts = if_expr
73 .then_branch()?
74 .statements()
75 .map(|stmt| format!("\n{then_child_indent}{stmt}"))
76 .join("");
77 let then_replacement =
78 format!("\n{then_indent}if {condition} {{{then_stmts}\n{then_indent}}}",);
79 let replacement = format!("{{{then_replacement}{else_replacement}\n{base_indent}}}");
80 (replacement, if_expr.syntax().text_range())
81 } else {
82 (
83 else_replacement,
84 TextRange::new(
85 if_expr.then_branch()?.syntax().text_range().end(),
86 if_expr.syntax().text_range().end(),
87 ),
88 )
89 };
90
91 let edit = TextEdit::replace(range, replacement);
92 let source_change = SourceChange::from_text_edit(
93 d.if_expr.file_id.original_file(ctx.sema.db).file_id(ctx.sema.db),
94 edit,
95 );
96
97 Some(vec![fix(
98 "remove_unnecessary_else",
99 "Remove unnecessary else block",
100 source_change,
101 range,
102 )])
103}
104
105#[cfg(test)]
106mod tests {
107 use crate::tests::{check_diagnostics_with_disabled, check_fix};
108
109 #[test]
110 fn remove_unnecessary_else_for_return() {
111 check_diagnostics_with_disabled(
112 r#"
113fn test() {
114 if foo {
115 return bar;
116 } else {
117 //^^^^ 💡 weak: remove unnecessary else block
118 do_something_else();
119 }
120}
121"#,
122 &["needless_return", "E0425"],
123 );
124 check_fix(
125 r#"
126fn test() {
127 if foo {
128 return bar;
129 } else$0 {
130 do_something_else();
131 }
132}
133"#,
134 r#"
135fn test() {
136 if foo {
137 return bar;
138 }
139 do_something_else();
140}
141"#,
142 );
143 }
144
145 #[test]
146 fn remove_unnecessary_else_for_return2() {
147 check_diagnostics_with_disabled(
148 r#"
149fn test() {
150 if foo {
151 return bar;
152 } else if qux {
153 //^^^^ 💡 weak: remove unnecessary else block
154 do_something_else();
155 } else {
156 do_something_else2();
157 }
158}
159"#,
160 &["needless_return", "E0425"],
161 );
162 check_fix(
163 r#"
164fn test() {
165 if foo {
166 return bar;
167 } else$0 if qux {
168 do_something_else();
169 } else {
170 do_something_else2();
171 }
172}
173"#,
174 r#"
175fn test() {
176 if foo {
177 return bar;
178 }
179 if qux {
180 do_something_else();
181 } else {
182 do_something_else2();
183 }
184}
185"#,
186 );
187 }
188
189 #[test]
190 fn remove_unnecessary_else_for_return3() {
191 check_diagnostics_with_disabled(
192 r#"
193fn test(a: bool) -> i32 {
194 if a {
195 return 1;
196 } else {
197 //^^^^ 💡 weak: remove unnecessary else block
198 0
199 }
200}
201"#,
202 &["needless_return", "E0425"],
203 );
204 check_fix(
205 r#"
206fn test(a: bool) -> i32 {
207 if a {
208 return 1;
209 } else$0 {
210 0
211 }
212}
213"#,
214 r#"
215fn test(a: bool) -> i32 {
216 if a {
217 return 1;
218 }
219 0
220}
221"#,
222 );
223 }
224
225 #[test]
226 fn remove_unnecessary_else_for_return_in_child_if_expr() {
227 check_diagnostics_with_disabled(
228 r#"
229fn test() {
230 if foo {
231 do_something();
232 } else if qux {
233 return bar;
234 } else {
235 //^^^^ 💡 weak: remove unnecessary else block
236 do_something_else();
237 }
238}
239"#,
240 &["needless_return", "E0425"],
241 );
242 check_fix(
243 r#"
244fn test() {
245 if foo {
246 do_something();
247 } else if qux {
248 return bar;
249 } else$0 {
250 do_something_else();
251 }
252}
253"#,
254 r#"
255fn test() {
256 if foo {
257 do_something();
258 } else {
259 if qux {
260 return bar;
261 }
262 do_something_else();
263 }
264}
265"#,
266 );
267 }
268
269 #[test]
270 fn remove_unnecessary_else_for_return_in_child_if_expr2() {
271 check_fix(
272 r#"
273fn test() {
274 if foo {
275 do_something();
276 } else if qux {
277 return bar;
278 } else$0 if quux {
279 do_something_else();
280 } else {
281 do_something_else2();
282 }
283}
284"#,
285 r#"
286fn test() {
287 if foo {
288 do_something();
289 } else {
290 if qux {
291 return bar;
292 }
293 if quux {
294 do_something_else();
295 } else {
296 do_something_else2();
297 }
298 }
299}
300"#,
301 );
302 }
303
304 #[test]
305 fn remove_unnecessary_else_for_break() {
306 check_diagnostics_with_disabled(
307 r#"
308fn test() {
309 loop {
310 if foo {
311 break;
312 } else {
313 //^^^^ 💡 weak: remove unnecessary else block
314 do_something_else();
315 }
316 }
317}
318"#,
319 &["E0425"],
320 );
321 check_fix(
322 r#"
323fn test() {
324 loop {
325 if foo {
326 break;
327 } else$0 {
328 do_something_else();
329 }
330 }
331}
332"#,
333 r#"
334fn test() {
335 loop {
336 if foo {
337 break;
338 }
339 do_something_else();
340 }
341}
342"#,
343 );
344 }
345
346 #[test]
347 fn remove_unnecessary_else_for_continue() {
348 check_diagnostics_with_disabled(
349 r#"
350fn test() {
351 loop {
352 if foo {
353 continue;
354 } else {
355 //^^^^ 💡 weak: remove unnecessary else block
356 do_something_else();
357 }
358 }
359}
360"#,
361 &["E0425"],
362 );
363 check_fix(
364 r#"
365fn test() {
366 loop {
367 if foo {
368 continue;
369 } else$0 {
370 do_something_else();
371 }
372 }
373}
374"#,
375 r#"
376fn test() {
377 loop {
378 if foo {
379 continue;
380 }
381 do_something_else();
382 }
383}
384"#,
385 );
386 }
387
388 #[test]
389 fn remove_unnecessary_else_for_never() {
390 check_diagnostics_with_disabled(
391 r#"
392fn test() {
393 if foo {
394 never();
395 } else {
396 //^^^^ 💡 weak: remove unnecessary else block
397 do_something_else();
398 }
399}
400
401fn never() -> ! {
402 loop {}
403}
404"#,
405 &["E0425"],
406 );
407 check_fix(
408 r#"
409fn test() {
410 if foo {
411 never();
412 } else$0 {
413 do_something_else();
414 }
415}
416
417fn never() -> ! {
418 loop {}
419}
420"#,
421 r#"
422fn test() {
423 if foo {
424 never();
425 }
426 do_something_else();
427}
428
429fn never() -> ! {
430 loop {}
431}
432"#,
433 );
434 }
435
436 #[test]
437 fn no_diagnostic_if_no_else_branch() {
438 check_diagnostics_with_disabled(
439 r#"
440fn test() {
441 if foo {
442 return bar;
443 }
444
445 do_something_else();
446}
447"#,
448 &["E0425"],
449 );
450 }
451
452 #[test]
453 fn no_diagnostic_if_no_divergence() {
454 check_diagnostics_with_disabled(
455 r#"
456fn test() {
457 if foo {
458 do_something();
459 } else {
460 do_something_else();
461 }
462}
463"#,
464 &["E0425"],
465 );
466 }
467
468 #[test]
469 fn no_diagnostic_if_no_divergence_in_else_branch() {
470 check_diagnostics_with_disabled(
471 r#"
472fn test() {
473 if foo {
474 do_something();
475 } else {
476 return bar;
477 }
478}
479"#,
480 &["needless_return", "E0425"],
481 );
482 }
483
484 #[test]
485 fn no_diagnostic_if_not_expr_stmt() {
486 check_diagnostics_with_disabled(
487 r#"
488fn test1() {
489 let _x = if a {
490 return;
491 } else {
492 1
493 };
494}
495
496fn test2() {
497 let _x = if a {
498 return;
499 } else if b {
500 return;
501 } else if c {
502 1
503 } else {
504 return;
505 };
506}
507"#,
508 &["needless_return", "E0425"],
509 );
510 check_diagnostics_with_disabled(
511 r#"
512fn test3() -> u8 {
513 foo(if a { return 1 } else { 0 })
514}
515"#,
516 &["E0425"],
517 );
518 }
519}