Skip to content

Commit 7a870ae

Browse files
committed
Auto merge of #10432 - samueltardieu:issue-10430, r=Manishearth
New lint: detect `if` expressions with simple boolean assignments to the same target Closes #10430 changelog: [`needless_bool_assign`] new lint to detect simple boolean assignment to the same target in `if` branches
2 parents 797a7fe + 69da902 commit 7a870ae

File tree

6 files changed

+201
-3
lines changed

6 files changed

+201
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4864,6 +4864,7 @@ Released 2018-09-13
48644864
[`needless_arbitrary_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_arbitrary_self_type
48654865
[`needless_bitwise_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bitwise_bool
48664866
[`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool
4867+
[`needless_bool_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool_assign
48674868
[`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
48684869
[`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference
48694870
[`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
446446
crate::needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE_INFO,
447447
crate::needless_bool::BOOL_COMPARISON_INFO,
448448
crate::needless_bool::NEEDLESS_BOOL_INFO,
449+
crate::needless_bool::NEEDLESS_BOOL_ASSIGN_INFO,
449450
crate::needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE_INFO,
450451
crate::needless_continue::NEEDLESS_CONTINUE_INFO,
451452
crate::needless_for_each::NEEDLESS_FOR_EACH_INFO,

clippy_lints/src/needless_bool.rs

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
//! This lint is **warn** by default
44
55
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
6-
use clippy_utils::higher;
76
use clippy_utils::source::snippet_with_applicability;
87
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};
1012
use rustc_ast::ast::LitKind;
1113
use rustc_errors::Applicability;
1214
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Node, UnOp};
@@ -77,7 +79,39 @@ declare_clippy_lint! {
7779
"comparing a variable to a boolean, e.g., `if x == true` or `if x != true`"
7880
}
7981

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]);
81115

82116
fn condition_needs_parentheses(e: &Expr<'_>) -> bool {
83117
let mut inner = e;
@@ -173,6 +207,29 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
173207
_ => (),
174208
}
175209
}
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+
}
176233
}
177234
}
178235
}
@@ -376,3 +433,11 @@ fn fetch_bool_expr(expr: &Expr<'_>) -> Option<bool> {
376433
}
377434
None
378435
}
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+
}

tests/ui/needless_bool_assign.fixed

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//@run-rustfix
2+
3+
#![allow(unused)]
4+
#![warn(clippy::needless_bool_assign)]
5+
6+
fn random() -> bool {
7+
true
8+
}
9+
10+
fn main() {
11+
struct Data {
12+
field: bool,
13+
};
14+
let mut a = Data { field: false };
15+
a.field = random() && random();
16+
a.field = !(random() && random());
17+
// Do not lint…
18+
if random() {
19+
a.field = false;
20+
} else {
21+
// …to avoid losing this comment
22+
a.field = true
23+
}
24+
// This one also triggers lint `clippy::if_same_then_else`
25+
// which does not suggest a rewrite.
26+
random(); a.field = true;
27+
let mut b = false;
28+
if random() {
29+
a.field = false;
30+
} else {
31+
b = true;
32+
}
33+
}

tests/ui/needless_bool_assign.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//@run-rustfix
2+
3+
#![allow(unused)]
4+
#![warn(clippy::needless_bool_assign)]
5+
6+
fn random() -> bool {
7+
true
8+
}
9+
10+
fn main() {
11+
struct Data {
12+
field: bool,
13+
};
14+
let mut a = Data { field: false };
15+
if random() && random() {
16+
a.field = true;
17+
} else {
18+
a.field = false
19+
}
20+
if random() && random() {
21+
a.field = false;
22+
} else {
23+
a.field = true
24+
}
25+
// Do not lint…
26+
if random() {
27+
a.field = false;
28+
} else {
29+
// …to avoid losing this comment
30+
a.field = true
31+
}
32+
// This one also triggers lint `clippy::if_same_then_else`
33+
// which does not suggest a rewrite.
34+
if random() {
35+
a.field = true;
36+
} else {
37+
a.field = true;
38+
}
39+
let mut b = false;
40+
if random() {
41+
a.field = false;
42+
} else {
43+
b = true;
44+
}
45+
}

tests/ui/needless_bool_assign.stderr

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
error: this if-then-else expression assigns a bool literal
2+
--> $DIR/needless_bool_assign.rs:15:5
3+
|
4+
LL | / if random() && random() {
5+
LL | | a.field = true;
6+
LL | | } else {
7+
LL | | a.field = false
8+
LL | | }
9+
| |_____^ help: you can reduce it to: `a.field = random() && random();`
10+
|
11+
= note: `-D clippy::needless-bool-assign` implied by `-D warnings`
12+
13+
error: this if-then-else expression assigns a bool literal
14+
--> $DIR/needless_bool_assign.rs:20:5
15+
|
16+
LL | / if random() && random() {
17+
LL | | a.field = false;
18+
LL | | } else {
19+
LL | | a.field = true
20+
LL | | }
21+
| |_____^ help: you can reduce it to: `a.field = !(random() && random());`
22+
23+
error: this if-then-else expression assigns a bool literal
24+
--> $DIR/needless_bool_assign.rs:34:5
25+
|
26+
LL | / if random() {
27+
LL | | a.field = true;
28+
LL | | } else {
29+
LL | | a.field = true;
30+
LL | | }
31+
| |_____^ help: you can reduce it to: `random(); a.field = true;`
32+
33+
error: this `if` has identical blocks
34+
--> $DIR/needless_bool_assign.rs:34:17
35+
|
36+
LL | if random() {
37+
| _________________^
38+
LL | | a.field = true;
39+
LL | | } else {
40+
| |_____^
41+
|
42+
note: same as this
43+
--> $DIR/needless_bool_assign.rs:36:12
44+
|
45+
LL | } else {
46+
| ____________^
47+
LL | | a.field = true;
48+
LL | | }
49+
| |_____^
50+
= note: `#[deny(clippy::if_same_then_else)]` on by default
51+
52+
error: aborting due to 4 previous errors
53+

0 commit comments

Comments
 (0)