Skip to content

Commit 176778f

Browse files
committed
Auto merge of #3556 - lucasloisp:bool-ord-comparison, r=oli-obk
Implements lint for order comparisons against bool (#3438) As described on issue #3438, this change implements linting for `>` and `<` comparisons against both `boolean` literals and between expressions.
2 parents a637d55 + de42dfb commit 176778f

File tree

4 files changed

+153
-25
lines changed

4 files changed

+153
-25
lines changed

clippy_lints/src/needless_bool.rs

+80-24
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,9 @@ declare_clippy_lint! {
4545
"if-statements with plain booleans in the then- and else-clause, e.g. `if p { true } else { false }`"
4646
}
4747

48-
/// **What it does:** Checks for expressions of the form `x == true` and
49-
/// `x != true` (or vice versa) and suggest using the variable directly.
48+
/// **What it does:** Checks for expressions of the form `x == true`,
49+
/// `x != true` and order comparisons such as `x < true` (or vice versa) and
50+
/// suggest using the variable directly.
5051
///
5152
/// **Why is this bad?** Unnecessary code.
5253
///
@@ -143,22 +144,54 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoolComparison {
143144
}
144145

145146
if let ExprKind::Binary(Spanned { node, .. }, ..) = e.node {
147+
let ignore_case = None::<(fn(_) -> _, &str)>;
148+
let ignore_no_literal = None::<(fn(_, _) -> _, &str)>;
146149
match node {
147-
BinOpKind::Eq => check_comparison(
150+
BinOpKind::Eq => {
151+
let true_case = Some((|h| h, "equality checks against true are unnecessary"));
152+
let false_case = Some((
153+
|h: Sugg<'_>| !h,
154+
"equality checks against false can be replaced by a negation",
155+
));
156+
check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal)
157+
},
158+
BinOpKind::Ne => {
159+
let true_case = Some((
160+
|h: Sugg<'_>| !h,
161+
"inequality checks against true can be replaced by a negation",
162+
));
163+
let false_case = Some((|h| h, "inequality checks against false are unnecessary"));
164+
check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal)
165+
},
166+
BinOpKind::Lt => check_comparison(
148167
cx,
149168
e,
150-
"equality checks against true are unnecessary",
151-
"equality checks against false can be replaced by a negation",
152-
|h| h,
153-
|h| !h,
169+
ignore_case,
170+
Some((|h| h, "greater than checks against false are unnecessary")),
171+
Some((
172+
|h: Sugg<'_>| !h,
173+
"less than comparison against true can be replaced by a negation",
174+
)),
175+
ignore_case,
176+
Some((
177+
|l: Sugg<'_>, r: Sugg<'_>| (!l).bit_and(&r),
178+
"order comparisons between booleans can be simplified",
179+
)),
154180
),
155-
BinOpKind::Ne => check_comparison(
181+
BinOpKind::Gt => check_comparison(
156182
cx,
157183
e,
158-
"inequality checks against true can be replaced by a negation",
159-
"inequality checks against false are unnecessary",
160-
|h| !h,
161-
|h| h,
184+
Some((
185+
|h: Sugg<'_>| !h,
186+
"less than comparison against true can be replaced by a negation",
187+
)),
188+
ignore_case,
189+
ignore_case,
190+
Some((|h| h, "greater than checks against false are unnecessary")),
191+
Some((
192+
|l: Sugg<'_>, r: Sugg<'_>| l.bit_and(&(!r)),
193+
"order comparisons between booleans can be simplified",
194+
)),
162195
),
163196
_ => (),
164197
}
@@ -169,22 +202,45 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoolComparison {
169202
fn check_comparison<'a, 'tcx>(
170203
cx: &LateContext<'a, 'tcx>,
171204
e: &'tcx Expr,
172-
true_message: &str,
173-
false_message: &str,
174-
true_hint: impl FnOnce(Sugg<'_>) -> Sugg<'_>,
175-
false_hint: impl FnOnce(Sugg<'_>) -> Sugg<'_>,
205+
left_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
206+
left_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
207+
right_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
208+
right_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
209+
no_literal: Option<(impl FnOnce(Sugg<'a>, Sugg<'a>) -> Sugg<'a>, &str)>,
176210
) {
177211
use self::Expression::*;
178212

179213
if let ExprKind::Binary(_, ref left_side, ref right_side) = e.node {
180-
let applicability = Applicability::MachineApplicable;
214+
let mut applicability = Applicability::MachineApplicable;
181215
match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) {
182-
(Bool(true), Other) => suggest_bool_comparison(cx, e, right_side, applicability, true_message, true_hint),
183-
(Other, Bool(true)) => suggest_bool_comparison(cx, e, left_side, applicability, true_message, true_hint),
184-
(Bool(false), Other) => {
185-
suggest_bool_comparison(cx, e, right_side, applicability, false_message, false_hint)
186-
},
187-
(Other, Bool(false)) => suggest_bool_comparison(cx, e, left_side, applicability, false_message, false_hint),
216+
(Bool(true), Other) => left_true.map_or((), |(h, m)| {
217+
suggest_bool_comparison(cx, e, right_side, applicability, m, h)
218+
}),
219+
(Other, Bool(true)) => right_true.map_or((), |(h, m)| {
220+
suggest_bool_comparison(cx, e, left_side, applicability, m, h)
221+
}),
222+
(Bool(false), Other) => left_false.map_or((), |(h, m)| {
223+
suggest_bool_comparison(cx, e, right_side, applicability, m, h)
224+
}),
225+
(Other, Bool(false)) => right_false.map_or((), |(h, m)| {
226+
suggest_bool_comparison(cx, e, left_side, applicability, m, h)
227+
}),
228+
(Other, Other) => no_literal.map_or((), |(h, m)| {
229+
let (l_ty, r_ty) = (cx.tables.expr_ty(left_side), cx.tables.expr_ty(right_side));
230+
if l_ty.is_bool() && r_ty.is_bool() {
231+
let left_side = Sugg::hir_with_applicability(cx, left_side, "..", &mut applicability);
232+
let right_side = Sugg::hir_with_applicability(cx, right_side, "..", &mut applicability);
233+
span_lint_and_sugg(
234+
cx,
235+
BOOL_COMPARISON,
236+
e.span,
237+
m,
238+
"try simplifying it as shown",
239+
h(left_side, right_side).to_string(),
240+
applicability,
241+
)
242+
}
243+
}),
188244
_ => (),
189245
}
190246
}
@@ -196,7 +252,7 @@ fn suggest_bool_comparison<'a, 'tcx>(
196252
expr: &Expr,
197253
mut applicability: Applicability,
198254
message: &str,
199-
conv_hint: impl FnOnce(Sugg<'_>) -> Sugg<'_>,
255+
conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>,
200256
) {
201257
let hint = Sugg::hir_with_applicability(cx, expr, "..", &mut applicability);
202258
span_lint_and_sugg(

clippy_lints/src/utils/sugg.rs

+5
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,11 @@ impl<'a> Sugg<'a> {
174174
make_binop(ast::BinOpKind::And, &self, rhs)
175175
}
176176

177+
/// Convenience method to create the `<lhs> & <rhs>` suggestion.
178+
pub fn bit_and(self, rhs: &Self) -> Sugg<'static> {
179+
make_binop(ast::BinOpKind::BitAnd, &self, rhs)
180+
}
181+
177182
/// Convenience method to create the `<lhs> as <rhs>` suggestion.
178183
pub fn as_ty<R: Display>(self, rhs: R) -> Sugg<'static> {
179184
make_assoc(AssocOp::As, &self, &Sugg::NonParen(rhs.to_string().into()))

tests/ui/bool_comparison.rs

+31
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,35 @@ fn main() {
5050
} else {
5151
"no"
5252
};
53+
if x < true {
54+
"yes"
55+
} else {
56+
"no"
57+
};
58+
if false < x {
59+
"yes"
60+
} else {
61+
"no"
62+
};
63+
if x > false {
64+
"yes"
65+
} else {
66+
"no"
67+
};
68+
if true > x {
69+
"yes"
70+
} else {
71+
"no"
72+
};
73+
let y = true;
74+
if x < y {
75+
"yes"
76+
} else {
77+
"no"
78+
};
79+
if x > y {
80+
"yes"
81+
} else {
82+
"no"
83+
};
5384
}

tests/ui/bool_comparison.stderr

+37-1
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,41 @@ error: inequality checks against false are unnecessary
4848
48 | if false != x {
4949
| ^^^^^^^^^^ help: try simplifying it as shown: `x`
5050

51-
error: aborting due to 8 previous errors
51+
error: less than comparison against true can be replaced by a negation
52+
--> $DIR/bool_comparison.rs:53:8
53+
|
54+
53 | if x < true {
55+
| ^^^^^^^^ help: try simplifying it as shown: `!x`
56+
57+
error: greater than checks against false are unnecessary
58+
--> $DIR/bool_comparison.rs:58:8
59+
|
60+
58 | if false < x {
61+
| ^^^^^^^^^ help: try simplifying it as shown: `x`
62+
63+
error: greater than checks against false are unnecessary
64+
--> $DIR/bool_comparison.rs:63:8
65+
|
66+
63 | if x > false {
67+
| ^^^^^^^^^ help: try simplifying it as shown: `x`
68+
69+
error: less than comparison against true can be replaced by a negation
70+
--> $DIR/bool_comparison.rs:68:8
71+
|
72+
68 | if true > x {
73+
| ^^^^^^^^ help: try simplifying it as shown: `!x`
74+
75+
error: order comparisons between booleans can be simplified
76+
--> $DIR/bool_comparison.rs:74:8
77+
|
78+
74 | if x < y {
79+
| ^^^^^ help: try simplifying it as shown: `!x & y`
80+
81+
error: order comparisons between booleans can be simplified
82+
--> $DIR/bool_comparison.rs:79:8
83+
|
84+
79 | if x > y {
85+
| ^^^^^ help: try simplifying it as shown: `x & !y`
86+
87+
error: aborting due to 14 previous errors
5288

0 commit comments

Comments
 (0)