about summary refs log tree commit diff
path: root/clippy_lints/src
diff options
context:
space:
mode:
authorPhilipp Krones <hello@philkrones.com>2023-08-11 14:05:13 +0200
committerPhilipp Krones <hello@philkrones.com>2023-08-11 14:05:13 +0200
commitf730a2655a0ea22da4cf104d3e97f9fa17c0658f (patch)
treea93171179363bddc55403723e0b625e9c9758f91 /clippy_lints/src
parenta1ab2d765f8ee3c7735c5573e023f4f4057159e1 (diff)
downloadrust-f730a2655a0ea22da4cf104d3e97f9fa17c0658f.tar.gz
rust-f730a2655a0ea22da4cf104d3e97f9fa17c0658f.zip
Merge commit '1e8fdf492808a25d78a97e1242b835ace9924e4d' into clippyup
Diffstat (limited to 'clippy_lints/src')
-rw-r--r--clippy_lints/src/casts/ptr_as_ptr.rs64
-rw-r--r--clippy_lints/src/declared_lints.rs4
-rw-r--r--clippy_lints/src/derive.rs50
-rw-r--r--clippy_lints/src/doc.rs5
-rw-r--r--clippy_lints/src/eta_reduction.rs4
-rw-r--r--clippy_lints/src/ignored_unit_patterns.rs52
-rw-r--r--clippy_lints/src/lib.rs2
-rw-r--r--clippy_lints/src/loops/never_loop.rs4
-rw-r--r--clippy_lints/src/manual_float_methods.rs2
-rw-r--r--clippy_lints/src/matches/redundant_guards.rs10
-rw-r--r--clippy_lints/src/methods/expect_used.rs44
-rw-r--r--clippy_lints/src/methods/filter_map_bool_then.rs53
-rw-r--r--clippy_lints/src/methods/mod.rs121
-rw-r--r--clippy_lints/src/methods/unwrap_expect_used.rs83
-rw-r--r--clippy_lints/src/methods/unwrap_used.rs53
-rw-r--r--clippy_lints/src/mut_reference.rs5
-rw-r--r--clippy_lints/src/operators/const_comparisons.rs207
-rw-r--r--clippy_lints/src/operators/mod.rs43
-rw-r--r--clippy_lints/src/option_if_let_else.rs2
-rw-r--r--clippy_lints/src/question_mark.rs12
-rw-r--r--clippy_lints/src/redundant_locals.rs13
-rw-r--r--clippy_lints/src/redundant_type_annotations.rs11
-rw-r--r--clippy_lints/src/slow_vector_initialization.rs20
-rw-r--r--clippy_lints/src/suspicious_xor_used_as_pow.rs48
-rw-r--r--clippy_lints/src/utils/internal_lints/invalid_paths.rs2
25 files changed, 676 insertions, 238 deletions
diff --git a/clippy_lints/src/casts/ptr_as_ptr.rs b/clippy_lints/src/casts/ptr_as_ptr.rs
index 15ffb00da88..181dbcf6e9a 100644
--- a/clippy_lints/src/casts/ptr_as_ptr.rs
+++ b/clippy_lints/src/casts/ptr_as_ptr.rs
@@ -1,9 +1,7 @@
-use std::borrow::Cow;
-
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::sugg::Sugg;
-use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, Mutability, TyKind};
 use rustc_lint::LateContext;
@@ -16,33 +14,41 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) {
         return;
     }
 
-    if_chain! {
-        if let ExprKind::Cast(cast_expr, cast_to_hir_ty) = expr.kind;
-        let (cast_from, cast_to) = (cx.typeck_results().expr_ty(cast_expr), cx.typeck_results().expr_ty(expr));
-        if let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, .. }) = cast_from.kind();
-        if let ty::RawPtr(TypeAndMut { ty: to_pointee_ty, mutbl: to_mutbl }) = cast_to.kind();
-        if matches!((from_mutbl, to_mutbl),
-            (Mutability::Not, Mutability::Not) | (Mutability::Mut, Mutability::Mut));
+    if let ExprKind::Cast(cast_expr, cast_to_hir_ty) = expr.kind
+        && let (cast_from, cast_to) = (cx.typeck_results().expr_ty(cast_expr), cx.typeck_results().expr_ty(expr))
+        && let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, .. }) = cast_from.kind()
+        && let ty::RawPtr(TypeAndMut { ty: to_pointee_ty, mutbl: to_mutbl }) = cast_to.kind()
+        && matches!((from_mutbl, to_mutbl),
+            (Mutability::Not, Mutability::Not) | (Mutability::Mut, Mutability::Mut))
         // The `U` in `pointer::cast` have to be `Sized`
         // as explained here: https://github.com/rust-lang/rust/issues/60602.
-        if to_pointee_ty.is_sized(cx.tcx, cx.param_env);
-        then {
-            let mut applicability = Applicability::MachineApplicable;
-            let cast_expr_sugg = Sugg::hir_with_applicability(cx, cast_expr, "_", &mut applicability);
-            let turbofish = match &cast_to_hir_ty.kind {
-                    TyKind::Infer => Cow::Borrowed(""),
-                    TyKind::Ptr(mut_ty) if matches!(mut_ty.ty.kind, TyKind::Infer) => Cow::Borrowed(""),
-                    _ => Cow::Owned(format!("::<{to_pointee_ty}>")),
-                };
-            span_lint_and_sugg(
-                cx,
-                PTR_AS_PTR,
-                expr.span,
-                "`as` casting between raw pointers without changing its mutability",
-                "try `pointer::cast`, a safer alternative",
-                format!("{}.cast{turbofish}()", cast_expr_sugg.maybe_par()),
-                applicability,
-            );
-        }
+        && to_pointee_ty.is_sized(cx.tcx, cx.param_env)
+    {
+        let mut app = Applicability::MachineApplicable;
+        let cast_expr_sugg = Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app);
+        let turbofish = match &cast_to_hir_ty.kind {
+            TyKind::Infer => String::new(),
+            TyKind::Ptr(mut_ty) => {
+                if matches!(mut_ty.ty.kind, TyKind::Infer) {
+                    String::new()
+                } else {
+                    format!(
+                        "::<{}>",
+                        snippet_with_applicability(cx, mut_ty.ty.span, "/* type */", &mut app)
+                    )
+                }
+            },
+            _ => return,
+        };
+
+        span_lint_and_sugg(
+            cx,
+            PTR_AS_PTR,
+            expr.span,
+            "`as` casting between raw pointers without changing its mutability",
+            "try `pointer::cast`, a safer alternative",
+            format!("{}.cast{turbofish}()", cast_expr_sugg.maybe_par()),
+            app,
+        );
     }
 }
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index 9a9998cca4a..db114abfc86 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -203,6 +203,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::if_let_mutex::IF_LET_MUTEX_INFO,
     crate::if_not_else::IF_NOT_ELSE_INFO,
     crate::if_then_some_else_none::IF_THEN_SOME_ELSE_NONE_INFO,
+    crate::ignored_unit_patterns::IGNORED_UNIT_PATTERNS_INFO,
     crate::implicit_hasher::IMPLICIT_HASHER_INFO,
     crate::implicit_return::IMPLICIT_RETURN_INFO,
     crate::implicit_saturating_add::IMPLICIT_SATURATING_ADD_INFO,
@@ -337,6 +338,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::methods::EXPECT_USED_INFO,
     crate::methods::EXTEND_WITH_DRAIN_INFO,
     crate::methods::FILETYPE_IS_FILE_INFO,
+    crate::methods::FILTER_MAP_BOOL_THEN_INFO,
     crate::methods::FILTER_MAP_IDENTITY_INFO,
     crate::methods::FILTER_MAP_NEXT_INFO,
     crate::methods::FILTER_NEXT_INFO,
@@ -516,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,
@@ -524,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/derive.rs b/clippy_lints/src/derive.rs
index 9900dbdee6b..d3311792cfa 100644
--- a/clippy_lints/src/derive.rs
+++ b/clippy_lints/src/derive.rs
@@ -1,6 +1,4 @@
-use clippy_utils::diagnostics::{
-    span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
-};
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy};
 use clippy_utils::{is_lint_allowed, match_def_path, paths};
 use if_chain::if_chain;
@@ -8,15 +6,14 @@ use rustc_errors::Applicability;
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor};
 use rustc_hir::{
-    self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Impl, Item, ItemKind,
-    UnsafeSource, Unsafety,
+    self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Impl, Item, ItemKind, UnsafeSource, Unsafety,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::traits::Reveal;
 use rustc_middle::ty::{
-    self, ClauseKind, GenericArgKind, GenericParamDefKind, ImplPolarity, ParamEnv,
-    ToPredicate, TraitPredicate, Ty, TyCtxt,
+    self, ClauseKind, GenericArgKind, GenericParamDefKind, ImplPolarity, ParamEnv, ToPredicate, TraitPredicate, Ty,
+    TyCtxt,
 };
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::def_id::LocalDefId;
@@ -207,10 +204,13 @@ declare_lint_pass!(Derive => [
 
 impl<'tcx> LateLintPass<'tcx> for Derive {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
-        if let ItemKind::Impl(Impl { of_trait: Some(ref trait_ref), .. }) = item.kind {
+        if let ItemKind::Impl(Impl {
+            of_trait: Some(ref trait_ref),
+            ..
+        }) = item.kind
+        {
             let ty = cx.tcx.type_of(item.owner_id).instantiate_identity();
-            let is_automatically_derived =
-                cx.tcx.has_attr(item.owner_id, sym::automatically_derived);
+            let is_automatically_derived = cx.tcx.has_attr(item.owner_id, sym::automatically_derived);
 
             check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived);
             check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived);
@@ -327,12 +327,7 @@ fn check_ord_partial_ord<'tcx>(
 }
 
 /// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.
-fn check_copy_clone<'tcx>(
-    cx: &LateContext<'tcx>,
-    item: &Item<'_>,
-    trait_ref: &hir::TraitRef<'_>,
-    ty: Ty<'tcx>,
-) {
+fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
     let clone_id = match cx.tcx.lang_items().clone_trait() {
         Some(id) if trait_ref.trait_def_id() == Some(id) => id,
         _ => return,
@@ -350,9 +345,10 @@ fn check_copy_clone<'tcx>(
     if !is_copy(cx, ty) {
         if ty_subs.non_erasable_generics().next().is_some() {
             let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(&copy_id).map_or(false, |impls| {
-                impls
-                    .iter()
-                    .any(|&id| matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _) if ty_adt.did() == adt.did()))
+                impls.iter().any(|&id| {
+                    matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _)
+                                        if ty_adt.did() == adt.did())
+                })
             });
             if !has_copy_impl {
                 return;
@@ -431,14 +427,7 @@ struct UnsafeVisitor<'a, 'tcx> {
 impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
     type NestedFilter = nested_filter::All;
 
-    fn visit_fn(
-        &mut self,
-        kind: FnKind<'tcx>,
-        decl: &'tcx FnDecl<'_>,
-        body_id: BodyId,
-        _: Span,
-        id: LocalDefId,
-    ) {
+    fn visit_fn(&mut self, kind: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, body_id: BodyId, _: Span, id: LocalDefId) {
         if self.has_unsafe {
             return;
         }
@@ -474,12 +463,7 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
 }
 
 /// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint.
-fn check_partial_eq_without_eq<'tcx>(
-    cx: &LateContext<'tcx>,
-    span: Span,
-    trait_ref: &hir::TraitRef<'_>,
-    ty: Ty<'tcx>,
-) {
+fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
     if_chain! {
         if let ty::Adt(adt, args) = ty.kind();
         if cx.tcx.visibility(adt.did()).is_public();
diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs
index 2c4d93e33ba..e29ab634c97 100644
--- a/clippy_lints/src/doc.rs
+++ b/clippy_lints/src/doc.rs
@@ -716,10 +716,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
                 let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
                 let fallback_bundle =
                     rustc_errors::fallback_fluent_bundle(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
-                let emitter = EmitterWriter::new(
-                    Box::new(io::sink()),
-                    fallback_bundle,
-                );
+                let emitter = EmitterWriter::new(Box::new(io::sink()), fallback_bundle);
                 let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings();
                 let sess = ParseSess::with_span_handler(handler, sm);
 
diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs
index ba7957b0dec..38066503c07 100644
--- a/clippy_lints/src/eta_reduction.rs
+++ b/clippy_lints/src/eta_reduction.rs
@@ -10,8 +10,8 @@ use rustc_hir::{BindingAnnotation, Expr, ExprKind, FnRetTy, Param, PatKind, QPat
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{
-    self, Binder, ClosureArgs, ClosureKind, EarlyBinder, FnSig, GenericArg, GenericArgKind,
-    GenericArgsRef, ImplPolarity, List, Region, RegionKind, Ty, TypeVisitableExt, TypeckResults,
+    self, Binder, ClosureArgs, ClosureKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, GenericArgsRef,
+    ImplPolarity, List, Region, RegionKind, Ty, TypeVisitableExt, TypeckResults,
 };
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::symbol::sym;
diff --git a/clippy_lints/src/ignored_unit_patterns.rs b/clippy_lints/src/ignored_unit_patterns.rs
new file mode 100644
index 00000000000..c635120b882
--- /dev/null
+++ b/clippy_lints/src/ignored_unit_patterns.rs
@@ -0,0 +1,52 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use hir::PatKind;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for usage of `_` in patterns of type `()`.
+    ///
+    /// ### Why is this bad?
+    /// Matching with `()` explicitly instead of `_` outlines
+    /// the fact that the pattern contains no data. Also it
+    /// would detect a type change that `_` would ignore.
+    ///
+    /// ### Example
+    /// ```rust
+    /// match std::fs::create_dir("tmp-work-dir") {
+    ///    Ok(_) => println!("Working directory created"),
+    ///    Err(s) => eprintln!("Could not create directory: {s}"),
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// match std::fs::create_dir("tmp-work-dir") {
+    ///    Ok(()) => println!("Working directory created"),
+    ///    Err(s) => eprintln!("Could not create directory: {s}"),
+    /// }
+    /// ```
+    #[clippy::version = "1.73.0"]
+    pub IGNORED_UNIT_PATTERNS,
+    pedantic,
+    "suggest replacing `_` by `()` in patterns where appropriate"
+}
+declare_lint_pass!(IgnoredUnitPatterns => [IGNORED_UNIT_PATTERNS]);
+
+impl<'tcx> LateLintPass<'tcx> for IgnoredUnitPatterns {
+    fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx hir::Pat<'tcx>) {
+        if matches!(pat.kind, PatKind::Wild) && cx.typeck_results().pat_ty(pat).is_unit() {
+            span_lint_and_sugg(
+                cx,
+                IGNORED_UNIT_PATTERNS,
+                pat.span,
+                "matching over `()` is more explicit",
+                "use `()` instead of `_`",
+                String::from("()"),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+}
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 9d6096ccb2a..358004cf460 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -147,6 +147,7 @@ mod future_not_send;
 mod if_let_mutex;
 mod if_not_else;
 mod if_then_some_else_none;
+mod ignored_unit_patterns;
 mod implicit_hasher;
 mod implicit_return;
 mod implicit_saturating_add;
@@ -1093,6 +1094,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         })
     });
     store.register_late_pass(|_| Box::new(redundant_locals::RedundantLocals));
+    store.register_late_pass(|_| Box::new(ignored_unit_patterns::IgnoredUnitPatterns));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs
index dd77c69ef88..cc19ac55e5e 100644
--- a/clippy_lints/src/loops/never_loop.rs
+++ b/clippy_lints/src/loops/never_loop.rs
@@ -162,7 +162,9 @@ fn never_loop_expr<'tcx>(
         ExprKind::Binary(_, e1, e2)
         | ExprKind::Assign(e1, e2, _)
         | ExprKind::AssignOp(_, e1, e2)
-        | ExprKind::Index(e1, e2, _) => never_loop_expr_all(cx, &mut [e1, e2].iter().copied(), ignore_ids, main_loop_id),
+        | ExprKind::Index(e1, e2, _) => {
+            never_loop_expr_all(cx, &mut [e1, e2].iter().copied(), ignore_ids, main_loop_id)
+        },
         ExprKind::Loop(b, _, _, _) => {
             // Break can come from the inner loop so remove them.
             absorb_break(never_loop_block(cx, b, ignore_ids, main_loop_id))
diff --git a/clippy_lints/src/manual_float_methods.rs b/clippy_lints/src/manual_float_methods.rs
index f48a5d9d245..88db7ae6aec 100644
--- a/clippy_lints/src/manual_float_methods.rs
+++ b/clippy_lints/src/manual_float_methods.rs
@@ -84,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
         if !in_external_macro(cx.sess(), expr.span)
             && (
-                matches!(cx.tcx.constness(cx.tcx.hir().enclosing_body_owner(expr.hir_id)), Constness::NotConst) 
+                matches!(cx.tcx.constness(cx.tcx.hir().enclosing_body_owner(expr.hir_id)), Constness::NotConst)
                     || cx.tcx.features().active(sym!(const_float_classify))
             ) && let ExprKind::Binary(kind, lhs, rhs) = expr.kind
             && let ExprKind::Binary(lhs_kind, lhs_lhs, lhs_rhs) = lhs.kind
diff --git a/clippy_lints/src/matches/redundant_guards.rs b/clippy_lints/src/matches/redundant_guards.rs
index 6383326aa38..29af4812351 100644
--- a/clippy_lints/src/matches/redundant_guards.rs
+++ b/clippy_lints/src/matches/redundant_guards.rs
@@ -2,6 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::path_to_local;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::visitors::{for_each_expr, is_local_used};
+use rustc_ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, Guard, MatchSource, Node, Pat, PatKind};
@@ -160,6 +161,11 @@ fn emit_redundant_guards<'tcx>(
 }
 
 /// Checks if the given `Expr` can also be represented as a `Pat`.
+///
+/// All literals generally also work as patterns, however float literals are special.
+/// They are currently (as of 2023/08/08) still allowed in patterns, but that will become
+/// an error in the future, and rustc already actively warns against this (see rust#41620),
+/// so we don't consider those as usable within patterns for linting purposes.
 fn expr_can_be_pat(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     for_each_expr(expr, |expr| {
         if match expr.kind {
@@ -177,8 +183,8 @@ fn expr_can_be_pat(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
             ExprKind::AddrOf(..)
             | ExprKind::Array(..)
             | ExprKind::Tup(..)
-            | ExprKind::Struct(..)
-            | ExprKind::Lit(..) => true,
+            | ExprKind::Struct(..) => true,
+            ExprKind::Lit(lit) if !matches!(lit.node, LitKind::Float(..)) => true,
             _ => false,
         } {
             return ControlFlow::Continue(());
diff --git a/clippy_lints/src/methods/expect_used.rs b/clippy_lints/src/methods/expect_used.rs
deleted file mode 100644
index 614610335a1..00000000000
--- a/clippy_lints/src/methods/expect_used.rs
+++ /dev/null
@@ -1,44 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{is_in_cfg_test, is_in_test_function};
-use rustc_hir as hir;
-use rustc_lint::LateContext;
-use rustc_span::sym;
-
-use super::EXPECT_USED;
-
-/// lint use of `expect()` or `expect_err` for `Result` and `expect()` for `Option`.
-pub(super) fn check(
-    cx: &LateContext<'_>,
-    expr: &hir::Expr<'_>,
-    recv: &hir::Expr<'_>,
-    is_err: bool,
-    allow_expect_in_tests: bool,
-) {
-    let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
-
-    let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err {
-        Some((EXPECT_USED, "an `Option`", "None", ""))
-    } else if is_type_diagnostic_item(cx, obj_ty, sym::Result) {
-        Some((EXPECT_USED, "a `Result`", if is_err { "Ok" } else { "Err" }, "an "))
-    } else {
-        None
-    };
-
-    let method = if is_err { "expect_err" } else { "expect" };
-
-    if allow_expect_in_tests && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)) {
-        return;
-    }
-
-    if let Some((lint, kind, none_value, none_prefix)) = mess {
-        span_lint_and_help(
-            cx,
-            lint,
-            expr.span,
-            &format!("used `{method}()` on {kind} value"),
-            None,
-            &format!("if this value is {none_prefix}`{none_value}`, it will panic"),
-        );
-    }
-}
diff --git a/clippy_lints/src/methods/filter_map_bool_then.rs b/clippy_lints/src/methods/filter_map_bool_then.rs
new file mode 100644
index 00000000000..fafc9709770
--- /dev/null
+++ b/clippy_lints/src/methods/filter_map_bool_then.rs
@@ -0,0 +1,53 @@
+use super::FILTER_MAP_BOOL_THEN;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::paths::BOOL_THEN;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::is_copy;
+use clippy_utils::{is_from_proc_macro, is_trait_method, match_def_path, peel_blocks};
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::Binder;
+use rustc_span::{sym, Span};
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &Expr<'_>, call_span: Span) {
+    if !in_external_macro(cx.sess(), expr.span)
+        && is_trait_method(cx, expr, sym::Iterator)
+        && let ExprKind::Closure(closure) = arg.kind
+        && let body = cx.tcx.hir().body(closure.body)
+        && let value = peel_blocks(body.value)
+        // Indexing should be fine as `filter_map` always has 1 input, we unfortunately need both
+        // `inputs` and `params` here as we need both the type and the span
+        && let param_ty = closure.fn_decl.inputs[0]
+        && let param = body.params[0]
+        // Issue #11309
+        && let param_ty = cx.tcx.liberate_late_bound_regions(
+            closure.def_id.to_def_id(),
+            Binder::bind_with_vars(
+                cx.typeck_results().node_type(param_ty.hir_id),
+                cx.tcx.late_bound_vars(cx.tcx.hir().local_def_id_to_hir_id(closure.def_id)),
+            ),
+        )
+        && is_copy(cx, param_ty)
+        && let ExprKind::MethodCall(_, recv, [then_arg], _) = value.kind
+        && let ExprKind::Closure(then_closure) = then_arg.kind
+        && let then_body = peel_blocks(cx.tcx.hir().body(then_closure.body).value)
+        && let Some(def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id)
+        && match_def_path(cx, def_id, &BOOL_THEN)
+        && !is_from_proc_macro(cx, expr)
+        && let Some(param_snippet) = snippet_opt(cx, param.span)
+        && let Some(filter) = snippet_opt(cx, recv.span)
+        && let Some(map) = snippet_opt(cx, then_body.span)
+    {
+        span_lint_and_sugg(
+            cx,
+            FILTER_MAP_BOOL_THEN,
+            call_span,
+            "usage of `bool::then` in `filter_map`",
+            "use `filter` then `map` instead",
+            format!("filter(|&{param_snippet}| {filter}).map(|{param_snippet}| {map})"),
+            Applicability::MachineApplicable,
+        );
+    }
+}
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index 28a8978973f..42756b27d01 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -17,10 +17,10 @@ mod collapsible_str_replace;
 mod drain_collect;
 mod err_expect;
 mod expect_fun_call;
-mod expect_used;
 mod extend_with_drain;
 mod filetype_is_file;
 mod filter_map;
+mod filter_map_bool_then;
 mod filter_map_identity;
 mod filter_map_next;
 mod filter_next;
@@ -104,7 +104,7 @@ mod unnecessary_lazy_eval;
 mod unnecessary_literal_unwrap;
 mod unnecessary_sort_by;
 mod unnecessary_to_owned;
-mod unwrap_used;
+mod unwrap_expect_used;
 mod useless_asref;
 mod utils;
 mod vec_resize_to_zero;
@@ -3478,6 +3478,37 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
+    /// Checks for usage of `bool::then` in `Iterator::filter_map`.
+    ///
+    /// ### Why is this bad?
+    /// This can be written with `filter` then `map` instead, which would reduce nesting and
+    /// separates the filtering from the transformation phase. This comes with no cost to
+    /// performance and is just cleaner.
+    ///
+    /// ### Limitations
+    /// Does not lint `bool::then_some`, as it eagerly evaluates its arguments rather than lazily.
+    /// This can create differing behavior, so better safe than sorry.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # fn really_expensive_fn(i: i32) -> i32 { i }
+    /// # let v = vec![];
+    /// _ = v.into_iter().filter_map(|i| (i % 2 == 0).then(|| really_expensive_fn(i)));
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # fn really_expensive_fn(i: i32) -> i32 { i }
+    /// # let v = vec![];
+    /// _ = v.into_iter().filter(|i| i % 2 == 0).map(|i| really_expensive_fn(i));
+    /// ```
+    #[clippy::version = "1.72.0"]
+    pub FILTER_MAP_BOOL_THEN,
+    style,
+    "checks for usage of `bool::then` in `Iterator::filter_map`"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
     /// Looks for calls to `RwLock::write` where the lock is only used for reading.
     ///
     /// ### Why is this bad?
@@ -3644,6 +3675,7 @@ impl_lint_pass!(Methods => [
     FORMAT_COLLECT,
     STRING_LIT_CHARS_ANY,
     ITER_SKIP_ZERO,
+    FILTER_MAP_BOOL_THEN,
     READONLY_WRITE_LOCK
 ]);
 
@@ -3848,6 +3880,13 @@ impl Methods {
                         unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
                     }
                 },
+                ("any", [arg]) if let ExprKind::Closure(arg) = arg.kind
+                    && let body = cx.tcx.hir().body(arg.body)
+                    && let [param] = body.params
+                    && let Some(("chars", recv, _, _, _)) = method_call(recv) =>
+                {
+                    string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv);
+                }
                 ("arg", [arg]) => {
                     suspicious_command_arg_space::check(cx, recv, arg, span);
                 }
@@ -3908,13 +3947,27 @@ impl Methods {
                     match method_call(recv) {
                         Some(("ok", recv, [], _, _)) => ok_expect::check(cx, expr, recv),
                         Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, span, err_span, &self.msrv),
-                        _ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests),
+                        _ => unwrap_expect_used::check(
+                            cx,
+                            expr,
+                            recv,
+                            false,
+                            self.allow_expect_in_tests,
+                            unwrap_expect_used::Variant::Expect,
+                        ),
                     }
                     unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
                 },
                 ("expect_err", [_]) => {
                     unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
-                    expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests);
+                    unwrap_expect_used::check(
+                        cx,
+                        expr,
+                        recv,
+                        true,
+                        self.allow_expect_in_tests,
+                        unwrap_expect_used::Variant::Expect,
+                    );
                 },
                 ("extend", [arg]) => {
                     string_extend_chars::check(cx, expr, recv, arg);
@@ -3922,6 +3975,7 @@ impl Methods {
                 },
                 ("filter_map", [arg]) => {
                     unnecessary_filter_map::check(cx, expr, arg, name);
+                    filter_map_bool_then::check(cx, expr, arg, call_span);
                     filter_map_identity::check(cx, expr, arg, span);
                 },
                 ("find_map", [arg]) => {
@@ -3965,20 +4019,9 @@ impl Methods {
                         unnecessary_join::check(cx, expr, recv, join_arg, span);
                     }
                 },
-                ("skip", [arg]) => {
-                    iter_skip_zero::check(cx, expr, arg);
-
-                    if let Some((name2, recv2, args2, _span2, _)) = method_call(recv) {
-                        if let ("cloned", []) = (name2, args2) {
-                            iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
-                        }
-                    }
-                }
                 ("last", []) => {
-                    if let Some((name2, recv2, args2, _span2, _)) = method_call(recv) {
-                        if let ("cloned", []) = (name2, args2) {
-                            iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
-                        }
+                    if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
+                        iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
                     }
                 },
                 ("lock", []) => {
@@ -4026,13 +4069,6 @@ impl Methods {
                         }
                     }
                 },
-                ("any", [arg]) if let ExprKind::Closure(arg) = arg.kind
-                    && let body = cx.tcx.hir().body(arg.body)
-                    && let [param] = body.params
-                    && let Some(("chars", recv, _, _, _)) = method_call(recv) =>
-                {
-                    string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv);
-                }
                 ("nth", [n_arg]) => match method_call(recv) {
                     Some(("bytes", recv2, [], _, _)) => bytes_nth::check(cx, expr, recv2, n_arg),
                     Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false),
@@ -4086,6 +4122,13 @@ impl Methods {
                         seek_to_start_instead_of_rewind::check(cx, expr, recv, arg, span);
                     }
                 },
+                ("skip", [arg]) => {
+                    iter_skip_zero::check(cx, expr, arg);
+
+                    if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
+                        iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
+                    }
+                }
                 ("sort", []) => {
                     stable_sort_primitive::check(cx, expr, recv);
                 },
@@ -4108,10 +4151,8 @@ impl Methods {
                 },
                 ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
                 ("take", [_arg]) => {
-                    if let Some((name2, recv2, args2, _span2, _)) = method_call(recv) {
-                        if let ("cloned", []) = (name2, args2) {
-                            iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
-                        }
+                    if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
+                        iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
                     }
                 },
                 ("take", []) => needless_option_take::check(cx, expr, recv),
@@ -4146,11 +4187,25 @@ impl Methods {
                         _ => {},
                     }
                     unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
-                    unwrap_used::check(cx, expr, recv, false, self.allow_unwrap_in_tests);
+                    unwrap_expect_used::check(
+                        cx,
+                        expr,
+                        recv,
+                        false,
+                        self.allow_unwrap_in_tests,
+                        unwrap_expect_used::Variant::Unwrap,
+                    );
                 },
                 ("unwrap_err", []) => {
                     unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
-                    unwrap_used::check(cx, expr, recv, true, self.allow_unwrap_in_tests);
+                    unwrap_expect_used::check(
+                        cx,
+                        expr,
+                        recv,
+                        true,
+                        self.allow_unwrap_in_tests,
+                        unwrap_expect_used::Variant::Unwrap,
+                    );
                 },
                 ("unwrap_or", [u_arg]) => {
                     match method_call(recv) {
@@ -4180,6 +4235,9 @@ impl Methods {
                     }
                     unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
                 },
+                ("write", []) => {
+                    readonly_write_lock::check(cx, expr, recv);
+                }
                 ("zip", [arg]) => {
                     if let ExprKind::MethodCall(name, iter_recv, [], _) = recv.kind
                         && name.ident.name == sym::iter
@@ -4187,9 +4245,6 @@ impl Methods {
                         range_zip_with_len::check(cx, expr, iter_recv, arg);
                     }
                 },
-                ("write", []) => {
-                    readonly_write_lock::check(cx, expr, recv);
-                }
                 _ => {},
             }
         }
diff --git a/clippy_lints/src/methods/unwrap_expect_used.rs b/clippy_lints/src/methods/unwrap_expect_used.rs
new file mode 100644
index 00000000000..7bd16b473ce
--- /dev/null
+++ b/clippy_lints/src/methods/unwrap_expect_used.rs
@@ -0,0 +1,83 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::ty::{is_never_like, is_type_diagnostic_item};
+use clippy_utils::{is_in_cfg_test, is_in_test_function, is_lint_allowed};
+use rustc_hir::Expr;
+use rustc_lint::{LateContext, Lint};
+use rustc_middle::ty;
+use rustc_span::sym;
+
+use super::{EXPECT_USED, UNWRAP_USED};
+
+#[derive(Clone, Copy, Eq, PartialEq)]
+pub(super) enum Variant {
+    Unwrap,
+    Expect,
+}
+
+impl Variant {
+    fn method_name(self, is_err: bool) -> &'static str {
+        match (self, is_err) {
+            (Variant::Unwrap, true) => "unwrap_err",
+            (Variant::Unwrap, false) => "unwrap",
+            (Variant::Expect, true) => "expect_err",
+            (Variant::Expect, false) => "expect",
+        }
+    }
+
+    fn lint(self) -> &'static Lint {
+        match self {
+            Variant::Unwrap => UNWRAP_USED,
+            Variant::Expect => EXPECT_USED,
+        }
+    }
+}
+
+/// Lint usage of `unwrap` or `unwrap_err` for `Result` and `unwrap()` for `Option` (and their
+/// `expect` counterparts).
+pub(super) fn check(
+    cx: &LateContext<'_>,
+    expr: &Expr<'_>,
+    recv: &Expr<'_>,
+    is_err: bool,
+    allow_unwrap_in_tests: bool,
+    variant: Variant,
+) {
+    let ty = cx.typeck_results().expr_ty(recv).peel_refs();
+
+    let (kind, none_value, none_prefix) = if is_type_diagnostic_item(cx, ty, sym::Option) && !is_err {
+        ("an `Option`", "None", "")
+    } else if is_type_diagnostic_item(cx, ty, sym::Result)
+        && let ty::Adt(_, substs) = ty.kind()
+        && let Some(t_or_e_ty) = substs[usize::from(!is_err)].as_type()
+    {
+        if is_never_like(t_or_e_ty) {
+            return;
+        }
+
+        ("a `Result`", if is_err { "Ok" } else { "Err" }, "an ")
+    } else {
+        return;
+    };
+
+    let method_suffix = if is_err { "_err" } else { "" };
+
+    if allow_unwrap_in_tests && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)) {
+        return;
+    }
+
+    span_lint_and_then(
+        cx,
+        variant.lint(),
+        expr.span,
+        &format!("used `{}()` on {kind} value", variant.method_name(is_err)),
+        |diag| {
+            diag.note(format!("if this value is {none_prefix}`{none_value}`, it will panic"));
+
+            if variant == Variant::Unwrap && is_lint_allowed(cx, EXPECT_USED, expr.hir_id) {
+                diag.help(format!(
+                    "consider using `expect{method_suffix}()` to provide a better panic message"
+                ));
+            }
+        },
+    );
+}
diff --git a/clippy_lints/src/methods/unwrap_used.rs b/clippy_lints/src/methods/unwrap_used.rs
deleted file mode 100644
index 5e4c3daee64..00000000000
--- a/clippy_lints/src/methods/unwrap_used.rs
+++ /dev/null
@@ -1,53 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{is_in_cfg_test, is_in_test_function, is_lint_allowed};
-use rustc_hir as hir;
-use rustc_lint::LateContext;
-use rustc_span::sym;
-
-use super::{EXPECT_USED, UNWRAP_USED};
-
-/// lint use of `unwrap()` or `unwrap_err` for `Result` and `unwrap()` for `Option`.
-pub(super) fn check(
-    cx: &LateContext<'_>,
-    expr: &hir::Expr<'_>,
-    recv: &hir::Expr<'_>,
-    is_err: bool,
-    allow_unwrap_in_tests: bool,
-) {
-    let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
-
-    let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err {
-        Some((UNWRAP_USED, "an `Option`", "None", ""))
-    } else if is_type_diagnostic_item(cx, obj_ty, sym::Result) {
-        Some((UNWRAP_USED, "a `Result`", if is_err { "Ok" } else { "Err" }, "an "))
-    } else {
-        None
-    };
-
-    let method_suffix = if is_err { "_err" } else { "" };
-
-    if allow_unwrap_in_tests && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)) {
-        return;
-    }
-
-    if let Some((lint, kind, none_value, none_prefix)) = mess {
-        let help = if is_lint_allowed(cx, EXPECT_USED, expr.hir_id) {
-            format!(
-                "if you don't want to handle the `{none_value}` case gracefully, consider \
-                using `expect{method_suffix}()` to provide a better panic message"
-            )
-        } else {
-            format!("if this value is {none_prefix}`{none_value}`, it will panic")
-        };
-
-        span_lint_and_help(
-            cx,
-            lint,
-            expr.span,
-            &format!("used `unwrap{method_suffix}()` on {kind} value"),
-            None,
-            &help,
-        );
-    }
-}
diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs
index 4b20aecad4a..e53e146ec5d 100644
--- a/clippy_lints/src/mut_reference.rs
+++ b/clippy_lints/src/mut_reference.rs
@@ -37,6 +37,11 @@ declare_lint_pass!(UnnecessaryMutPassed => [UNNECESSARY_MUT_PASSED]);
 
 impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
+        if e.span.from_expansion() {
+            // Issue #11268
+            return;
+        }
+
         match e.kind {
             ExprKind::Call(fn_expr, arguments) => {
                 if let ExprKind::Path(ref path) = fn_expr.kind {
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_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs
index 40da002f4ff..a7a7f4fd8fa 100644
--- a/clippy_lints/src/option_if_let_else.rs
+++ b/clippy_lints/src/option_if_let_else.rs
@@ -155,7 +155,7 @@ fn try_get_option_occurrence<'tcx>(
                 });
                 if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(local_id), .. })) = e.kind {
                     match some_captures.get(local_id)
-                        .or_else(|| (method_sugg == "map_or_else").then_some(()).and_then(|_| none_captures.get(local_id)))
+                        .or_else(|| (method_sugg == "map_or_else").then_some(()).and_then(|()| none_captures.get(local_id)))
                     {
                         Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None,
                         Some(CaptureKind::Ref(Mutability::Not)) if as_mut => return None,
diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs
index f5502cffb66..734ca2914f5 100644
--- a/clippy_lints/src/question_mark.rs
+++ b/clippy_lints/src/question_mark.rs
@@ -1,11 +1,13 @@
 use crate::manual_let_else::{MatchLintBehaviour, MANUAL_LET_ELSE};
+use crate::question_mark_used::QUESTION_MARK_USED;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::msrvs::Msrv;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{
-    eq_expr_value, get_parent_node, higher, in_constant, is_else_clause, is_path_lang_item, is_res_lang_ctor,
-    pat_and_expr_can_be_question_mark, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt,
+    eq_expr_value, get_parent_node, higher, in_constant, is_else_clause, is_lint_allowed, is_path_lang_item,
+    is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_to_local, path_to_local_id, peel_blocks,
+    peel_blocks_with_stmt,
 };
 use if_chain::if_chain;
 use rustc_errors::Applicability;
@@ -299,13 +301,17 @@ fn is_try_block(cx: &LateContext<'_>, bl: &rustc_hir::Block<'_>) -> bool {
 
 impl<'tcx> LateLintPass<'tcx> for QuestionMark {
     fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
+        if !is_lint_allowed(cx, QUESTION_MARK_USED, stmt.hir_id) {
+            return;
+        }
+
         if !in_constant(cx, stmt.hir_id) {
             check_let_some_else_return_none(cx, stmt);
         }
         self.check_manual_let_else(cx, stmt);
     }
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if !in_constant(cx, expr.hir_id) {
+        if !in_constant(cx, expr.hir_id) && is_lint_allowed(cx, QUESTION_MARK_USED, expr.hir_id) {
             self.check_is_none_or_err_and_early_return(cx, expr);
             self.check_if_let_some_or_err_and_early_return(cx, expr);
         }
diff --git a/clippy_lints/src/redundant_locals.rs b/clippy_lints/src/redundant_locals.rs
index 896bd79b20b..0c89c7ee47d 100644
--- a/clippy_lints/src/redundant_locals.rs
+++ b/clippy_lints/src/redundant_locals.rs
@@ -1,6 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::is_from_proc_macro;
 use clippy_utils::ty::needs_ordered_drop;
+use rustc_ast::Mutability;
 use rustc_hir::def::Res;
 use rustc_hir::{
     BindingAnnotation, ByRef, Expr, ExprKind, HirId, Local, Node, Pat, PatKind, QPath,
@@ -9,6 +10,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::{in_external_macro, is_from_async_await};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::symbol::Ident;
+use rustc_span::DesugaringKind;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -47,6 +49,7 @@ declare_lint_pass!(RedundantLocals => [REDUNDANT_LOCALS]);
 impl<'tcx> LateLintPass<'tcx> for RedundantLocals {
     fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
         if_chain! {
+            if !local.span.is_desugaring(DesugaringKind::Async);
             // the pattern is a single by-value binding
             if let PatKind::Binding(BindingAnnotation(ByRef::No, mutability), _, ident, None) = local.pat.kind;
             // the binding is not type-ascribed
@@ -62,6 +65,8 @@ impl<'tcx> LateLintPass<'tcx> for RedundantLocals {
             if let Node::Pat(binding_pat) = cx.tcx.hir().get(binding_id);
             // the previous binding has the same mutability
             if find_binding(binding_pat, ident).unwrap().1 == mutability;
+            // the local does not change the effect of assignments to the binding. see #11290
+            if !affects_assignments(cx, mutability, binding_id, local.hir_id);
             // the local does not affect the code's drop behavior
             if !affects_drop_behavior(cx, binding_id, local.hir_id, expr);
             // the local is user-controlled
@@ -97,6 +102,14 @@ fn find_binding(pat: &Pat<'_>, name: Ident) -> Option<BindingAnnotation> {
     ret
 }
 
+/// Check if a rebinding of a local changes the effect of assignments to the binding.
+fn affects_assignments(cx: &LateContext<'_>, mutability: Mutability, bind: HirId, rebind: HirId) -> bool {
+    let hir = cx.tcx.hir();
+
+    // the binding is mutable and the rebinding is in a different scope than the original binding
+    mutability == Mutability::Mut && hir.get_enclosing_scope(bind) != hir.get_enclosing_scope(rebind)
+}
+
 /// Check if a rebinding of a local affects the code's drop behavior.
 fn affects_drop_behavior<'tcx>(
     cx: &LateContext<'tcx>,
diff --git a/clippy_lints/src/redundant_type_annotations.rs b/clippy_lints/src/redundant_type_annotations.rs
index 8e9234bba3c..3e963d79892 100644
--- a/clippy_lints/src/redundant_type_annotations.rs
+++ b/clippy_lints/src/redundant_type_annotations.rs
@@ -1,6 +1,8 @@
 use clippy_utils::diagnostics::span_lint;
+use clippy_utils::is_lint_allowed;
 use rustc_ast::LitKind;
 use rustc_hir as hir;
+use rustc_hir::def::DefKind;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::Ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -45,8 +47,8 @@ fn is_same_type<'tcx>(cx: &LateContext<'tcx>, ty_resolved_path: hir::def::Res, f
         return primty.name() == func_return_type_sym;
     }
 
-    // type annotation is any other non generic type
-    if let hir::def::Res::Def(_, defid) = ty_resolved_path
+    // type annotation is a non generic type
+    if let hir::def::Res::Def(DefKind::Struct | DefKind::Union | DefKind::Enum, defid) = ty_resolved_path
         && let Some(annotation_ty) = cx.tcx.type_of(defid).no_bound_vars()
     {
         return annotation_ty == func_return_type;
@@ -130,8 +132,9 @@ fn extract_primty(ty_kind: &hir::TyKind<'_>) -> Option<hir::PrimTy> {
 
 impl LateLintPass<'_> for RedundantTypeAnnotations {
     fn check_local<'tcx>(&mut self, cx: &LateContext<'tcx>, local: &'tcx rustc_hir::Local<'tcx>) {
-        // type annotation part
-        if !local.span.from_expansion()
+        if !is_lint_allowed(cx, REDUNDANT_TYPE_ANNOTATIONS, local.hir_id)
+            // type annotation part
+            && !local.span.from_expansion()
             && let Some(ty) = &local.ty
 
             // initialization part
diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs
index 54a33eb2986..c9ab622ad25 100644
--- a/clippy_lints/src/slow_vector_initialization.rs
+++ b/clippy_lints/src/slow_vector_initialization.rs
@@ -20,18 +20,27 @@ declare_clippy_lint! {
     /// These structures are non-idiomatic and less efficient than simply using
     /// `vec![0; len]`.
     ///
+    /// Specifically, for `vec![0; len]`, the compiler can use a specialized type of allocation
+    /// that also zero-initializes the allocated memory in the same call
+    /// (see: [alloc_zeroed](https://doc.rust-lang.org/stable/std/alloc/trait.GlobalAlloc.html#method.alloc_zeroed)).
+    ///
+    /// Writing `Vec::new()` followed by `vec.resize(len, 0)` is suboptimal because,
+    /// while it does do the same number of allocations,
+    /// it involves two operations for allocating and initializing.
+    /// The `resize` call first allocates memory (since `Vec::new()` did not), and only *then* zero-initializes it.
+    ///
     /// ### Example
     /// ```rust
     /// # use core::iter::repeat;
     /// # let len = 4;
-    /// let mut vec1 = Vec::with_capacity(len);
+    /// let mut vec1 = Vec::new();
     /// vec1.resize(len, 0);
     ///
-    /// let mut vec1 = Vec::with_capacity(len);
-    /// vec1.resize(vec1.capacity(), 0);
-    ///
     /// let mut vec2 = Vec::with_capacity(len);
-    /// vec2.extend(repeat(0).take(len));
+    /// vec2.resize(len, 0);
+    ///
+    /// let mut vec3 = Vec::with_capacity(len);
+    /// vec3.extend(repeat(0).take(len));
     /// ```
     ///
     /// Use instead:
@@ -39,6 +48,7 @@ declare_clippy_lint! {
     /// # let len = 4;
     /// let mut vec1 = vec![0; len];
     /// let mut vec2 = vec![0; len];
+    /// let mut vec3 = vec![0; len];
     /// ```
     #[clippy::version = "1.32.0"]
     pub SLOW_VECTOR_INITIALIZATION,
diff --git a/clippy_lints/src/suspicious_xor_used_as_pow.rs b/clippy_lints/src/suspicious_xor_used_as_pow.rs
index 6fa49afe0ec..8e156b8829b 100644
--- a/clippy_lints/src/suspicious_xor_used_as_pow.rs
+++ b/clippy_lints/src/suspicious_xor_used_as_pow.rs
@@ -1,5 +1,7 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::numeric_literal::NumericLiteral;
-use clippy_utils::source::snippet_with_context;
+use clippy_utils::source::snippet;
+use rustc_ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -28,27 +30,29 @@ declare_lint_pass!(ConfusingXorAndPow => [SUSPICIOUS_XOR_USED_AS_POW]);
 
 impl LateLintPass<'_> for ConfusingXorAndPow {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
-        if !in_external_macro(cx.sess(), expr.span) &&
-        	let ExprKind::Binary(op, left, right) = &expr.kind &&
-            op.node == BinOpKind::BitXor &&
-            left.span.ctxt() == right.span.ctxt() &&
-            let ExprKind::Lit(lit_left) = &left.kind &&
-            let ExprKind::Lit(lit_right) = &right.kind &&
-            let snip_left = snippet_with_context(cx, lit_left.span, lit_left.span.ctxt(), "..", &mut Applicability::MaybeIncorrect) &&
-            let snip_right = snippet_with_context(cx, lit_right.span, lit_right.span.ctxt(), "..", &mut Applicability::MaybeIncorrect) &&
-            let Some(left_val) = NumericLiteral::from_lit_kind(&snip_left.0, &lit_left.node) &&
-            let Some(right_val) = NumericLiteral::from_lit_kind(&snip_right.0, &lit_right.node) &&
-			left_val.is_decimal() &&
-			right_val.is_decimal() {
-					clippy_utils::diagnostics::span_lint_and_sugg(
-					        cx,
-					        SUSPICIOUS_XOR_USED_AS_POW,
-					        expr.span,
-					        "`^` is not the exponentiation operator",
-					        "did you mean to write",
-					        format!("{}.pow({})", left_val.format(), right_val.format()),
-					        Applicability::MaybeIncorrect,
-					    );
+        if !in_external_macro(cx.sess(), expr.span)
+            && let ExprKind::Binary(op, left, right) = &expr.kind
+            && op.node == BinOpKind::BitXor
+            && left.span.ctxt() == right.span.ctxt()
+            && let ExprKind::Lit(lit_left) = &left.kind
+            && let ExprKind::Lit(lit_right) = &right.kind
+            && matches!(lit_right.node, LitKind::Int(..) | LitKind::Float(..))
+            && matches!(lit_left.node, LitKind::Int(..) | LitKind::Float(..))
+            && NumericLiteral::from_lit_kind(&snippet(cx, lit_right.span, ".."), &lit_right.node).is_some_and(|x| x.is_decimal())
+            {
+                span_lint_and_sugg(
+                    cx,
+                    SUSPICIOUS_XOR_USED_AS_POW,
+                    expr.span,
+                    "`^` is not the exponentiation operator",
+                    "did you mean to write",
+                    format!(
+                        "{}.pow({})",
+                        lit_left.node,
+                        lit_right.node
+                    ),
+                    Applicability::MaybeIncorrect,
+                );
 		}
     }
 }
diff --git a/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs
index e4906944c8d..4ed985f54d0 100644
--- a/clippy_lints/src/utils/internal_lints/invalid_paths.rs
+++ b/clippy_lints/src/utils/internal_lints/invalid_paths.rs
@@ -34,7 +34,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
         let mod_name = &cx.tcx.item_name(local_def_id.to_def_id());
         if_chain! {
             if mod_name.as_str() == "paths";
-            if let hir::ItemKind::Const(ty, body_id) = item.kind;
+            if let hir::ItemKind::Const(ty, _, body_id) = item.kind;
             let ty = hir_ty_to_ty(cx.tcx, ty);
             if let ty::Array(el_ty, _) = &ty.kind();
             if let ty::Ref(_, el_ty, _) = &el_ty.kind();