Skip to content

Commit 77a7856

Browse files
committed
lints: constant_bool_expr: add support for rudimentary const eval
1 parent ed984b9 commit 77a7856

4 files changed

Lines changed: 93 additions & 7 deletions

File tree

clippy_lints/src/operators/constant_bool_expr.rs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use clippy_utils::consts::{ConstEvalCtxt, Constant};
12
use clippy_utils::diagnostics::span_lint;
23
use clippy_utils::eq_expr_value;
34
use rustc_hir::{BinOpKind, Expr, ExprKind};
@@ -68,11 +69,27 @@ fn detect_variants<'tcx>(
6869
) -> bool {
6970
let ctxt = e.span.ctxt();
7071

71-
let detect_variant = |left_a, right_a, left_literal: &Expr<'_>, right_literal: &Expr<'_>| {
72-
matches!(left_literal.kind, ExprKind::Lit(_))
73-
&& matches!(right_literal.kind, ExprKind::Lit(_))
74-
&& !eq_expr_value(cx, ctxt, left_literal, right_literal)
75-
&& eq_expr_value(cx, ctxt, left_a, right_a)
72+
let detect_variant = |left_a, right_a, left_cmp: &Expr<'_>, right_cmp: &Expr<'_>| {
73+
// If the `a` expression is not the same on both sides, there is no need to go further
74+
if !eq_expr_value(cx, ctxt, left_a, right_a) {
75+
return false;
76+
}
77+
78+
let const_ctx = ConstEvalCtxt::new(cx);
79+
80+
// We try to look into constants and const blocks, but if that fails we fall back to handling
81+
// literals only
82+
if let Some(left_evaluated) = const_ctx.eval(left_cmp)
83+
&& let Some(right_evaluated) = const_ctx.eval(right_cmp)
84+
&& left_evaluated != Constant::Err
85+
&& right_evaluated != Constant::Err
86+
{
87+
left_evaluated != right_evaluated
88+
} else {
89+
matches!(left_cmp.kind, ExprKind::Lit(_))
90+
&& matches!(right_cmp.kind, ExprKind::Lit(_))
91+
&& !eq_expr_value(cx, ctxt, left_cmp, right_cmp)
92+
}
7693
};
7794

7895
// (a CMP_OP _) BIN_OP (a CMP_OP _)

clippy_lints/src/operators/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,8 @@ declare_clippy_lint! {
215215
/// This is most likely a logic bug.
216216
///
217217
/// ### Limitations
218-
/// This lint only checks against literals.
218+
/// This lint only checks against literals and constant expressions since it cannot determine
219+
/// if `a == bar() && a == foo()` will actually be different or not at runtime.
219220
///
220221
/// ### Example
221222
/// ```no_run

tests/ui/constant_bool_expr.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,25 @@ fn main() {
1717
const A: i32 = 100;
1818

1919
let _b = a != A || a != 101;
20+
//~^ constant_bool_expr
2021
let _b = a == A && a == 101;
22+
//~^ constant_bool_expr
23+
24+
let _b = a != A || a != const { 100 + 1 };
25+
//~^ constant_bool_expr
26+
let _b = a == A && a == const { 100 + 1 };
27+
//~^ constant_bool_expr
2128
}
29+
30+
const B: i32 = 100;
31+
const C: i32 = 99;
32+
33+
const _: bool = C != B || C != const { 100 + 1 };
34+
//~^ constant_bool_expr
35+
const _: bool = C == B && C == const { 100 + 1 };
36+
//~^ constant_bool_expr
37+
38+
static D: bool = C != B || C != const { 100 + 1 };
39+
//~^ constant_bool_expr
40+
static E: bool = C == B && C == const { 100 + 1 };
41+
//~^ constant_bool_expr

tests/ui/constant_bool_expr.stderr

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,53 @@ error: expression always evaluates to `false`
2525
LL | let _b = a == 100 && a == 101;
2626
| ^^^^^^^^^^^^^^^^^^^^
2727

28-
error: aborting due to 4 previous errors
28+
error: expression always evaluates to `true`
29+
--> tests/ui/constant_bool_expr.rs:19:14
30+
|
31+
LL | let _b = a != A || a != 101;
32+
| ^^^^^^^^^^^^^^^^^^
33+
34+
error: expression always evaluates to `false`
35+
--> tests/ui/constant_bool_expr.rs:21:14
36+
|
37+
LL | let _b = a == A && a == 101;
38+
| ^^^^^^^^^^^^^^^^^^
39+
40+
error: expression always evaluates to `true`
41+
--> tests/ui/constant_bool_expr.rs:24:14
42+
|
43+
LL | let _b = a != A || a != const { 100 + 1 };
44+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
45+
46+
error: expression always evaluates to `false`
47+
--> tests/ui/constant_bool_expr.rs:26:14
48+
|
49+
LL | let _b = a == A && a == const { 100 + 1 };
50+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
51+
52+
error: expression always evaluates to `true`
53+
--> tests/ui/constant_bool_expr.rs:33:17
54+
|
55+
LL | const _: bool = C != B || C != const { 100 + 1 };
56+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
57+
58+
error: expression always evaluates to `false`
59+
--> tests/ui/constant_bool_expr.rs:35:17
60+
|
61+
LL | const _: bool = C == B && C == const { 100 + 1 };
62+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
63+
64+
error: expression always evaluates to `true`
65+
--> tests/ui/constant_bool_expr.rs:38:18
66+
|
67+
LL | static D: bool = C != B || C != const { 100 + 1 };
68+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
69+
70+
error: expression always evaluates to `false`
71+
--> tests/ui/constant_bool_expr.rs:40:18
72+
|
73+
LL | static E: bool = C == B && C == const { 100 + 1 };
74+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
75+
76+
error: aborting due to 12 previous errors
2977

0 commit comments

Comments
 (0)