|
3 | 3 | //! This lint is **warn** by default |
4 | 4 |
|
5 | 5 | use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; |
6 | | -use clippy_utils::higher; |
7 | 6 | use clippy_utils::source::snippet_with_applicability; |
8 | 7 | use clippy_utils::sugg::Sugg; |
9 | | -use clippy_utils::{get_parent_node, is_else_clause, is_expn_of, peel_blocks, peel_blocks_with_stmt}; |
| 8 | +use clippy_utils::{ |
| 9 | + get_parent_node, is_else_clause, is_expn_of, peel_blocks, peel_blocks_with_stmt, span_extract_comment, |
| 10 | +}; |
| 11 | +use clippy_utils::{higher, SpanlessEq}; |
10 | 12 | use rustc_ast::ast::LitKind; |
11 | 13 | use rustc_errors::Applicability; |
12 | 14 | use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Node, UnOp}; |
@@ -77,7 +79,39 @@ declare_clippy_lint! { |
77 | 79 | "comparing a variable to a boolean, e.g., `if x == true` or `if x != true`" |
78 | 80 | } |
79 | 81 |
|
80 | | -declare_lint_pass!(NeedlessBool => [NEEDLESS_BOOL]); |
| 82 | +declare_clippy_lint! { |
| 83 | + /// ### What it does |
| 84 | + /// Checks for expressions of the form `if c { x = true } else { x = false }` |
| 85 | + /// (or vice versa) and suggest assigning the variable directly from the |
| 86 | + /// condition. |
| 87 | + /// |
| 88 | + /// ### Why is this bad? |
| 89 | + /// Redundant code. |
| 90 | + /// |
| 91 | + /// ### Example |
| 92 | + /// ```rust,ignore |
| 93 | + /// # fn must_keep(x: i32, y: i32) -> bool { x == y } |
| 94 | + /// # let x = 32; let y = 10; |
| 95 | + /// # let mut skip: bool; |
| 96 | + /// if must_keep(x, y) { |
| 97 | + /// skip = false; |
| 98 | + /// } else { |
| 99 | + /// skip = true; |
| 100 | + /// } |
| 101 | + /// ``` |
| 102 | + /// Use instead: |
| 103 | + /// ```rust,ignore |
| 104 | + /// # fn must_keep(x: i32, y: i32) -> bool { x == y } |
| 105 | + /// # let x = 32; let y = 10; |
| 106 | + /// # let mut skip: bool; |
| 107 | + /// skip = !must_keep(x, y); |
| 108 | + /// ``` |
| 109 | + #[clippy::version = "1.69.0"] |
| 110 | + pub NEEDLESS_BOOL_ASSIGN, |
| 111 | + complexity, |
| 112 | + "setting the same boolean variable in both branches of an if-statement" |
| 113 | +} |
| 114 | +declare_lint_pass!(NeedlessBool => [NEEDLESS_BOOL, NEEDLESS_BOOL_ASSIGN]); |
81 | 115 |
|
82 | 116 | fn condition_needs_parentheses(e: &Expr<'_>) -> bool { |
83 | 117 | let mut inner = e; |
@@ -173,6 +207,29 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { |
173 | 207 | _ => (), |
174 | 208 | } |
175 | 209 | } |
| 210 | + if let Some((lhs_a, a)) = fetch_assign(then) && |
| 211 | + let Some((lhs_b, b)) = fetch_assign(r#else) && |
| 212 | + SpanlessEq::new(cx).eq_expr(lhs_a, lhs_b) && |
| 213 | + span_extract_comment(cx.tcx.sess.source_map(), e.span).is_empty() |
| 214 | + { |
| 215 | + let mut applicability = Applicability::MachineApplicable; |
| 216 | + let cond = Sugg::hir_with_applicability(cx, cond, "..", &mut applicability); |
| 217 | + let lhs = snippet_with_applicability(cx, lhs_a.span, "..", &mut applicability); |
| 218 | + let sugg = if a == b { |
| 219 | + format!("{cond}; {lhs} = {a:?};") |
| 220 | + } else { |
| 221 | + format!("{lhs} = {};", if a { cond } else { !cond }) |
| 222 | + }; |
| 223 | + span_lint_and_sugg( |
| 224 | + cx, |
| 225 | + NEEDLESS_BOOL_ASSIGN, |
| 226 | + e.span, |
| 227 | + "this if-then-else expression assigns a bool literal", |
| 228 | + "you can reduce it to", |
| 229 | + sugg, |
| 230 | + applicability |
| 231 | + ); |
| 232 | + } |
176 | 233 | } |
177 | 234 | } |
178 | 235 | } |
@@ -376,3 +433,11 @@ fn fetch_bool_expr(expr: &Expr<'_>) -> Option<bool> { |
376 | 433 | } |
377 | 434 | None |
378 | 435 | } |
| 436 | + |
| 437 | +fn fetch_assign<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, bool)> { |
| 438 | + if let ExprKind::Assign(lhs, rhs, _) = peel_blocks_with_stmt(expr).kind { |
| 439 | + fetch_bool_expr(rhs).map(|b| (lhs, b)) |
| 440 | + } else { |
| 441 | + None |
| 442 | + } |
| 443 | +} |
0 commit comments