about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md2
-rw-r--r--clippy_lints/src/declared_lints.rs2
-rw-r--r--clippy_lints/src/operators/double_const_comparison.rs204
-rw-r--r--clippy_lints/src/operators/mod.rs41
-rw-r--r--tests/ui/double_const_comparisons.rs52
-rw-r--r--tests/ui/double_const_comparisons.stderr180
-rw-r--r--tests/ui/range_contains.fixed2
-rw-r--r--tests/ui/range_contains.rs2
-rw-r--r--tests/ui/range_contains.stderr42
9 files changed, 506 insertions, 21 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3ef22555979..3f9ec3a4df9 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_double_const_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#impossible_double_const_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
@@ -4905,6 +4906,7 @@ Released 2018-09-13
 [`index_refutable_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice
 [`indexing_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing
 [`ineffective_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_bit_mask
+[`ineffective_double_const_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_double_const_comparisons
 [`inefficient_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string
 [`infallible_destructuring_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#infallible_destructuring_match
 [`infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#infinite_iter
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index ea66c760a2f..897e6c7bd10 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -518,7 +518,9 @@ 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_DOUBLE_CONST_COMPARISONS_INFO,
     crate::operators::INEFFECTIVE_BIT_MASK_INFO,
+    crate::operators::INEFFECTIVE_DOUBLE_CONST_COMPARISONS_INFO,
     crate::operators::INTEGER_DIVISION_INFO,
     crate::operators::MISREFACTORED_ASSIGN_OP_INFO,
     crate::operators::MODULO_ARITHMETIC_INFO,
diff --git a/clippy_lints/src/operators/double_const_comparison.rs b/clippy_lints/src/operators/double_const_comparison.rs
new file mode 100644
index 00000000000..d029a3d4ebe
--- /dev/null
+++ b/clippy_lints/src/operators/double_const_comparison.rs
@@ -0,0 +1,204 @@
+#![allow(clippy::match_same_arms)]
+
+use std::cmp::Ordering;
+
+use clippy_utils::consts;
+use clippy_utils::consts::{ConstEvalLateContext, Constant};
+use if_chain::if_chain;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{layout::HasTyCtxt, 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_DOUBLE_CONST_COMPARISONS;
+use super::INEFFECTIVE_DOUBLE_CONST_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>(
+    ctx: &mut ConstEvalLateContext<'_, 'tcx>,
+    typeck: &TypeckResults<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+) -> Option<(CmpOp, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Constant, 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 (ctx.expr(left), ctx.expr(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;
+
+        let typeck_results = cx.typeck_results();
+        let mut const_context = consts::ConstEvalLateContext::new(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(&mut const_context, typeck_results, left_cond);
+        if let Some((right_cmp_op, right_expr, right_const_expr, right_const, right_type)) =
+            comparison_to_const(&mut const_context, typeck_results, 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, "");
+                let rhs_str = snippet(cx, right_cond.span, "");
+                // 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,
+                        INEFFECTIVE_DOUBLE_CONST_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,
+                        INEFFECTIVE_DOUBLE_CONST_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, "");
+                let rhs_str = snippet(cx, right_const_expr.span, "");
+                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_DOUBLE_CONST_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..41efcef86c8 100644
--- a/clippy_lints/src/operators/mod.rs
+++ b/clippy_lints/src/operators/mod.rs
@@ -3,6 +3,7 @@ mod assign_op_pattern;
 mod bit_mask;
 mod cmp_owned;
 mod double_comparison;
+mod double_const_comparison;
 mod duration_subsec;
 mod eq_op;
 mod erasing_op;
@@ -300,6 +301,43 @@ 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
+    /// status_code <= 400 && status_code > 500;
+    /// ```
+    #[clippy::version = "1.71.0"]
+    pub IMPOSSIBLE_DOUBLE_CONST_COMPARISONS,
+    correctness,
+    "default lint description"
+}
+
+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
+    /// status_code <= 400 && status_code < 500;
+    /// ```
+    #[clippy::version = "1.71.0"]
+    pub INEFFECTIVE_DOUBLE_CONST_COMPARISONS,
+    correctness,
+    "default lint description"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
     /// Checks for calculation of subsecond microseconds or milliseconds
     /// from other `Duration` methods.
     ///
@@ -742,6 +780,8 @@ impl_lint_pass!(Operators => [
     INEFFECTIVE_BIT_MASK,
     VERBOSE_BIT_MASK,
     DOUBLE_COMPARISONS,
+    IMPOSSIBLE_DOUBLE_CONST_COMPARISONS,
+    INEFFECTIVE_DOUBLE_CONST_COMPARISONS,
     DURATION_SUBSEC,
     EQ_OP,
     OP_REF,
@@ -786,6 +826,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);
+                double_const_comparison::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/tests/ui/double_const_comparisons.rs b/tests/ui/double_const_comparisons.rs
new file mode 100644
index 00000000000..0f57fbba1eb
--- /dev/null
+++ b/tests/ui/double_const_comparisons.rs
@@ -0,0 +1,52 @@
+#![allow(unused)]
+#![warn(clippy::impossible_double_const_comparisons)]
+#![warn(clippy::ineffective_double_const_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;
+
+fn main() {
+    let status_code = 500; // Value doesn't matter for the lint
+
+    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;
+
+    // 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
+
+    // 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/double_const_comparisons.stderr b/tests/ui/double_const_comparisons.stderr
new file mode 100644
index 00000000000..76cfc82c0a4
--- /dev/null
+++ b/tests/ui/double_const_comparisons.stderr
@@ -0,0 +1,180 @@
+error: boolean expression will never evaluate to 'true'
+  --> $DIR/double_const_comparisons.rs:15: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-double-const-comparisons` implied by `-D warnings`
+
+error: boolean expression will never evaluate to 'true'
+  --> $DIR/double_const_comparisons.rs:16: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/double_const_comparisons.rs:17: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/double_const_comparisons.rs:20: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/double_const_comparisons.rs:21: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/double_const_comparisons.rs:22: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/double_const_comparisons.rs:23: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/double_const_comparisons.rs:28: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/double_const_comparisons.rs:29: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: right-hand side of `&&` operator has no effect
+  --> $DIR/double_const_comparisons.rs:32: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/double_const_comparisons.rs:32:23
+   |
+LL |     status_code < 200 && status_code <= 299;
+   |                       ^^^^^^^^^^^^^^^^^^^^^
+   = note: `-D clippy::ineffective-double-const-comparisons` implied by `-D warnings`
+
+error: left-hand side of `&&` operator has no effect
+  --> $DIR/double_const_comparisons.rs:33: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/double_const_comparisons.rs:33:5
+   |
+LL |     status_code > 200 && status_code >= 299;
+   |     ^^^^^^^^^^^^^^^^^^^^^
+
+error: left-hand side of `&&` operator has no effect
+  --> $DIR/double_const_comparisons.rs:35: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/double_const_comparisons.rs:35:5
+   |
+LL |     status_code >= 500 && status_code > 500; // Useless left
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+
+error: right-hand side of `&&` operator has no effect
+  --> $DIR/double_const_comparisons.rs:36: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/double_const_comparisons.rs:36:23
+   |
+LL |     status_code > 500 && status_code >= 500; // Useless right
+   |                       ^^^^^^^^^^^^^^^^^^^^^
+
+error: left-hand side of `&&` operator has no effect
+  --> $DIR/double_const_comparisons.rs:37: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/double_const_comparisons.rs:37:5
+   |
+LL |     status_code <= 500 && status_code < 500; // Useless left
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+
+error: right-hand side of `&&` operator has no effect
+  --> $DIR/double_const_comparisons.rs:38: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/double_const_comparisons.rs:38:23
+   |
+LL |     status_code < 500 && status_code <= 500; // Useless right
+   |                       ^^^^^^^^^^^^^^^^^^^^^
+
+error: boolean expression will never evaluate to 'true'
+  --> $DIR/double_const_comparisons.rs:42: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/double_const_comparisons.rs:45: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/double_const_comparisons.rs:48: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/double_const_comparisons.rs:51: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 19 previous errors
+
diff --git a/tests/ui/range_contains.fixed b/tests/ui/range_contains.fixed
index 0a92ee7c8dd..429d8608291 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_double_const_comparisons)]
+#![allow(clippy::ineffective_double_const_comparisons)]
 
 fn main() {
     let x = 9_i32;
diff --git a/tests/ui/range_contains.rs b/tests/ui/range_contains.rs
index 7a83be60957..e47e2948147 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_double_const_comparisons)]
+#![allow(clippy::ineffective_double_const_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)`