]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/redundant_else.rs
Auto merge of #82864 - jyn514:short-circuit, r=GuillaumeGomez
[rust.git] / src / tools / clippy / clippy_lints / src / redundant_else.rs
1 use clippy_utils::diagnostics::span_lint_and_help;
2 use rustc_ast::ast::{Block, Expr, ExprKind, Stmt, StmtKind};
3 use rustc_ast::visit::{walk_expr, Visitor};
4 use rustc_lint::{EarlyContext, EarlyLintPass};
5 use rustc_middle::lint::in_external_macro;
6 use rustc_session::{declare_lint_pass, declare_tool_lint};
7
8 declare_clippy_lint! {
9     /// **What it does:** Checks for `else` blocks that can be removed without changing semantics.
10     ///
11     /// **Why is this bad?** The `else` block adds unnecessary indentation and verbosity.
12     ///
13     /// **Known problems:** Some may prefer to keep the `else` block for clarity.
14     ///
15     /// **Example:**
16     ///
17     /// ```rust
18     /// fn my_func(count: u32) {
19     ///     if count == 0 {
20     ///         print!("Nothing to do");
21     ///         return;
22     ///     } else {
23     ///         print!("Moving on...");
24     ///     }
25     /// }
26     /// ```
27     /// Use instead:
28     /// ```rust
29     /// fn my_func(count: u32) {
30     ///     if count == 0 {
31     ///         print!("Nothing to do");
32     ///         return;
33     ///     }
34     ///     print!("Moving on...");
35     /// }
36     /// ```
37     pub REDUNDANT_ELSE,
38     pedantic,
39     "`else` branch that can be removed without changing semantics"
40 }
41
42 declare_lint_pass!(RedundantElse => [REDUNDANT_ELSE]);
43
44 impl EarlyLintPass for RedundantElse {
45     fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &Stmt) {
46         if in_external_macro(cx.sess, stmt.span) {
47             return;
48         }
49         // Only look at expressions that are a whole statement
50         let expr: &Expr = match &stmt.kind {
51             StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr,
52             _ => return,
53         };
54         // if else
55         let (mut then, mut els): (&Block, &Expr) = match &expr.kind {
56             ExprKind::If(_, then, Some(els)) => (then, els),
57             _ => return,
58         };
59         loop {
60             if !BreakVisitor::default().check_block(then) {
61                 // then block does not always break
62                 return;
63             }
64             match &els.kind {
65                 // else if else
66                 ExprKind::If(_, next_then, Some(next_els)) => {
67                     then = next_then;
68                     els = next_els;
69                     continue;
70                 },
71                 // else if without else
72                 ExprKind::If(..) => return,
73                 // done
74                 _ => break,
75             }
76         }
77         span_lint_and_help(
78             cx,
79             REDUNDANT_ELSE,
80             els.span,
81             "redundant else block",
82             None,
83             "remove the `else` block and move the contents out",
84         );
85     }
86 }
87
88 /// Call `check` functions to check if an expression always breaks control flow
89 #[derive(Default)]
90 struct BreakVisitor {
91     is_break: bool,
92 }
93
94 impl<'ast> Visitor<'ast> for BreakVisitor {
95     fn visit_block(&mut self, block: &'ast Block) {
96         self.is_break = match block.stmts.as_slice() {
97             [.., last] => self.check_stmt(last),
98             _ => false,
99         };
100     }
101
102     fn visit_expr(&mut self, expr: &'ast Expr) {
103         self.is_break = match expr.kind {
104             ExprKind::Break(..) | ExprKind::Continue(..) | ExprKind::Ret(..) => true,
105             ExprKind::Match(_, ref arms) => arms.iter().all(|arm| self.check_expr(&arm.body)),
106             ExprKind::If(_, ref then, Some(ref els)) => self.check_block(then) && self.check_expr(els),
107             ExprKind::If(_, _, None)
108             // ignore loops for simplicity
109             | ExprKind::While(..) | ExprKind::ForLoop(..) | ExprKind::Loop(..) => false,
110             _ => {
111                 walk_expr(self, expr);
112                 return;
113             },
114         };
115     }
116 }
117
118 impl BreakVisitor {
119     fn check<T>(&mut self, item: T, visit: fn(&mut Self, T)) -> bool {
120         visit(self, item);
121         std::mem::replace(&mut self.is_break, false)
122     }
123
124     fn check_block(&mut self, block: &Block) -> bool {
125         self.check(block, Self::visit_block)
126     }
127
128     fn check_expr(&mut self, expr: &Expr) -> bool {
129         self.check(expr, Self::visit_expr)
130     }
131
132     fn check_stmt(&mut self, stmt: &Stmt) -> bool {
133         self.check(stmt, Self::visit_stmt)
134     }
135 }