about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-08-06 17:20:07 +0000
committerbors <bors@rust-lang.org>2023-08-06 17:20:07 +0000
commit6a2c8a1e7bfe6cd4823cfd785c49af4afcb98180 (patch)
tree0f3df58038b850b8766c47c9b3c2da2997ebac05
parent5818225a89bfb8645cbf9c10c0d8a10c5b063d71 (diff)
parentd62804624441c2f7bf3de45cd2c9b5c411b2d6a6 (diff)
downloadrust-6a2c8a1e7bfe6cd4823cfd785c49af4afcb98180.tar.gz
rust-6a2c8a1e7bfe6cd4823cfd785c49af4afcb98180.zip
Auto merge of #10843 - MortenLohne:feat/double-const-comparisons, r=Centri3
New lints: `impossible_comparisons` and `redundant_comparisons`

Inspired by a bug we had in production, like all good lints ;)

Adds two lints for "double" comparisons, specifically when the same expression is being compared against two different constants.

`impossible_comparisons` checks for expressions that can never be true at all. Example:
```rust
status_code <= 400 && status_code > 500
```
Presumably, the programmer intended to write `status_code >= 400 && status_code < 500` in this case.

`redundant_comparisons` checks for expressions where either half has no effect. Example:
```rust
status_code <= 400 && status_code < 500
```

Works with other literal types like floats and strings, and *some* cases where the constant is more complex than a literal, see the tests for more.

**Limitations and/or future work:**
* Doesn't work if the LHS can have side-effects at all, for example by being a function call
* Only works for exactly two comparison expressions, so `x > y && x > z && x < w` won't get checked
* Doesn't check for comparison expressions combined with `||`. Very similar logic could be applied there.

changelog: New lints [`impossible_comparisons`] and [`redundant_comparisons`]
-rw-r--r--CHANGELOG.md2
-rw-r--r--clippy_lints/src/declared_lints.rs2
-rw-r--r--clippy_lints/src/operators/const_comparisons.rs207
-rw-r--r--clippy_lints/src/operators/mod.rs43
-rw-r--r--clippy_utils/src/consts.rs2
-rw-r--r--tests/ui/const_comparisons.rs93
-rw-r--r--tests/ui/const_comparisons.stderr228
-rw-r--r--tests/ui/range_contains.fixed2
-rw-r--r--tests/ui/range_contains.rs2
-rw-r--r--tests/ui/range_contains.stderr42
10 files changed, 601 insertions, 22 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3ef22555979..71671273c57 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4897,6 +4897,7 @@ Released 2018-09-13
 [`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return
 [`implicit_saturating_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_add
 [`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub
+[`impossible_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#impossible_comparisons
 [`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops
 [`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping
 [`inconsistent_struct_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor
@@ -5191,6 +5192,7 @@ Released 2018-09-13
 [`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
 [`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call
 [`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls
+[`redundant_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_comparisons
 [`redundant_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else
 [`redundant_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_feature_names
 [`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index ea66c760a2f..db114abfc86 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -518,6 +518,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::operators::FLOAT_CMP_CONST_INFO,
     crate::operators::FLOAT_EQUALITY_WITHOUT_ABS_INFO,
     crate::operators::IDENTITY_OP_INFO,
+    crate::operators::IMPOSSIBLE_COMPARISONS_INFO,
     crate::operators::INEFFECTIVE_BIT_MASK_INFO,
     crate::operators::INTEGER_DIVISION_INFO,
     crate::operators::MISREFACTORED_ASSIGN_OP_INFO,
@@ -526,6 +527,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::operators::NEEDLESS_BITWISE_BOOL_INFO,
     crate::operators::OP_REF_INFO,
     crate::operators::PTR_EQ_INFO,
+    crate::operators::REDUNDANT_COMPARISONS_INFO,
     crate::operators::SELF_ASSIGNMENT_INFO,
     crate::operators::VERBOSE_BIT_MASK_INFO,
     crate::option_env_unwrap::OPTION_ENV_UNWRAP_INFO,
diff --git a/clippy_lints/src/operators/const_comparisons.rs b/clippy_lints/src/operators/const_comparisons.rs
new file mode 100644
index 00000000000..abe8df19543
--- /dev/null
+++ b/clippy_lints/src/operators/const_comparisons.rs
@@ -0,0 +1,207 @@
+#![allow(clippy::match_same_arms)]
+
+use std::cmp::Ordering;
+
+use clippy_utils::consts::{constant, Constant};
+use if_chain::if_chain;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty::layout::HasTyCtxt;
+use rustc_middle::ty::{Ty, TypeckResults};
+use rustc_span::source_map::{Span, Spanned};
+
+use clippy_utils::diagnostics::span_lint_and_note;
+use clippy_utils::source::snippet;
+use clippy_utils::SpanlessEq;
+
+use super::{IMPOSSIBLE_COMPARISONS, REDUNDANT_COMPARISONS};
+
+// Extract a comparison between a const and non-const
+// Flip yoda conditionals, turnings expressions like `42 < x` into `x > 42`
+fn comparison_to_const<'tcx>(
+    cx: &LateContext<'tcx>,
+    typeck: &TypeckResults<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+) -> Option<(CmpOp, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Constant<'tcx>, Ty<'tcx>)> {
+    if_chain! {
+        if let ExprKind::Binary(operator, left, right) = expr.kind;
+        if let Ok(cmp_op) = CmpOp::try_from(operator.node);
+        then {
+            match (constant(cx, typeck, left), constant(cx, typeck, right)) {
+                (Some(_), Some(_)) => None,
+                (_, Some(con)) => Some((cmp_op, left, right, con, typeck.expr_ty(right))),
+                (Some(con), _) => Some((cmp_op.reverse(), right, left, con, typeck.expr_ty(left))),
+                _ => None,
+            }
+        } else {
+            None
+        }
+    }
+}
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    and_op: Spanned<BinOpKind>,
+    left_cond: &'tcx Expr<'tcx>,
+    right_cond: &'tcx Expr<'tcx>,
+    span: Span,
+) {
+    if_chain! {
+        // Ensure that the binary operator is &&
+        if and_op.node == BinOpKind::And;
+
+        // Check that both operands to '&&' are themselves a binary operation
+        // The `comparison_to_const` step also checks this, so this step is just an optimization
+        if let ExprKind::Binary(_, _, _) = left_cond.kind;
+        if let ExprKind::Binary(_, _, _) = right_cond.kind;
+
+        let typeck = cx.typeck_results();
+
+        // Check that both operands to '&&' compare a non-literal to a literal
+        if let Some((left_cmp_op, left_expr, left_const_expr, left_const, left_type)) =
+            comparison_to_const(cx, typeck, left_cond);
+        if let Some((right_cmp_op, right_expr, right_const_expr, right_const, right_type)) =
+            comparison_to_const(cx, typeck, right_cond);
+
+        if left_type == right_type;
+
+        // Check that the same expression is compared in both comparisons
+        if SpanlessEq::new(cx).eq_expr(left_expr, right_expr);
+
+        if !left_expr.can_have_side_effects();
+
+        // Compare the two constant expressions
+        if let Some(ordering) = Constant::partial_cmp(cx.tcx(), left_type, &left_const, &right_const);
+
+        // Rule out the `x >= 42 && x <= 42` corner case immediately
+        // Mostly to simplify the implementation, but it is also covered by `clippy::double_comparisons`
+        if !matches!(
+            (&left_cmp_op, &right_cmp_op, ordering),
+            (CmpOp::Le | CmpOp::Ge, CmpOp::Le | CmpOp::Ge, Ordering::Equal)
+        );
+
+        then {
+            if left_cmp_op.direction() == right_cmp_op.direction() {
+                let lhs_str = snippet(cx, left_cond.span, "<lhs>");
+                let rhs_str = snippet(cx, right_cond.span, "<rhs>");
+                // We already know that either side of `&&` has no effect,
+                // but emit a different error message depending on which side it is
+                if left_side_is_useless(left_cmp_op, ordering) {
+                    span_lint_and_note(
+                        cx,
+                        REDUNDANT_COMPARISONS,
+                        span,
+                        "left-hand side of `&&` operator has no effect",
+                        Some(left_cond.span.until(right_cond.span)),
+                        &format!("`if `{rhs_str}` evaluates to true, {lhs_str}` will always evaluate to true as well"),
+                    );
+                } else {
+                    span_lint_and_note(
+                        cx,
+                        REDUNDANT_COMPARISONS,
+                        span,
+                        "right-hand side of `&&` operator has no effect",
+                        Some(and_op.span.to(right_cond.span)),
+                        &format!("`if `{lhs_str}` evaluates to true, {rhs_str}` will always evaluate to true as well"),
+                    );
+                }
+                // We could autofix this error but choose not to,
+                // because code triggering this lint probably not behaving correctly in the first place
+            }
+            else if !comparison_is_possible(left_cmp_op.direction(), ordering) {
+                let expr_str = snippet(cx, left_expr.span, "..");
+                let lhs_str = snippet(cx, left_const_expr.span, "<lhs>");
+                let rhs_str = snippet(cx, right_const_expr.span, "<rhs>");
+                let note = match ordering {
+                    Ordering::Less => format!("since `{lhs_str}` < `{rhs_str}`, the expression evaluates to false for any value of `{expr_str}`"),
+                    Ordering::Equal => format!("`{expr_str}` cannot simultaneously be greater than and less than `{lhs_str}`"),
+                    Ordering::Greater => format!("since `{lhs_str}` > `{rhs_str}`, the expression evaluates to false for any value of `{expr_str}`"),
+                };
+                span_lint_and_note(
+                    cx,
+                    IMPOSSIBLE_COMPARISONS,
+                    span,
+                    "boolean expression will never evaluate to 'true'",
+                    None,
+                    &note,
+                );
+            };
+        }
+    }
+}
+
+fn left_side_is_useless(left_cmp_op: CmpOp, ordering: Ordering) -> bool {
+    // Special-case for equal constants with an inclusive comparison
+    if ordering == Ordering::Equal {
+        match left_cmp_op {
+            CmpOp::Lt | CmpOp::Gt => false,
+            CmpOp::Le | CmpOp::Ge => true,
+        }
+    } else {
+        match (left_cmp_op.direction(), ordering) {
+            (CmpOpDirection::Lesser, Ordering::Less) => false,
+            (CmpOpDirection::Lesser, Ordering::Equal) => false,
+            (CmpOpDirection::Lesser, Ordering::Greater) => true,
+            (CmpOpDirection::Greater, Ordering::Less) => true,
+            (CmpOpDirection::Greater, Ordering::Equal) => false,
+            (CmpOpDirection::Greater, Ordering::Greater) => false,
+        }
+    }
+}
+
+fn comparison_is_possible(left_cmp_direction: CmpOpDirection, ordering: Ordering) -> bool {
+    match (left_cmp_direction, ordering) {
+        (CmpOpDirection::Lesser, Ordering::Less | Ordering::Equal) => false,
+        (CmpOpDirection::Lesser, Ordering::Greater) => true,
+        (CmpOpDirection::Greater, Ordering::Greater | Ordering::Equal) => false,
+        (CmpOpDirection::Greater, Ordering::Less) => true,
+    }
+}
+
+#[derive(PartialEq, Eq, Clone, Copy)]
+enum CmpOpDirection {
+    Lesser,
+    Greater,
+}
+
+#[derive(Clone, Copy)]
+enum CmpOp {
+    Lt,
+    Le,
+    Ge,
+    Gt,
+}
+
+impl CmpOp {
+    fn reverse(self) -> Self {
+        match self {
+            CmpOp::Lt => CmpOp::Gt,
+            CmpOp::Le => CmpOp::Ge,
+            CmpOp::Ge => CmpOp::Le,
+            CmpOp::Gt => CmpOp::Lt,
+        }
+    }
+
+    fn direction(self) -> CmpOpDirection {
+        match self {
+            CmpOp::Lt => CmpOpDirection::Lesser,
+            CmpOp::Le => CmpOpDirection::Lesser,
+            CmpOp::Ge => CmpOpDirection::Greater,
+            CmpOp::Gt => CmpOpDirection::Greater,
+        }
+    }
+}
+
+impl TryFrom<BinOpKind> for CmpOp {
+    type Error = ();
+
+    fn try_from(bin_op: BinOpKind) -> Result<Self, Self::Error> {
+        match bin_op {
+            BinOpKind::Lt => Ok(CmpOp::Lt),
+            BinOpKind::Le => Ok(CmpOp::Le),
+            BinOpKind::Ge => Ok(CmpOp::Ge),
+            BinOpKind::Gt => Ok(CmpOp::Gt),
+            _ => Err(()),
+        }
+    }
+}
diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs
index 2cf15adda01..4635e1164cd 100644
--- a/clippy_lints/src/operators/mod.rs
+++ b/clippy_lints/src/operators/mod.rs
@@ -2,6 +2,7 @@ mod absurd_extreme_comparisons;
 mod assign_op_pattern;
 mod bit_mask;
 mod cmp_owned;
+mod const_comparisons;
 mod double_comparison;
 mod duration_subsec;
 mod eq_op;
@@ -300,6 +301,45 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
+    /// Checks for double comparisons that can never succeed
+    ///
+    /// ### Why is this bad?
+    /// The whole expression can be replaced by `false`,
+    /// which is probably not the programmer's intention
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let status_code = 200;
+    /// if status_code <= 400 && status_code > 500 {}
+    /// ```
+    #[clippy::version = "1.71.0"]
+    pub IMPOSSIBLE_COMPARISONS,
+    correctness,
+    "double comparisons that will never evaluate to `true`"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for ineffective double comparisons against constants.
+    ///
+    /// ### Why is this bad?
+    /// Only one of the comparisons has any effect on the result, the programmer
+    /// probably intended to flip one of the comparison operators, or compare a
+    /// different value entirely.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let status_code = 200;
+    /// if status_code <= 400 && status_code < 500 {}
+    /// ```
+    #[clippy::version = "1.71.0"]
+    pub REDUNDANT_COMPARISONS,
+    correctness,
+    "double comparisons where one of them can be removed"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
     /// Checks for calculation of subsecond microseconds or milliseconds
     /// from other `Duration` methods.
     ///
@@ -742,6 +782,8 @@ impl_lint_pass!(Operators => [
     INEFFECTIVE_BIT_MASK,
     VERBOSE_BIT_MASK,
     DOUBLE_COMPARISONS,
+    IMPOSSIBLE_COMPARISONS,
+    REDUNDANT_COMPARISONS,
     DURATION_SUBSEC,
     EQ_OP,
     OP_REF,
@@ -786,6 +828,7 @@ impl<'tcx> LateLintPass<'tcx> for Operators {
                 bit_mask::check(cx, e, op.node, lhs, rhs);
                 verbose_bit_mask::check(cx, e, op.node, lhs, rhs, self.verbose_bit_mask_threshold);
                 double_comparison::check(cx, op.node, lhs, rhs, e.span);
+                const_comparisons::check(cx, op, lhs, rhs, e.span);
                 duration_subsec::check(cx, e, op.node, lhs, rhs);
                 float_equality_without_abs::check(cx, e, op.node, lhs, rhs);
                 integer_division::check(cx, e, op.node, lhs, rhs);
diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs
index 061086c4fc2..63dfd2145b8 100644
--- a/clippy_utils/src/consts.rs
+++ b/clippy_utils/src/consts.rs
@@ -329,7 +329,7 @@ pub struct ConstEvalLateContext<'a, 'tcx> {
 }
 
 impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
-    fn new(lcx: &'a LateContext<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>) -> Self {
+    pub fn new(lcx: &'a LateContext<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>) -> Self {
         Self {
             lcx,
             typeck_results,
diff --git a/tests/ui/const_comparisons.rs b/tests/ui/const_comparisons.rs
new file mode 100644
index 00000000000..8e265c9141c
--- /dev/null
+++ b/tests/ui/const_comparisons.rs
@@ -0,0 +1,93 @@
+#![allow(unused)]
+#![warn(clippy::impossible_comparisons)]
+#![warn(clippy::redundant_comparisons)]
+#![allow(clippy::no_effect)]
+#![allow(clippy::short_circuit_statement)]
+#![allow(clippy::manual_range_contains)]
+
+const STATUS_BAD_REQUEST: u16 = 400;
+const STATUS_SERVER_ERROR: u16 = 500;
+
+struct Status {
+    code: u16,
+}
+
+impl PartialEq<u16> for Status {
+    fn eq(&self, other: &u16) -> bool {
+        self.code == *other
+    }
+}
+
+impl PartialOrd<u16> for Status {
+    fn partial_cmp(&self, other: &u16) -> Option<std::cmp::Ordering> {
+        self.code.partial_cmp(other)
+    }
+}
+
+impl PartialEq<Status> for u16 {
+    fn eq(&self, other: &Status) -> bool {
+        *self == other.code
+    }
+}
+
+impl PartialOrd<Status> for u16 {
+    fn partial_cmp(&self, other: &Status) -> Option<std::cmp::Ordering> {
+        self.partial_cmp(&other.code)
+    }
+}
+
+fn main() {
+    let status_code = 500; // Value doesn't matter for the lint
+    let status = Status { code: status_code };
+
+    status_code >= 400 && status_code < 500; // Correct
+    status_code <= 400 && status_code > 500;
+    status_code > 500 && status_code < 400;
+    status_code < 500 && status_code > 500;
+
+    // More complex expressions
+    status_code < { 400 } && status_code > { 500 };
+    status_code < STATUS_BAD_REQUEST && status_code > STATUS_SERVER_ERROR;
+    status_code <= u16::MIN + 1 && status_code > STATUS_SERVER_ERROR;
+    status_code < STATUS_SERVER_ERROR && status_code > STATUS_SERVER_ERROR;
+
+    // Comparing two different types, via the `impl PartialOrd<u16> for Status`
+    status < { 400 } && status > { 500 };
+    status < STATUS_BAD_REQUEST && status > STATUS_SERVER_ERROR;
+    status <= u16::MIN + 1 && status > STATUS_SERVER_ERROR;
+    status < STATUS_SERVER_ERROR && status > STATUS_SERVER_ERROR;
+
+    // Yoda conditions
+    500 <= status_code && 600 > status_code; // Correct
+    500 <= status_code && status_code <= 600; // Correct
+    500 >= status_code && 600 < status_code; // Incorrect
+    500 >= status_code && status_code > 600; // Incorrect
+
+    // Yoda conditions, comparing two different types
+    500 <= status && 600 > status; // Correct
+    500 <= status && status <= 600; // Correct
+    500 >= status && 600 < status; // Incorrect
+    500 >= status && status > 600; // Incorrect
+
+    // Expressions where one of the sides has no effect
+    status_code < 200 && status_code <= 299;
+    status_code > 200 && status_code >= 299;
+
+    status_code >= 500 && status_code > 500; // Useless left
+    status_code > 500 && status_code >= 500; // Useless right
+    status_code <= 500 && status_code < 500; // Useless left
+    status_code < 500 && status_code <= 500; // Useless right
+
+    // Other types
+    let name = "Steve";
+    name < "Jennifer" && name > "Shannon";
+
+    let numbers = [1, 2];
+    numbers < [3, 4] && numbers > [5, 6];
+
+    let letter = 'a';
+    letter < 'b' && letter > 'c';
+
+    let area = 42.0;
+    area < std::f32::consts::E && area > std::f32::consts::PI;
+}
diff --git a/tests/ui/const_comparisons.stderr b/tests/ui/const_comparisons.stderr
new file mode 100644
index 00000000000..90e6db64762
--- /dev/null
+++ b/tests/ui/const_comparisons.stderr
@@ -0,0 +1,228 @@
+error: boolean expression will never evaluate to 'true'
+  --> $DIR/const_comparisons.rs:44:5
+   |
+LL |     status_code <= 400 && status_code > 500;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: since `400` < `500`, the expression evaluates to false for any value of `status_code`
+   = note: `-D clippy::impossible-comparisons` implied by `-D warnings`
+
+error: boolean expression will never evaluate to 'true'
+  --> $DIR/const_comparisons.rs:45:5
+   |
+LL |     status_code > 500 && status_code < 400;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: since `500` > `400`, the expression evaluates to false for any value of `status_code`
+
+error: boolean expression will never evaluate to 'true'
+  --> $DIR/const_comparisons.rs:46:5
+   |
+LL |     status_code < 500 && status_code > 500;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `status_code` cannot simultaneously be greater than and less than `500`
+
+error: boolean expression will never evaluate to 'true'
+  --> $DIR/const_comparisons.rs:49:5
+   |
+LL |     status_code < { 400 } && status_code > { 500 };
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: since `{ 400 }` < `{ 500 }`, the expression evaluates to false for any value of `status_code`
+
+error: boolean expression will never evaluate to 'true'
+  --> $DIR/const_comparisons.rs:50:5
+   |
+LL |     status_code < STATUS_BAD_REQUEST && status_code > STATUS_SERVER_ERROR;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: since `STATUS_BAD_REQUEST` < `STATUS_SERVER_ERROR`, the expression evaluates to false for any value of `status_code`
+
+error: boolean expression will never evaluate to 'true'
+  --> $DIR/const_comparisons.rs:51:5
+   |
+LL |     status_code <= u16::MIN + 1 && status_code > STATUS_SERVER_ERROR;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: since `u16::MIN + 1` < `STATUS_SERVER_ERROR`, the expression evaluates to false for any value of `status_code`
+
+error: boolean expression will never evaluate to 'true'
+  --> $DIR/const_comparisons.rs:52:5
+   |
+LL |     status_code < STATUS_SERVER_ERROR && status_code > STATUS_SERVER_ERROR;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `status_code` cannot simultaneously be greater than and less than `STATUS_SERVER_ERROR`
+
+error: boolean expression will never evaluate to 'true'
+  --> $DIR/const_comparisons.rs:55:5
+   |
+LL |     status < { 400 } && status > { 500 };
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: since `{ 400 }` < `{ 500 }`, the expression evaluates to false for any value of `status`
+
+error: boolean expression will never evaluate to 'true'
+  --> $DIR/const_comparisons.rs:56:5
+   |
+LL |     status < STATUS_BAD_REQUEST && status > STATUS_SERVER_ERROR;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: since `STATUS_BAD_REQUEST` < `STATUS_SERVER_ERROR`, the expression evaluates to false for any value of `status`
+
+error: boolean expression will never evaluate to 'true'
+  --> $DIR/const_comparisons.rs:57:5
+   |
+LL |     status <= u16::MIN + 1 && status > STATUS_SERVER_ERROR;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: since `u16::MIN + 1` < `STATUS_SERVER_ERROR`, the expression evaluates to false for any value of `status`
+
+error: boolean expression will never evaluate to 'true'
+  --> $DIR/const_comparisons.rs:58:5
+   |
+LL |     status < STATUS_SERVER_ERROR && status > STATUS_SERVER_ERROR;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `status` cannot simultaneously be greater than and less than `STATUS_SERVER_ERROR`
+
+error: boolean expression will never evaluate to 'true'
+  --> $DIR/const_comparisons.rs:63:5
+   |
+LL |     500 >= status_code && 600 < status_code; // Incorrect
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: since `500` < `600`, the expression evaluates to false for any value of `status_code`
+
+error: boolean expression will never evaluate to 'true'
+  --> $DIR/const_comparisons.rs:64:5
+   |
+LL |     500 >= status_code && status_code > 600; // Incorrect
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: since `500` < `600`, the expression evaluates to false for any value of `status_code`
+
+error: boolean expression will never evaluate to 'true'
+  --> $DIR/const_comparisons.rs:69:5
+   |
+LL |     500 >= status && 600 < status; // Incorrect
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: since `500` < `600`, the expression evaluates to false for any value of `status`
+
+error: boolean expression will never evaluate to 'true'
+  --> $DIR/const_comparisons.rs:70:5
+   |
+LL |     500 >= status && status > 600; // Incorrect
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: since `500` < `600`, the expression evaluates to false for any value of `status`
+
+error: right-hand side of `&&` operator has no effect
+  --> $DIR/const_comparisons.rs:73:5
+   |
+LL |     status_code < 200 && status_code <= 299;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: `if `status_code < 200` evaluates to true, status_code <= 299` will always evaluate to true as well
+  --> $DIR/const_comparisons.rs:73:23
+   |
+LL |     status_code < 200 && status_code <= 299;
+   |                       ^^^^^^^^^^^^^^^^^^^^^
+   = note: `-D clippy::redundant-comparisons` implied by `-D warnings`
+
+error: left-hand side of `&&` operator has no effect
+  --> $DIR/const_comparisons.rs:74:5
+   |
+LL |     status_code > 200 && status_code >= 299;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: `if `status_code >= 299` evaluates to true, status_code > 200` will always evaluate to true as well
+  --> $DIR/const_comparisons.rs:74:5
+   |
+LL |     status_code > 200 && status_code >= 299;
+   |     ^^^^^^^^^^^^^^^^^^^^^
+
+error: left-hand side of `&&` operator has no effect
+  --> $DIR/const_comparisons.rs:76:5
+   |
+LL |     status_code >= 500 && status_code > 500; // Useless left
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: `if `status_code > 500` evaluates to true, status_code >= 500` will always evaluate to true as well
+  --> $DIR/const_comparisons.rs:76:5
+   |
+LL |     status_code >= 500 && status_code > 500; // Useless left
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+
+error: right-hand side of `&&` operator has no effect
+  --> $DIR/const_comparisons.rs:77:5
+   |
+LL |     status_code > 500 && status_code >= 500; // Useless right
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: `if `status_code > 500` evaluates to true, status_code >= 500` will always evaluate to true as well
+  --> $DIR/const_comparisons.rs:77:23
+   |
+LL |     status_code > 500 && status_code >= 500; // Useless right
+   |                       ^^^^^^^^^^^^^^^^^^^^^
+
+error: left-hand side of `&&` operator has no effect
+  --> $DIR/const_comparisons.rs:78:5
+   |
+LL |     status_code <= 500 && status_code < 500; // Useless left
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: `if `status_code < 500` evaluates to true, status_code <= 500` will always evaluate to true as well
+  --> $DIR/const_comparisons.rs:78:5
+   |
+LL |     status_code <= 500 && status_code < 500; // Useless left
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+
+error: right-hand side of `&&` operator has no effect
+  --> $DIR/const_comparisons.rs:79:5
+   |
+LL |     status_code < 500 && status_code <= 500; // Useless right
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: `if `status_code < 500` evaluates to true, status_code <= 500` will always evaluate to true as well
+  --> $DIR/const_comparisons.rs:79:23
+   |
+LL |     status_code < 500 && status_code <= 500; // Useless right
+   |                       ^^^^^^^^^^^^^^^^^^^^^
+
+error: boolean expression will never evaluate to 'true'
+  --> $DIR/const_comparisons.rs:83:5
+   |
+LL |     name < "Jennifer" && name > "Shannon";
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: since `"Jennifer"` < `"Shannon"`, the expression evaluates to false for any value of `name`
+
+error: boolean expression will never evaluate to 'true'
+  --> $DIR/const_comparisons.rs:86:5
+   |
+LL |     numbers < [3, 4] && numbers > [5, 6];
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: since `[3, 4]` < `[5, 6]`, the expression evaluates to false for any value of `numbers`
+
+error: boolean expression will never evaluate to 'true'
+  --> $DIR/const_comparisons.rs:89:5
+   |
+LL |     letter < 'b' && letter > 'c';
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: since `'b'` < `'c'`, the expression evaluates to false for any value of `letter`
+
+error: boolean expression will never evaluate to 'true'
+  --> $DIR/const_comparisons.rs:92:5
+   |
+LL |     area < std::f32::consts::E && area > std::f32::consts::PI;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: since `std::f32::consts::E` < `std::f32::consts::PI`, the expression evaluates to false for any value of `area`
+
+error: aborting due to 25 previous errors
+
diff --git a/tests/ui/range_contains.fixed b/tests/ui/range_contains.fixed
index 0a92ee7c8dd..47c5248118e 100644
--- a/tests/ui/range_contains.fixed
+++ b/tests/ui/range_contains.fixed
@@ -5,6 +5,8 @@
 #![allow(clippy::no_effect)]
 #![allow(clippy::short_circuit_statement)]
 #![allow(clippy::unnecessary_operation)]
+#![allow(clippy::impossible_comparisons)]
+#![allow(clippy::redundant_comparisons)]
 
 fn main() {
     let x = 9_i32;
diff --git a/tests/ui/range_contains.rs b/tests/ui/range_contains.rs
index 7a83be60957..a35315a649d 100644
--- a/tests/ui/range_contains.rs
+++ b/tests/ui/range_contains.rs
@@ -5,6 +5,8 @@
 #![allow(clippy::no_effect)]
 #![allow(clippy::short_circuit_statement)]
 #![allow(clippy::unnecessary_operation)]
+#![allow(clippy::impossible_comparisons)]
+#![allow(clippy::redundant_comparisons)]
 
 fn main() {
     let x = 9_i32;
diff --git a/tests/ui/range_contains.stderr b/tests/ui/range_contains.stderr
index ea34023a466..1265db695bf 100644
--- a/tests/ui/range_contains.stderr
+++ b/tests/ui/range_contains.stderr
@@ -1,5 +1,5 @@
 error: manual `Range::contains` implementation
-  --> $DIR/range_contains.rs:13:5
+  --> $DIR/range_contains.rs:15:5
    |
 LL |     x >= 8 && x < 12;
    |     ^^^^^^^^^^^^^^^^ help: use: `(8..12).contains(&x)`
@@ -7,121 +7,121 @@ LL |     x >= 8 && x < 12;
    = note: `-D clippy::manual-range-contains` implied by `-D warnings`
 
 error: manual `Range::contains` implementation
-  --> $DIR/range_contains.rs:14:5
+  --> $DIR/range_contains.rs:16:5
    |
 LL |     x < 42 && x >= 21;
    |     ^^^^^^^^^^^^^^^^^ help: use: `(21..42).contains(&x)`
 
 error: manual `Range::contains` implementation
-  --> $DIR/range_contains.rs:15:5
+  --> $DIR/range_contains.rs:17:5
    |
 LL |     100 > x && 1 <= x;
    |     ^^^^^^^^^^^^^^^^^ help: use: `(1..100).contains(&x)`
 
 error: manual `RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:18:5
+  --> $DIR/range_contains.rs:20:5
    |
 LL |     x >= 9 && x <= 99;
    |     ^^^^^^^^^^^^^^^^^ help: use: `(9..=99).contains(&x)`
 
 error: manual `RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:19:5
+  --> $DIR/range_contains.rs:21:5
    |
 LL |     x <= 33 && x >= 1;
    |     ^^^^^^^^^^^^^^^^^ help: use: `(1..=33).contains(&x)`
 
 error: manual `RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:20:5
+  --> $DIR/range_contains.rs:22:5
    |
 LL |     999 >= x && 1 <= x;
    |     ^^^^^^^^^^^^^^^^^^ help: use: `(1..=999).contains(&x)`
 
 error: manual `!Range::contains` implementation
-  --> $DIR/range_contains.rs:23:5
+  --> $DIR/range_contains.rs:25:5
    |
 LL |     x < 8 || x >= 12;
    |     ^^^^^^^^^^^^^^^^ help: use: `!(8..12).contains(&x)`
 
 error: manual `!Range::contains` implementation
-  --> $DIR/range_contains.rs:24:5
+  --> $DIR/range_contains.rs:26:5
    |
 LL |     x >= 42 || x < 21;
    |     ^^^^^^^^^^^^^^^^^ help: use: `!(21..42).contains(&x)`
 
 error: manual `!Range::contains` implementation
-  --> $DIR/range_contains.rs:25:5
+  --> $DIR/range_contains.rs:27:5
    |
 LL |     100 <= x || 1 > x;
    |     ^^^^^^^^^^^^^^^^^ help: use: `!(1..100).contains(&x)`
 
 error: manual `!RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:28:5
+  --> $DIR/range_contains.rs:30:5
    |
 LL |     x < 9 || x > 99;
    |     ^^^^^^^^^^^^^^^ help: use: `!(9..=99).contains(&x)`
 
 error: manual `!RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:29:5
+  --> $DIR/range_contains.rs:31:5
    |
 LL |     x > 33 || x < 1;
    |     ^^^^^^^^^^^^^^^ help: use: `!(1..=33).contains(&x)`
 
 error: manual `!RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:30:5
+  --> $DIR/range_contains.rs:32:5
    |
 LL |     999 < x || 1 > x;
    |     ^^^^^^^^^^^^^^^^ help: use: `!(1..=999).contains(&x)`
 
 error: manual `Range::contains` implementation
-  --> $DIR/range_contains.rs:45:5
+  --> $DIR/range_contains.rs:47:5
    |
 LL |     y >= 0. && y < 1.;
    |     ^^^^^^^^^^^^^^^^^ help: use: `(0. ..1.).contains(&y)`
 
 error: manual `!RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:46:5
+  --> $DIR/range_contains.rs:48:5
    |
 LL |     y < 0. || y > 1.;
    |     ^^^^^^^^^^^^^^^^ help: use: `!(0. ..=1.).contains(&y)`
 
 error: manual `RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:49:5
+  --> $DIR/range_contains.rs:51:5
    |
 LL |     x >= -10 && x <= 10;
    |     ^^^^^^^^^^^^^^^^^^^ help: use: `(-10..=10).contains(&x)`
 
 error: manual `RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:51:5
+  --> $DIR/range_contains.rs:53:5
    |
 LL |     y >= -3. && y <= 3.;
    |     ^^^^^^^^^^^^^^^^^^^ help: use: `(-3. ..=3.).contains(&y)`
 
 error: manual `RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:56:30
+  --> $DIR/range_contains.rs:58:30
    |
 LL |     (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10);
    |                              ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&z)`
 
 error: manual `RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:56:5
+  --> $DIR/range_contains.rs:58:5
    |
 LL |     (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10);
    |     ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&x)`
 
 error: manual `!Range::contains` implementation
-  --> $DIR/range_contains.rs:57:29
+  --> $DIR/range_contains.rs:59:29
    |
 LL |     (x < 0) || (x >= 10) || (z < 0) || (z >= 10);
    |                             ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&z)`
 
 error: manual `!Range::contains` implementation
-  --> $DIR/range_contains.rs:57:5
+  --> $DIR/range_contains.rs:59:5
    |
 LL |     (x < 0) || (x >= 10) || (z < 0) || (z >= 10);
    |     ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&x)`
 
 error: manual `Range::contains` implementation
-  --> $DIR/range_contains.rs:76:5
+  --> $DIR/range_contains.rs:78:5
    |
 LL |     x >= 8 && x < 35;
    |     ^^^^^^^^^^^^^^^^ help: use: `(8..35).contains(&x)`