about summary refs log tree commit diff
path: root/clippy_lints
diff options
context:
space:
mode:
Diffstat (limited to 'clippy_lints')
-rw-r--r--clippy_lints/src/attrs.rs74
-rw-r--r--clippy_lints/src/casts/mod.rs2
-rw-r--r--clippy_lints/src/declared_lints.rs3
-rw-r--r--clippy_lints/src/endian_bytes.rs6
-rw-r--r--clippy_lints/src/excessive_nesting.rs2
-rw-r--r--clippy_lints/src/implied_bounds_in_impls.rs202
-rw-r--r--clippy_lints/src/incorrect_impls.rs10
-rw-r--r--clippy_lints/src/large_stack_frames.rs2
-rw-r--r--clippy_lints/src/lib.rs4
-rw-r--r--clippy_lints/src/macro_use.rs3
-rw-r--r--clippy_lints/src/manual_retain.rs6
-rw-r--r--clippy_lints/src/methods/implicit_clone.rs1
-rw-r--r--clippy_lints/src/methods/iter_overeager_cloned.rs126
-rw-r--r--clippy_lints/src/methods/mod.rs42
-rw-r--r--clippy_lints/src/methods/or_fun_call.rs19
-rw-r--r--clippy_lints/src/methods/unnecessary_sort_by.rs3
-rw-r--r--clippy_lints/src/mutable_debug_assertion.rs4
-rw-r--r--clippy_lints/src/needless_else.rs2
-rw-r--r--clippy_lints/src/new_without_default.rs21
-rw-r--r--clippy_lints/src/redundant_locals.rs16
-rw-r--r--clippy_lints/src/redundant_type_annotations.rs2
-rw-r--r--clippy_lints/src/reserve_after_initialization.rs134
-rw-r--r--clippy_lints/src/same_name_method.rs10
-rw-r--r--clippy_lints/src/tuple_array_conversions.rs4
24 files changed, 622 insertions, 76 deletions
diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs
index 2a5be275615..a88f2b51c82 100644
--- a/clippy_lints/src/attrs.rs
+++ b/clippy_lints/src/attrs.rs
@@ -6,7 +6,11 @@ use clippy_utils::macros::{is_panic, macro_backtrace};
 use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments};
 use if_chain::if_chain;
-use rustc_ast::{AttrKind, AttrStyle, Attribute, LitKind, MetaItemKind, MetaItemLit, NestedMetaItem};
+use rustc_ast::token::{Token, TokenKind};
+use rustc_ast::tokenstream::TokenTree;
+use rustc_ast::{
+    AttrArgs, AttrArgsEq, AttrKind, AttrStyle, Attribute, LitKind, MetaItemKind, MetaItemLit, NestedMetaItem,
+};
 use rustc_errors::Applicability;
 use rustc_hir::{
     Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind,
@@ -341,6 +345,41 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
+    /// Checks for `#[should_panic]` attributes without specifying the expected panic message.
+    ///
+    /// ### Why is this bad?
+    /// The expected panic message should be specified to ensure that the test is actually
+    /// panicking with the expected message, and not another unrelated panic.
+    ///
+    /// ### Example
+    /// ```rust
+    /// fn random() -> i32 { 0 }
+    ///
+    /// #[should_panic]
+    /// #[test]
+    /// fn my_test() {
+    ///     let _ = 1 / random();
+    /// }
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// fn random() -> i32 { 0 }
+    ///
+    /// #[should_panic = "attempt to divide by zero"]
+    /// #[test]
+    /// fn my_test() {
+    ///     let _ = 1 / random();
+    /// }
+    /// ```
+    #[clippy::version = "1.73.0"]
+    pub SHOULD_PANIC_WITHOUT_EXPECT,
+    pedantic,
+    "ensures that all `should_panic` attributes specify its expected panic message"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
     /// Checks for `any` and `all` combinators in `cfg` with only one condition.
     ///
     /// ### Why is this bad?
@@ -395,6 +434,7 @@ declare_lint_pass!(Attributes => [
     DEPRECATED_SEMVER,
     USELESS_ATTRIBUTE,
     BLANKET_CLIPPY_RESTRICTION_LINTS,
+    SHOULD_PANIC_WITHOUT_EXPECT,
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Attributes {
@@ -442,6 +482,9 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
                 }
             }
         }
+        if attr.has_name(sym::should_panic) {
+            check_should_panic_reason(cx, attr);
+        }
     }
 
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
@@ -550,6 +593,35 @@ fn extract_clippy_lint(lint: &NestedMetaItem) -> Option<Symbol> {
     None
 }
 
+fn check_should_panic_reason(cx: &LateContext<'_>, attr: &Attribute) {
+    if let AttrKind::Normal(normal_attr) = &attr.kind {
+        if let AttrArgs::Eq(_, AttrArgsEq::Hir(_)) = &normal_attr.item.args {
+            // `#[should_panic = ".."]` found, good
+            return;
+        }
+
+        if let AttrArgs::Delimited(args) = &normal_attr.item.args
+            && let mut tt_iter = args.tokens.trees()
+            && let Some(TokenTree::Token(Token { kind: TokenKind::Ident(sym::expected, _), .. }, _)) = tt_iter.next()
+            && let Some(TokenTree::Token(Token { kind: TokenKind::Eq, .. }, _)) = tt_iter.next()
+            && let Some(TokenTree::Token(Token { kind: TokenKind::Literal(_), .. }, _)) = tt_iter.next()
+        {
+            // `#[should_panic(expected = "..")]` found, good
+            return;
+        }
+
+        span_lint_and_sugg(
+            cx,
+            SHOULD_PANIC_WITHOUT_EXPECT,
+            attr.span,
+            "#[should_panic] attribute without a reason",
+            "consider specifying the expected panic",
+            r#"#[should_panic(expected = /* panic message */)]"#.into(),
+            Applicability::HasPlaceholders,
+        );
+    }
+}
+
 fn check_clippy_lint_names(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem]) {
     for lint in items {
         if let Some(lint_name) = extract_clippy_lint(lint) {
diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs
index d34de305f5d..88ffbb55486 100644
--- a/clippy_lints/src/casts/mod.rs
+++ b/clippy_lints/src/casts/mod.rs
@@ -418,7 +418,7 @@ declare_clippy_lint! {
     /// let mut_ptr = ptr.cast_mut();
     /// let ptr = mut_ptr.cast_const();
     /// ```
-    #[clippy::version = "1.71.0"]
+    #[clippy::version = "1.72.0"]
     pub PTR_CAST_CONSTNESS,
     pedantic,
     "casting using `as` from and to raw pointers to change constness when specialized methods apply"
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index db114abfc86..f73f1ed18f8 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -58,6 +58,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::attrs::MAYBE_MISUSED_CFG_INFO,
     crate::attrs::MISMATCHED_TARGET_OS_INFO,
     crate::attrs::NON_MINIMAL_CFG_INFO,
+    crate::attrs::SHOULD_PANIC_WITHOUT_EXPECT_INFO,
     crate::attrs::USELESS_ATTRIBUTE_INFO,
     crate::await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE_INFO,
     crate::await_holding_invalid::AWAIT_HOLDING_LOCK_INFO,
@@ -208,6 +209,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::implicit_return::IMPLICIT_RETURN_INFO,
     crate::implicit_saturating_add::IMPLICIT_SATURATING_ADD_INFO,
     crate::implicit_saturating_sub::IMPLICIT_SATURATING_SUB_INFO,
+    crate::implied_bounds_in_impls::IMPLIED_BOUNDS_IN_IMPLS_INFO,
     crate::inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR_INFO,
     crate::incorrect_impls::INCORRECT_CLONE_IMPL_ON_COPY_TYPE_INFO,
     crate::incorrect_impls::INCORRECT_PARTIAL_ORD_IMPL_ON_ORD_TYPE_INFO,
@@ -578,6 +580,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::reference::DEREF_ADDROF_INFO,
     crate::regex::INVALID_REGEX_INFO,
     crate::regex::TRIVIAL_REGEX_INFO,
+    crate::reserve_after_initialization::RESERVE_AFTER_INITIALIZATION_INFO,
     crate::return_self_not_must_use::RETURN_SELF_NOT_MUST_USE_INFO,
     crate::returns::LET_AND_RETURN_INFO,
     crate::returns::NEEDLESS_RETURN_INFO,
diff --git a/clippy_lints/src/endian_bytes.rs b/clippy_lints/src/endian_bytes.rs
index dda14b4df53..affd0822120 100644
--- a/clippy_lints/src/endian_bytes.rs
+++ b/clippy_lints/src/endian_bytes.rs
@@ -21,7 +21,7 @@ declare_clippy_lint! {
     /// let _x = 2i32.to_ne_bytes();
     /// let _y = 2i64.to_ne_bytes();
     /// ```
-    #[clippy::version = "1.71.0"]
+    #[clippy::version = "1.72.0"]
     pub HOST_ENDIAN_BYTES,
     restriction,
     "disallows usage of the `to_ne_bytes` method"
@@ -40,7 +40,7 @@ declare_clippy_lint! {
     /// let _x = 2i32.to_le_bytes();
     /// let _y = 2i64.to_le_bytes();
     /// ```
-    #[clippy::version = "1.71.0"]
+    #[clippy::version = "1.72.0"]
     pub LITTLE_ENDIAN_BYTES,
     restriction,
     "disallows usage of the `to_le_bytes` method"
@@ -59,7 +59,7 @@ declare_clippy_lint! {
     /// let _x = 2i32.to_be_bytes();
     /// let _y = 2i64.to_be_bytes();
     /// ```
-    #[clippy::version = "1.71.0"]
+    #[clippy::version = "1.72.0"]
     pub BIG_ENDIAN_BYTES,
     restriction,
     "disallows usage of the `to_be_bytes` method"
diff --git a/clippy_lints/src/excessive_nesting.rs b/clippy_lints/src/excessive_nesting.rs
index 8911f1872c2..83480fc5eeb 100644
--- a/clippy_lints/src/excessive_nesting.rs
+++ b/clippy_lints/src/excessive_nesting.rs
@@ -56,7 +56,7 @@ declare_clippy_lint! {
     /// // lib.rs
     /// pub mod a;
     /// ```
-    #[clippy::version = "1.70.0"]
+    #[clippy::version = "1.72.0"]
     pub EXCESSIVE_NESTING,
     complexity,
     "checks for blocks nested beyond a certain threshold"
diff --git a/clippy_lints/src/implied_bounds_in_impls.rs b/clippy_lints/src/implied_bounds_in_impls.rs
new file mode 100644
index 00000000000..c6d1acad3a0
--- /dev/null
+++ b/clippy_lints/src/implied_bounds_in_impls.rs
@@ -0,0 +1,202 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet;
+use rustc_errors::{Applicability, SuggestionStyle};
+use rustc_hir::def_id::LocalDefId;
+use rustc_hir::intravisit::FnKind;
+use rustc_hir::{
+    Body, FnDecl, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind, ItemKind, TraitBoundModifier, TraitItem,
+    TraitItemKind, TyKind,
+};
+use rustc_hir_analysis::hir_ty_to_ty;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::{self, ClauseKind, TyCtxt};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::Span;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Looks for bounds in `impl Trait` in return position that are implied by other bounds.
+    /// This can happen when a trait is specified that another trait already has as a supertrait
+    /// (e.g. `fn() -> impl Deref + DerefMut<Target = i32>` has an unnecessary `Deref` bound,
+    /// because `Deref` is a supertrait of `DerefMut`)
+    ///
+    /// ### Why is this bad?
+    /// Specifying more bounds than necessary adds needless complexity for the reader.
+    ///
+    /// ### Limitations
+    /// This lint does not check for implied bounds transitively. Meaning that
+    /// it does't check for implied bounds from supertraits of supertraits
+    /// (e.g. `trait A {} trait B: A {} trait C: B {}`, then having an `fn() -> impl A + C`)
+    ///
+    /// ### Example
+    /// ```rust
+    /// # use std::ops::{Deref,DerefMut};
+    /// fn f() -> impl Deref<Target = i32> + DerefMut<Target = i32> {
+    /// //             ^^^^^^^^^^^^^^^^^^^ unnecessary bound, already implied by the `DerefMut` trait bound
+    ///     Box::new(123)
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # use std::ops::{Deref,DerefMut};
+    /// fn f() -> impl DerefMut<Target = i32> {
+    ///     Box::new(123)
+    /// }
+    /// ```
+    #[clippy::version = "1.73.0"]
+    pub IMPLIED_BOUNDS_IN_IMPLS,
+    complexity,
+    "specifying bounds that are implied by other bounds in `impl Trait` type"
+}
+declare_lint_pass!(ImpliedBoundsInImpls => [IMPLIED_BOUNDS_IN_IMPLS]);
+
+/// This function tries to, for all type parameters in a supertype predicate `GenericTrait<U>`,
+/// check if the substituted type in the implied-by bound matches with what's subtituted in the
+/// implied type.
+///
+/// Consider this example.
+/// ```rust,ignore
+/// trait GenericTrait<T> {}
+/// trait GenericSubTrait<T, U, V>: GenericTrait<U> {}
+///                                              ^ trait_predicate_args: [Self#0, U#2]
+/// impl GenericTrait<i32> for () {}
+/// impl GenericSubTrait<(), i32, ()> for () {}
+/// impl GenericSubTrait<(), [u8; 8], ()> for () {}
+///
+/// fn f() -> impl GenericTrait<i32> + GenericSubTrait<(), [u8; 8], ()> {
+///                             ^^^ implied_args       ^^^^^^^^^^^^^^^ implied_by_args
+///                                                                    (we are interested in `[u8; 8]` specifically, as that
+///                                                                     is what `U` in `GenericTrait<U>` is substituted with)
+///     ()
+/// }
+/// ```
+/// Here i32 != [u8; 8], so this will return false.
+fn is_same_generics(
+    tcx: TyCtxt<'_>,
+    trait_predicate_args: &[ty::GenericArg<'_>],
+    implied_by_args: &[GenericArg<'_>],
+    implied_args: &[GenericArg<'_>],
+) -> bool {
+    trait_predicate_args
+        .iter()
+        .enumerate()
+        .skip(1) // skip `Self` implicit arg
+        .all(|(arg_index, arg)| {
+            if let Some(ty) = arg.as_type()
+                && let &ty::Param(ty::ParamTy{ index, .. }) = ty.kind()
+                // Since `trait_predicate_args` and type params in traits start with `Self=0`
+                // and generic argument lists `GenericTrait<i32>` don't have `Self`,
+                // we need to subtract 1 from the index.
+                && let GenericArg::Type(ty_a) = implied_by_args[index as usize - 1]
+                && let GenericArg::Type(ty_b) = implied_args[arg_index - 1]
+            {
+                hir_ty_to_ty(tcx, ty_a) == hir_ty_to_ty(tcx, ty_b)
+            } else {
+                false
+            }
+        })
+}
+
+fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
+    if let FnRetTy::Return(ty) = decl.output
+        &&let TyKind::OpaqueDef(item_id, ..) = ty.kind
+        && let item = cx.tcx.hir().item(item_id)
+        && let ItemKind::OpaqueTy(opaque_ty) = item.kind
+        // Very often there is only a single bound, e.g. `impl Deref<..>`, in which case
+        // we can avoid doing a bunch of stuff unnecessarily.
+        && opaque_ty.bounds.len() > 1
+    {
+        // Get all the (implied) trait predicates in the bounds.
+        // For `impl Deref + DerefMut` this will contain [`Deref`].
+        // The implied `Deref` comes from `DerefMut` because `trait DerefMut: Deref {}`.
+        // N.B. (G)ATs are fine to disregard, because they must be the same for all of its supertraits.
+        // Example:
+        // `impl Deref<Target = i32> + DerefMut<Target = u32>` is not allowed.
+        // `DerefMut::Target` needs to match `Deref::Target`.
+        let implied_bounds: Vec<_> = opaque_ty.bounds.iter().filter_map(|bound| {
+            if let GenericBound::Trait(poly_trait, TraitBoundModifier::None) = bound
+                && let [.., path]  = poly_trait.trait_ref.path.segments
+                && poly_trait.bound_generic_params.is_empty()
+                && let Some(trait_def_id) = path.res.opt_def_id()
+                && let predicates = cx.tcx.super_predicates_of(trait_def_id).predicates
+                && !predicates.is_empty() // If the trait has no supertrait, there is nothing to add.
+            {
+                Some((bound.span(), path.args.map_or([].as_slice(), |a| a.args), predicates))
+            } else {
+                None
+            }
+        }).collect();
+
+        // Lint all bounds in the `impl Trait` type that are also in the `implied_bounds` vec.
+        // This involves some extra logic when generic arguments are present, since
+        // simply comparing trait `DefId`s won't be enough. We also need to compare the generics.
+        for (index, bound) in opaque_ty.bounds.iter().enumerate() {
+            if let GenericBound::Trait(poly_trait, TraitBoundModifier::None) = bound
+                && let [.., path] = poly_trait.trait_ref.path.segments
+                && let implied_args = path.args.map_or([].as_slice(), |a| a.args)
+                && let Some(def_id) = poly_trait.trait_ref.path.res.opt_def_id()
+                && let Some(implied_by_span) = implied_bounds.iter().find_map(|&(span, implied_by_args, preds)| {
+                    preds.iter().find_map(|(clause, _)| {
+                        if let ClauseKind::Trait(tr) = clause.kind().skip_binder()
+                            && tr.def_id() == def_id
+                            && is_same_generics(cx.tcx, tr.trait_ref.args, implied_by_args, implied_args)
+                        {
+                            Some(span)
+                        } else {
+                            None
+                        }
+                    })
+                })
+            {
+                let implied_by = snippet(cx, implied_by_span, "..");
+                span_lint_and_then(
+                    cx, IMPLIED_BOUNDS_IN_IMPLS,
+                    poly_trait.span,
+                    &format!("this bound is already specified as the supertrait of `{implied_by}`"),
+                    |diag| {
+                        // If we suggest removing a bound, we may also need extend the span
+                        // to include the `+` token, so we don't end up with something like `impl + B`
+
+                        let implied_span_extended = if let Some(next_bound) = opaque_ty.bounds.get(index + 1) {
+                            poly_trait.span.to(next_bound.span().shrink_to_lo())
+                        } else {
+                            poly_trait.span
+                        };
+
+                        diag.span_suggestion_with_style(
+                            implied_span_extended,
+                            "try removing this bound",
+                            "",
+                            Applicability::MachineApplicable,
+                            SuggestionStyle::ShowAlways
+                        );
+                    }
+                );
+            }
+        }
+    }
+}
+
+impl LateLintPass<'_> for ImpliedBoundsInImpls {
+    fn check_fn(
+        &mut self,
+        cx: &LateContext<'_>,
+        _: FnKind<'_>,
+        decl: &FnDecl<'_>,
+        _: &Body<'_>,
+        _: Span,
+        _: LocalDefId,
+    ) {
+        check(cx, decl);
+    }
+    fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) {
+        if let TraitItemKind::Fn(sig, ..) = &item.kind {
+            check(cx, sig.decl);
+        }
+    }
+    fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &ImplItem<'_>) {
+        if let ImplItemKind::Fn(sig, ..) = &item.kind {
+            check(cx, sig.decl);
+        }
+    }
+}
diff --git a/clippy_lints/src/incorrect_impls.rs b/clippy_lints/src/incorrect_impls.rs
index 3c59b839a39..c19a46f4e97 100644
--- a/clippy_lints/src/incorrect_impls.rs
+++ b/clippy_lints/src/incorrect_impls.rs
@@ -5,6 +5,7 @@ use clippy_utils::{get_parent_node, is_res_lang_ctor, last_path_segment, match_d
 use rustc_errors::Applicability;
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, ItemKind, LangItem, Node, UnOp};
+use rustc_hir_analysis::hir_ty_to_ty;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::EarlyBinder;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -125,7 +126,7 @@ impl LateLintPass<'_> for IncorrectImpls {
         if cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) {
             return;
         }
-        let ItemKind::Impl(_) = item.kind else {
+        let ItemKind::Impl(imp) = item.kind else {
             return;
         };
         let ImplItemKind::Fn(_, impl_item_id) = cx.tcx.hir().impl_item(impl_item.impl_item_id()).kind else {
@@ -188,12 +189,7 @@ impl LateLintPass<'_> for IncorrectImpls {
                 .diagnostic_items(trait_impl.def_id.krate)
                 .name_to_id
                 .get(&sym::Ord)
-            && implements_trait(
-                    cx,
-                    trait_impl.self_ty(),
-                    *ord_def_id,
-                    &[],
-                )
+            && implements_trait(cx, hir_ty_to_ty(cx.tcx, imp.self_ty), *ord_def_id, &[])
         {
             // If the `cmp` call likely needs to be fully qualified in the suggestion
             // (like `std::cmp::Ord::cmp`). It's unfortunate we must put this here but we can't
diff --git a/clippy_lints/src/large_stack_frames.rs b/clippy_lints/src/large_stack_frames.rs
index 7aa1446d53d..1d28d7dd0e7 100644
--- a/clippy_lints/src/large_stack_frames.rs
+++ b/clippy_lints/src/large_stack_frames.rs
@@ -72,7 +72,7 @@ declare_clippy_lint! {
     /// //  ...
     /// }
     /// ```
-    #[clippy::version = "1.71.0"]
+    #[clippy::version = "1.72.0"]
     pub LARGE_STACK_FRAMES,
     nursery,
     "checks for functions that allocate a lot of stack space"
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 358004cf460..ab71bfbdc58 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -152,6 +152,7 @@ mod implicit_hasher;
 mod implicit_return;
 mod implicit_saturating_add;
 mod implicit_saturating_sub;
+mod implied_bounds_in_impls;
 mod inconsistent_struct_constructor;
 mod incorrect_impls;
 mod index_refutable_slice;
@@ -285,6 +286,7 @@ mod ref_option_ref;
 mod ref_patterns;
 mod reference;
 mod regex;
+mod reserve_after_initialization;
 mod return_self_not_must_use;
 mod returns;
 mod same_name_method;
@@ -1095,6 +1097,8 @@ 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));
+    store.register_late_pass(|_| Box::<reserve_after_initialization::ReserveAfterInitialization>::default());
+    store.register_late_pass(|_| Box::new(implied_bounds_in_impls::ImpliedBoundsInImpls));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs
index 9b158f18f62..70c5182472a 100644
--- a/clippy_lints/src/macro_use.rs
+++ b/clippy_lints/src/macro_use.rs
@@ -36,7 +36,8 @@ struct PathAndSpan {
     span: Span,
 }
 
-/// `MacroRefData` includes the name of the macro.
+/// `MacroRefData` includes the name of the macro
+/// and the path from `SourceMap::span_to_filename`.
 #[derive(Debug, Clone)]
 pub struct MacroRefData {
     name: String,
diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs
index 5259066eb71..1a69a48c582 100644
--- a/clippy_lints/src/manual_retain.rs
+++ b/clippy_lints/src/manual_retain.rs
@@ -12,13 +12,15 @@ use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::symbol::sym;
 
-const ACCEPTABLE_METHODS: [&[&str]; 4] = [
+const ACCEPTABLE_METHODS: [&[&str]; 5] = [
+    &paths::BINARYHEAP_ITER,
     &paths::HASHSET_ITER,
     &paths::BTREESET_ITER,
     &paths::SLICE_INTO,
     &paths::VEC_DEQUE_ITER,
 ];
-const ACCEPTABLE_TYPES: [(rustc_span::Symbol, Option<RustcVersion>); 6] = [
+const ACCEPTABLE_TYPES: [(rustc_span::Symbol, Option<RustcVersion>); 7] = [
+    (sym::BinaryHeap, Some(msrvs::BINARY_HEAP_RETAIN)),
     (sym::BTreeSet, Some(msrvs::BTREE_SET_RETAIN)),
     (sym::BTreeMap, Some(msrvs::BTREE_MAP_RETAIN)),
     (sym::HashSet, Some(msrvs::HASH_SET_RETAIN)),
diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs
index 043425300d8..e91ce64d8c8 100644
--- a/clippy_lints/src/methods/implicit_clone.rs
+++ b/clippy_lints/src/methods/implicit_clone.rs
@@ -17,6 +17,7 @@ pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv
         let return_type = cx.typeck_results().expr_ty(expr);
         let input_type = cx.typeck_results().expr_ty(recv);
         let (input_type, ref_count) = peel_mid_ty_refs(input_type);
+        if !(ref_count > 0 && is_diag_trait_item(cx, method_def_id, sym::ToOwned));
         if let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did()));
         if return_type == input_type;
         if let Some(clone_trait) = cx.tcx.lang_items().clone_trait();
diff --git a/clippy_lints/src/methods/iter_overeager_cloned.rs b/clippy_lints/src/methods/iter_overeager_cloned.rs
index 9f7ec19aa59..ee405a3e30c 100644
--- a/clippy_lints/src/methods/iter_overeager_cloned.rs
+++ b/clippy_lints/src/methods/iter_overeager_cloned.rs
@@ -1,21 +1,45 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::{implements_trait, is_copy};
+use rustc_ast::BindingAnnotation;
 use rustc_errors::Applicability;
-use rustc_hir::Expr;
+use rustc_hir::{Body, Expr, ExprKind, HirId, HirIdSet, PatKind};
+use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 use rustc_lint::LateContext;
-use rustc_middle::ty;
+use rustc_middle::mir::{FakeReadCause, Mutability};
+use rustc_middle::ty::{self, BorrowKind};
 use rustc_span::sym;
 
 use super::ITER_OVEREAGER_CLONED;
 use crate::redundant_clone::REDUNDANT_CLONE;
+use crate::rustc_trait_selection::infer::TyCtxtInferExt;
+
+#[derive(Clone, Copy)]
+pub(super) enum Op<'a> {
+    // rm `.cloned()`
+    // e.g. `count`
+    RmCloned,
+
+    // rm `.cloned()`
+    // e.g. `map` `for_each`
+    NeedlessMove(&'a str, &'a Expr<'a>),
+
+    // later `.cloned()`
+    // and add `&` to the parameter of closure parameter
+    // e.g. `find` `filter`
+    FixClosure(&'a str, &'a Expr<'a>),
+
+    // later `.cloned()`
+    // e.g. `skip` `take`
+    LaterCloned,
+}
 
 pub(super) fn check<'tcx>(
     cx: &LateContext<'tcx>,
     expr: &'tcx Expr<'_>,
     cloned_call: &'tcx Expr<'_>,
     cloned_recv: &'tcx Expr<'_>,
-    is_count: bool,
+    op: Op<'tcx>,
     needs_into_iter: bool,
 ) {
     let typeck = cx.typeck_results();
@@ -35,10 +59,47 @@ pub(super) fn check<'tcx>(
             return;
         }
 
-        let (lint, msg, trailing_clone) = if is_count {
-            (REDUNDANT_CLONE, "unneeded cloning of iterator items", "")
-        } else {
-            (ITER_OVEREAGER_CLONED, "unnecessarily eager cloning of iterator items", ".cloned()")
+        if let Op::NeedlessMove(_, expr) = op {
+            let rustc_hir::ExprKind::Closure(closure) = expr.kind else { return } ;
+            let body @ Body { params: [p], .. } = cx.tcx.hir().body(closure.body) else { return };
+            let mut delegate = MoveDelegate {used_move : HirIdSet::default()};
+            let infcx = cx.tcx.infer_ctxt().build();
+
+            ExprUseVisitor::new(
+                &mut delegate,
+                &infcx,
+                closure.body.hir_id.owner.def_id,
+                cx.param_env,
+                cx.typeck_results(),
+            )
+            .consume_body(body);
+
+            let mut to_be_discarded = false;
+
+            p.pat.walk(|it| {
+                if delegate.used_move.contains(&it.hir_id){
+                    to_be_discarded = true;
+                    return false;
+                }
+
+                match it.kind {
+                    PatKind::Binding(BindingAnnotation(_, Mutability::Mut), _, _, _)
+                    | PatKind::Ref(_, Mutability::Mut) => {
+                        to_be_discarded = true;
+                        false
+                    }
+                    _ => { true }
+                }
+            });
+
+            if to_be_discarded {
+                return;
+            }
+        }
+
+        let (lint, msg, trailing_clone) = match op {
+            Op::RmCloned | Op::NeedlessMove(_, _) => (REDUNDANT_CLONE, "unneeded cloning of iterator items", ""),
+            Op::LaterCloned | Op::FixClosure(_, _) => (ITER_OVEREAGER_CLONED, "unnecessarily eager cloning of iterator items", ".cloned()"),
         };
 
         span_lint_and_then(
@@ -47,13 +108,54 @@ pub(super) fn check<'tcx>(
             expr.span,
             msg,
             |diag| {
-                let method_span = expr.span.with_lo(cloned_call.span.hi());
-                if let Some(mut snip) = snippet_opt(cx, method_span) {
-                    snip.push_str(trailing_clone);
-                    let replace_span = expr.span.with_lo(cloned_recv.span.hi());
-                    diag.span_suggestion(replace_span, "try", snip, Applicability::MachineApplicable);
+                match op {
+                    Op::RmCloned | Op::LaterCloned => {
+                        let method_span = expr.span.with_lo(cloned_call.span.hi());
+                        if let Some(mut snip) = snippet_opt(cx, method_span) {
+                            snip.push_str(trailing_clone);
+                            let replace_span = expr.span.with_lo(cloned_recv.span.hi());
+                            diag.span_suggestion(replace_span, "try", snip, Applicability::MachineApplicable);
+                        }
+                    }
+                    Op::FixClosure(name, predicate_expr) => {
+                        if let Some(predicate) = snippet_opt(cx, predicate_expr.span) {
+                            let new_closure = if let ExprKind::Closure(_) = predicate_expr.kind {
+                                predicate.replacen('|', "|&", 1)
+                            } else {
+                                format!("|&x| {predicate}(x)")
+                            };
+                            let snip = format!(".{name}({new_closure}).cloned()" );
+                            let replace_span = expr.span.with_lo(cloned_recv.span.hi());
+                            diag.span_suggestion(replace_span, "try", snip, Applicability::MachineApplicable);
+                        }
+                    }
+                    Op::NeedlessMove(_, _) => {
+                        let method_span = expr.span.with_lo(cloned_call.span.hi());
+                        if let Some(snip) = snippet_opt(cx, method_span) {
+                            let replace_span = expr.span.with_lo(cloned_recv.span.hi());
+                            diag.span_suggestion(replace_span, "try", snip, Applicability::MaybeIncorrect);
+                        }
+                    }
                 }
             }
         );
     }
 }
+
+struct MoveDelegate {
+    used_move: HirIdSet,
+}
+
+impl<'tcx> Delegate<'tcx> for MoveDelegate {
+    fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, _: HirId) {
+        if let PlaceBase::Local(l) = place_with_id.place.base {
+            self.used_move.insert(l);
+        }
+    }
+
+    fn borrow(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId, _: BorrowKind) {}
+
+    fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
+
+    fn fake_read(&mut self, _: &PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {}
+}
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index 42756b27d01..5075137caa0 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -301,7 +301,7 @@ declare_clippy_lint! {
     /// let val2 = 1;
     /// let val3 = 1;
     /// ```
-    #[clippy::version = "1.69.0"]
+    #[clippy::version = "1.72.0"]
     pub UNNECESSARY_LITERAL_UNWRAP,
     complexity,
     "using `unwrap()` related calls on `Result` and `Option` constructors"
@@ -3328,7 +3328,7 @@ declare_clippy_lint! {
     ///     mem::take(v)
     /// }
     /// ```
-    #[clippy::version = "1.71.0"]
+    #[clippy::version = "1.72.0"]
     pub DRAIN_COLLECT,
     perf,
     "calling `.drain(..).collect()` to move all elements into a new collection"
@@ -3919,7 +3919,7 @@ impl Methods {
                     }
                 },
                 ("count", []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) {
-                    Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, true, false),
+                    Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::RmCloned , false),
                     Some((name2 @ ("into_iter" | "iter" | "iter_mut"), recv2, [], _, _)) => {
                         iter_count::check(cx, expr, recv2, name2);
                     },
@@ -3973,6 +3973,13 @@ impl Methods {
                     string_extend_chars::check(cx, expr, recv, arg);
                     extend_with_drain::check(cx, expr, recv, arg);
                 },
+                (name @ ( "filter" | "find" ) , [arg]) => {
+                    if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
+                        // if `arg` has side-effect, the semantic will change
+                        iter_overeager_cloned::check(cx, expr, recv, recv2,
+                                iter_overeager_cloned::Op::FixClosure(name, arg), false);
+                    }
+                }
                 ("filter_map", [arg]) => {
                     unnecessary_filter_map::check(cx, expr, arg, name);
                     filter_map_bool_then::check(cx, expr, arg, call_span);
@@ -3987,16 +3994,18 @@ impl Methods {
                 },
                 ("flatten", []) => match method_call(recv) {
                     Some(("map", recv, [map_arg], map_span, _)) => map_flatten::check(cx, expr, recv, map_arg, map_span),
-                    Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, true),
+                    Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::LaterCloned , true),
                     _ => {},
                 },
                 ("fold", [init, acc]) => {
                     manual_try_fold::check(cx, expr, init, acc, call_span, &self.msrv);
                     unnecessary_fold::check(cx, expr, init, acc, span);
                 },
-                ("for_each", [_]) => {
-                    if let Some(("inspect", _, [_], span2, _)) = method_call(recv) {
-                        inspect_for_each::check(cx, expr, span2);
+                ("for_each", [arg]) => {
+                    match method_call(recv) {
+                        Some(("inspect", _, [_], span2, _)) => inspect_for_each::check(cx, expr, span2),
+                        Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::NeedlessMove(name, arg), false),
+                        _ => {}
                     }
                 },
                 ("get", [arg]) => {
@@ -4021,7 +4030,8 @@ impl Methods {
                 },
                 ("last", []) => {
                     if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
-                        iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
+                        iter_overeager_cloned::check(cx, expr, recv, recv2,
+                                iter_overeager_cloned::Op::LaterCloned , false);
                     }
                 },
                 ("lock", []) => {
@@ -4030,8 +4040,10 @@ impl Methods {
                 (name @ ("map" | "map_err"), [m_arg]) => {
                     if name == "map" {
                         map_clone::check(cx, expr, recv, m_arg, &self.msrv);
-                        if let Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) = method_call(recv) {
-                            iter_kv_map::check(cx, map_name, expr, recv2, m_arg);
+                        match method_call(recv) {
+                            Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) => iter_kv_map::check(cx, map_name, expr, recv2, m_arg),
+                            Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::NeedlessMove(name, m_arg), false),
+                            _ => {}
                         }
                     } else {
                         map_err_ignore::check(cx, expr, m_arg);
@@ -4058,7 +4070,7 @@ impl Methods {
                 ("next", []) => {
                     if let Some((name2, recv2, args2, _, _)) = method_call(recv) {
                         match (name2, args2) {
-                            ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false),
+                            ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::LaterCloned, false),
                             ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg),
                             ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, &self.msrv),
                             ("iter", []) => iter_next_slice::check(cx, expr, recv2),
@@ -4071,7 +4083,7 @@ impl Methods {
                 },
                 ("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),
+                    Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::LaterCloned , false),
                     Some(("iter", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false),
                     Some(("iter_mut", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true),
                     _ => iter_nth_zero::check(cx, expr, recv, n_arg),
@@ -4126,7 +4138,8 @@ impl Methods {
                     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);
+                        iter_overeager_cloned::check(cx, expr, recv, recv2,
+                                iter_overeager_cloned::Op::LaterCloned , false);
                     }
                 }
                 ("sort", []) => {
@@ -4152,7 +4165,8 @@ impl Methods {
                 ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
                 ("take", [_arg]) => {
                     if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
-                        iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
+                        iter_overeager_cloned::check(cx, expr, recv, recv2,
+                                iter_overeager_cloned::Op::LaterCloned, false);
                     }
                 },
                 ("take", []) => needless_option_take::check(cx, expr, recv),
diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs
index 8b2f57160af..942f3bd79a6 100644
--- a/clippy_lints/src/methods/or_fun_call.rs
+++ b/clippy_lints/src/methods/or_fun_call.rs
@@ -65,11 +65,26 @@ pub(super) fn check<'tcx>(
         };
 
         let sugg = match (name, call_expr.is_some()) {
-            ("unwrap_or", true) | ("unwrap_or_else", false) => "unwrap_or_default",
-            ("or_insert", true) | ("or_insert_with", false) => "or_default",
+            ("unwrap_or", true) | ("unwrap_or_else", false) => sym!(unwrap_or_default),
+            ("or_insert", true) | ("or_insert_with", false) => sym!(or_default),
             _ => return false,
         };
 
+        let receiver_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs();
+        let has_suggested_method = receiver_ty.ty_adt_def().is_some_and(|adt_def| {
+            cx.tcx
+                .inherent_impls(adt_def.did())
+                .iter()
+                .flat_map(|impl_id| cx.tcx.associated_items(impl_id).filter_by_name_unhygienic(sugg))
+                .any(|assoc| {
+                    assoc.fn_has_self_parameter
+                        && cx.tcx.fn_sig(assoc.def_id).skip_binder().inputs().skip_binder().len() == 1
+                })
+        });
+        if !has_suggested_method {
+            return false;
+        }
+
         // needs to target Default::default in particular or be *::new and have a Default impl
         // available
         if (is_new(fun) && output_type_implements_default(fun))
diff --git a/clippy_lints/src/methods/unnecessary_sort_by.rs b/clippy_lints/src/methods/unnecessary_sort_by.rs
index e62a65a2712..79bff39379c 100644
--- a/clippy_lints/src/methods/unnecessary_sort_by.rs
+++ b/clippy_lints/src/methods/unnecessary_sort_by.rs
@@ -6,7 +6,8 @@ use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
 use rustc_lint::LateContext;
-use rustc_middle::ty::{self, GenericArgKind};
+use rustc_middle::ty;
+use rustc_middle::ty::GenericArgKind;
 use rustc_span::sym;
 use rustc_span::symbol::Ident;
 use std::iter;
diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs
index dea432fdbd5..9256119e6a4 100644
--- a/clippy_lints/src/mutable_debug_assertion.rs
+++ b/clippy_lints/src/mutable_debug_assertion.rs
@@ -96,10 +96,6 @@ impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> {
                 self.found = true;
                 return;
             },
-            ExprKind::If(..) => {
-                self.found = true;
-                return;
-            },
             ExprKind::Path(_) => {
                 if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
                     if adj
diff --git a/clippy_lints/src/needless_else.rs b/clippy_lints/src/needless_else.rs
index 03bab86c6d7..0c1fe881fc1 100644
--- a/clippy_lints/src/needless_else.rs
+++ b/clippy_lints/src/needless_else.rs
@@ -27,7 +27,7 @@ declare_clippy_lint! {
     ///     println!("Check successful!");
     /// }
     /// ```
-    #[clippy::version = "1.71.0"]
+    #[clippy::version = "1.72.0"]
     pub NEEDLESS_ELSE,
     style,
     "empty else branch"
diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs
index cf7cd671dca..f7f9dccfbce 100644
--- a/clippy_lints/src/new_without_default.rs
+++ b/clippy_lints/src/new_without_default.rs
@@ -130,6 +130,11 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
                                 }
 
                                 let generics_sugg = snippet(cx, generics.span, "");
+                                let where_clause_sugg = if generics.has_where_clause_predicates {
+                                    format!("\n{}\n", snippet(cx, generics.where_clause_span, ""))
+                                } else {
+                                    String::new()
+                                };
                                 let self_ty_fmt = self_ty.to_string();
                                 let self_type_snip = snippet(cx, impl_self_ty.span, &self_ty_fmt);
                                 span_lint_hir_and_then(
@@ -145,8 +150,12 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
                                             cx,
                                             item.span,
                                             "try adding this",
-                                            &create_new_without_default_suggest_msg(&self_type_snip, &generics_sugg),
-                                            Applicability::MaybeIncorrect,
+                                            &create_new_without_default_suggest_msg(
+                                                &self_type_snip,
+                                                &generics_sugg,
+                                                &where_clause_sugg
+                                            ),
+                                            Applicability::MachineApplicable,
                                         );
                                     },
                                 );
@@ -159,10 +168,14 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
     }
 }
 
-fn create_new_without_default_suggest_msg(self_type_snip: &str, generics_sugg: &str) -> String {
+fn create_new_without_default_suggest_msg(
+    self_type_snip: &str,
+    generics_sugg: &str,
+    where_clause_sugg: &str,
+) -> String {
     #[rustfmt::skip]
     format!(
-"impl{generics_sugg} Default for {self_type_snip} {{
+"impl{generics_sugg} Default for {self_type_snip}{where_clause_sugg} {{
     fn default() -> Self {{
         Self::new()
     }}
diff --git a/clippy_lints/src/redundant_locals.rs b/clippy_lints/src/redundant_locals.rs
index 0c89c7ee47d..a1ebba6a6a8 100644
--- a/clippy_lints/src/redundant_locals.rs
+++ b/clippy_lints/src/redundant_locals.rs
@@ -3,11 +3,9 @@ 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,
-};
+use rustc_hir::{BindingAnnotation, ByRef, Expr, ExprKind, HirId, Local, Node, Pat, PatKind, QPath};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::lint::{in_external_macro, is_from_async_await};
+use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::symbol::Ident;
 use rustc_span::DesugaringKind;
@@ -72,9 +70,6 @@ impl<'tcx> LateLintPass<'tcx> for RedundantLocals {
             // the local is user-controlled
             if !in_external_macro(cx.sess(), local.span);
             if !is_from_proc_macro(cx, expr);
-            // Async function parameters are lowered into the closure body, so we can't lint them.
-            // see `lower_maybe_async_body` in `rust_ast_lowering`
-            if !is_from_async_await(local.span);
             then {
                 span_lint_and_help(
                     cx,
@@ -111,12 +106,7 @@ fn affects_assignments(cx: &LateContext<'_>, mutability: Mutability, bind: HirId
 }
 
 /// Check if a rebinding of a local affects the code's drop behavior.
-fn affects_drop_behavior<'tcx>(
-    cx: &LateContext<'tcx>,
-    bind: HirId,
-    rebind: HirId,
-    rebind_expr: &Expr<'tcx>,
-) -> bool {
+fn affects_drop_behavior<'tcx>(cx: &LateContext<'tcx>, bind: HirId, rebind: HirId, rebind_expr: &Expr<'tcx>) -> bool {
     let hir = cx.tcx.hir();
 
     // the rebinding is in a different scope than the original binding
diff --git a/clippy_lints/src/redundant_type_annotations.rs b/clippy_lints/src/redundant_type_annotations.rs
index 3e963d79892..1d4fdb43a0b 100644
--- a/clippy_lints/src/redundant_type_annotations.rs
+++ b/clippy_lints/src/redundant_type_annotations.rs
@@ -31,7 +31,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let foo = String::new();
     /// ```
-    #[clippy::version = "1.70.0"]
+    #[clippy::version = "1.72.0"]
     pub REDUNDANT_TYPE_ANNOTATIONS,
     restriction,
     "warns about needless / redundant type annotations."
diff --git a/clippy_lints/src/reserve_after_initialization.rs b/clippy_lints/src/reserve_after_initialization.rs
new file mode 100644
index 00000000000..0c8c904e374
--- /dev/null
+++ b/clippy_lints/src/reserve_after_initialization.rs
@@ -0,0 +1,134 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher::{get_vec_init_kind, VecInitKind};
+use clippy_utils::source::snippet;
+use clippy_utils::{is_from_proc_macro, path_to_local_id};
+use rustc_errors::Applicability;
+use rustc_hir::def::Res;
+use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Local, PatKind, QPath, Stmt, StmtKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::Span;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Informs the user about a more concise way to create a vector with a known capacity.
+    ///
+    /// ### Why is this bad?
+    /// The `Vec::with_capacity` constructor is less complex.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let mut v: Vec<usize> = vec![];
+    /// v.reserve(10);
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let mut v: Vec<usize> = Vec::with_capacity(10);
+    /// ```
+    #[clippy::version = "1.73.0"]
+    pub RESERVE_AFTER_INITIALIZATION,
+    complexity,
+    "`reserve` called immediatly after `Vec` creation"
+}
+impl_lint_pass!(ReserveAfterInitialization => [RESERVE_AFTER_INITIALIZATION]);
+
+#[derive(Default)]
+pub struct ReserveAfterInitialization {
+    searcher: Option<VecReserveSearcher>,
+}
+
+struct VecReserveSearcher {
+    local_id: HirId,
+    err_span: Span,
+    init_part: String,
+    space_hint: String,
+}
+impl VecReserveSearcher {
+    fn display_err(&self, cx: &LateContext<'_>) {
+        if self.space_hint.is_empty() {
+            return;
+        }
+
+        let s = format!("{}Vec::with_capacity({});", self.init_part, self.space_hint);
+
+        span_lint_and_sugg(
+            cx,
+            RESERVE_AFTER_INITIALIZATION,
+            self.err_span,
+            "call to `reserve` immediately after creation",
+            "consider using `Vec::with_capacity(/* Space hint */)`",
+            s,
+            Applicability::HasPlaceholders,
+        );
+    }
+}
+
+impl<'tcx> LateLintPass<'tcx> for ReserveAfterInitialization {
+    fn check_block(&mut self, _: &LateContext<'tcx>, _: &'tcx Block<'tcx>) {
+        self.searcher = None;
+    }
+
+    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
+        if let Some(init_expr) = local.init
+            && let PatKind::Binding(BindingAnnotation::MUT, id, _, None) = local.pat.kind
+            && !in_external_macro(cx.sess(), local.span)
+            && let Some(init) = get_vec_init_kind(cx, init_expr)
+            && !matches!(init, VecInitKind::WithExprCapacity(_) | VecInitKind::WithConstCapacity(_))
+        {
+            self.searcher = Some(VecReserveSearcher {
+                local_id: id,
+                err_span: local.span,
+                init_part: snippet(cx, local.span.shrink_to_lo()
+                    .to(init_expr.span.source_callsite().shrink_to_lo()), "..")
+                    .into_owned(),
+                space_hint: String::new()
+            });
+        }
+    }
+
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if self.searcher.is_none()
+            && let ExprKind::Assign(left, right, _) = expr.kind
+            && let ExprKind::Path(QPath::Resolved(None, path)) = left.kind
+            && let Res::Local(id) = path.res
+            && !in_external_macro(cx.sess(), expr.span)
+            && let Some(init) = get_vec_init_kind(cx, right)
+            && !matches!(init, VecInitKind::WithExprCapacity(_) | VecInitKind::WithConstCapacity(_))
+        {
+            self.searcher = Some(VecReserveSearcher {
+                local_id: id,
+                err_span: expr.span,
+                init_part: snippet(cx, left.span.shrink_to_lo()
+                    .to(right.span.source_callsite().shrink_to_lo()), "..")
+                    .into_owned(), // see `assign_expression` test
+                space_hint: String::new()
+            });
+        }
+    }
+
+    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
+        if let Some(searcher) = self.searcher.take() {
+            if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind
+                && let ExprKind::MethodCall(name, self_arg, [space_hint], _) = expr.kind
+                && path_to_local_id(self_arg, searcher.local_id)
+                && name.ident.as_str() == "reserve"
+                && !is_from_proc_macro(cx, expr)
+            {
+                self.searcher = Some(VecReserveSearcher {
+                    err_span: searcher.err_span.to(stmt.span),
+                    space_hint: snippet(cx, space_hint.span, "..").into_owned(),
+                    .. searcher
+                });
+            } else {
+                searcher.display_err(cx);
+            }
+        }
+    }
+
+    fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx Block<'tcx>) {
+        if let Some(searcher) = self.searcher.take() {
+            searcher.display_err(cx);
+        }
+    }
+}
diff --git a/clippy_lints/src/same_name_method.rs b/clippy_lints/src/same_name_method.rs
index a37e2772d35..45433b2adf0 100644
--- a/clippy_lints/src/same_name_method.rs
+++ b/clippy_lints/src/same_name_method.rs
@@ -55,11 +55,11 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
             if matches!(cx.tcx.def_kind(id.owner_id), DefKind::Impl { .. })
                 && let item = cx.tcx.hir().item(id)
                 && let ItemKind::Impl(Impl {
-                  items,
-                  of_trait,
-                  self_ty,
-                  ..
-                                      }) = &item.kind
+                    items,
+                    of_trait,
+                    self_ty,
+                    ..
+                }) = &item.kind
                 && let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind
             {
                 if !map.contains_key(res) {
diff --git a/clippy_lints/src/tuple_array_conversions.rs b/clippy_lints/src/tuple_array_conversions.rs
index 78ad52d8a87..c12519d723c 100644
--- a/clippy_lints/src/tuple_array_conversions.rs
+++ b/clippy_lints/src/tuple_array_conversions.rs
@@ -189,8 +189,8 @@ fn all_bindings_are_for_conv<'tcx>(
                     tys.len() == elements.len() && tys.iter().chain(final_tys.iter().copied()).all_equal()
                 },
                 (ToType::Tuple, ty::Array(ty, len)) => {
-                    len.eval_target_usize(cx.tcx, cx.param_env) as usize == elements.len()
-                        && final_tys.iter().chain(once(ty)).all_equal()
+                    let Some(len) = len.try_eval_target_usize(cx.tcx, cx.param_env) else { return false };
+                    len as usize == elements.len() && final_tys.iter().chain(once(ty)).all_equal()
                 },
                 _ => false,
             }