about summary refs log tree commit diff
path: root/clippy_lints/src
diff options
context:
space:
mode:
authorJason Newcomb <jsnewcomb@pm.me>2022-08-31 09:24:45 -0400
committerJason Newcomb <jsnewcomb@pm.me>2022-08-31 09:24:45 -0400
commitfb41bfa77405233da5ddd324091380e018e3d956 (patch)
tree5542e247fac4af781cad93f553a8e6bbc4b8a842 /clippy_lints/src
parent7ba06ec9c5fb165e717f6d732ce50771733f31c4 (diff)
downloadrust-fb41bfa77405233da5ddd324091380e018e3d956.tar.gz
rust-fb41bfa77405233da5ddd324091380e018e3d956.zip
Merge commit 'f51aade56f93175dde89177a92e3669ebd8e7592' into clippyup
Diffstat (limited to 'clippy_lints/src')
-rw-r--r--clippy_lints/src/as_underscore.rs74
-rw-r--r--clippy_lints/src/borrow_as_ptr.rs99
-rw-r--r--clippy_lints/src/borrow_deref_ref.rs7
-rw-r--r--clippy_lints/src/bytecount.rs103
-rw-r--r--clippy_lints/src/bytes_count_to_len.rs70
-rw-r--r--clippy_lints/src/case_sensitive_file_extension_comparisons.rs86
-rw-r--r--clippy_lints/src/casts/as_underscore.rs25
-rw-r--r--clippy_lints/src/casts/borrow_as_ptr.rs37
-rw-r--r--clippy_lints/src/casts/cast_slice_from_raw_parts.rs63
-rw-r--r--clippy_lints/src/casts/mod.rs109
-rw-r--r--clippy_lints/src/casts/unnecessary_cast.rs9
-rw-r--r--clippy_lints/src/dereference.rs312
-rw-r--r--clippy_lints/src/derive.rs5
-rw-r--r--clippy_lints/src/doc_link_with_quotes.rs2
-rw-r--r--clippy_lints/src/duplicate_mod.rs2
-rw-r--r--clippy_lints/src/escape.rs13
-rw-r--r--clippy_lints/src/floating_point_arithmetic.rs22
-rw-r--r--clippy_lints/src/format.rs24
-rw-r--r--clippy_lints/src/format_args.rs94
-rw-r--r--clippy_lints/src/format_impl.rs11
-rw-r--r--clippy_lints/src/functions/mod.rs56
-rw-r--r--clippy_lints/src/functions/result.rs100
-rw-r--r--clippy_lints/src/functions/result_unit_err.rs66
-rw-r--r--clippy_lints/src/get_first.rs68
-rw-r--r--clippy_lints/src/if_let_mutex.rs50
-rw-r--r--clippy_lints/src/if_then_some_else_none.rs90
-rw-r--r--clippy_lints/src/lib.register_all.rs30
-rw-r--r--clippy_lints/src/lib.register_complexity.rs9
-rw-r--r--clippy_lints/src/lib.register_correctness.rs8
-rw-r--r--clippy_lints/src/lib.register_lints.rs50
-rw-r--r--clippy_lints/src/lib.register_nursery.rs5
-rw-r--r--clippy_lints/src/lib.register_pedantic.rs11
-rw-r--r--clippy_lints/src/lib.register_perf.rs2
-rw-r--r--clippy_lints/src/lib.register_restriction.rs6
-rw-r--r--clippy_lints/src/lib.register_style.rs6
-rw-r--r--clippy_lints/src/lib.register_suspicious.rs5
-rw-r--r--clippy_lints/src/lib.rs53
-rw-r--r--clippy_lints/src/loops/needless_collect.rs34
-rw-r--r--clippy_lints/src/manual_async_fn.rs2
-rw-r--r--clippy_lints/src/manual_ok_or.rs95
-rw-r--r--clippy_lints/src/manual_string_new.rs140
-rw-r--r--clippy_lints/src/map_clone.rs167
-rw-r--r--clippy_lints/src/map_err_ignore.rs154
-rw-r--r--clippy_lints/src/matches/match_like_matches.rs4
-rw-r--r--clippy_lints/src/matches/needless_match.rs22
-rw-r--r--clippy_lints/src/methods/bytecount.rs70
-rw-r--r--clippy_lints/src/methods/bytes_count_to_len.rs37
-rw-r--r--clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs41
-rw-r--r--clippy_lints/src/methods/collapsible_str_replace.rs96
-rw-r--r--clippy_lints/src/methods/expect_used.rs18
-rw-r--r--clippy_lints/src/methods/get_first.rs39
-rw-r--r--clippy_lints/src/methods/iter_on_single_or_empty_collections.rs107
-rw-r--r--clippy_lints/src/methods/manual_ok_or.rs64
-rw-r--r--clippy_lints/src/methods/map_clone.rs122
-rw-r--r--clippy_lints/src/methods/map_err_ignore.rs34
-rw-r--r--clippy_lints/src/methods/mod.rs857
-rw-r--r--clippy_lints/src/methods/mut_mutex_lock.rs30
-rw-r--r--clippy_lints/src/methods/open_options.rs (renamed from clippy_lints/src/open_options.rs)44
-rw-r--r--clippy_lints/src/methods/option_map_unwrap_or.rs2
-rw-r--r--clippy_lints/src/methods/path_buf_push_overwrite.rs37
-rw-r--r--clippy_lints/src/methods/range_zip_with_len.rs34
-rw-r--r--clippy_lints/src/methods/repeat_once.rs52
-rw-r--r--clippy_lints/src/methods/stable_sort_primitive.rs31
-rw-r--r--clippy_lints/src/methods/suspicious_to_owned.rs36
-rw-r--r--clippy_lints/src/methods/uninit_assumed_init.rs4
-rw-r--r--clippy_lints/src/methods/unit_hash.rs29
-rw-r--r--clippy_lints/src/methods/unnecessary_sort_by.rs (renamed from clippy_lints/src/unnecessary_sort_by.rs)156
-rw-r--r--clippy_lints/src/methods/unnecessary_to_owned.rs37
-rw-r--r--clippy_lints/src/methods/unwrap_used.rs20
-rw-r--r--clippy_lints/src/methods/vec_resize_to_zero.rs45
-rw-r--r--clippy_lints/src/methods/verbose_file_reads.rs28
-rw-r--r--clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs2
-rw-r--r--clippy_lints/src/mismatching_type_param_order.rs2
-rw-r--r--clippy_lints/src/multi_assignments.rs65
-rw-r--r--clippy_lints/src/mut_mutex_lock.rs70
-rw-r--r--clippy_lints/src/only_used_in_recursion.rs830
-rw-r--r--clippy_lints/src/option_if_let_else.rs167
-rw-r--r--clippy_lints/src/partialeq_to_none.rs3
-rw-r--r--clippy_lints/src/path_buf_push_overwrite.rs72
-rw-r--r--clippy_lints/src/question_mark.rs2
-rw-r--r--clippy_lints/src/ranges.rs73
-rw-r--r--clippy_lints/src/rc_clone_in_vec_init.rs2
-rw-r--r--clippy_lints/src/redundant_slicing.rs4
-rw-r--r--clippy_lints/src/redundant_static_lifetimes.rs12
-rw-r--r--clippy_lints/src/repeat_once.rs89
-rw-r--r--clippy_lints/src/returns.rs10
-rw-r--r--clippy_lints/src/self_named_constructors.rs4
-rw-r--r--clippy_lints/src/stable_sort_primitive.rs144
-rw-r--r--clippy_lints/src/trait_bounds.rs106
-rw-r--r--clippy_lints/src/transmute/mod.rs25
-rw-r--r--clippy_lints/src/transmute/transmute_undefined_repr.rs400
-rw-r--r--clippy_lints/src/transmute/transmuting_null.rs61
-rw-r--r--clippy_lints/src/transmuting_null.rs89
-rw-r--r--clippy_lints/src/unicode.rs7
-rw-r--r--clippy_lints/src/uninit_vec.rs2
-rw-r--r--clippy_lints/src/unit_hash.rs78
-rw-r--r--clippy_lints/src/unnecessary_wraps.rs2
-rw-r--r--clippy_lints/src/unused_peekable.rs225
-rw-r--r--clippy_lints/src/unused_rounding.rs2
-rw-r--r--clippy_lints/src/utils/conf.rs6
-rw-r--r--clippy_lints/src/utils/internal_lints.rs4
-rw-r--r--clippy_lints/src/utils/internal_lints/metadata_collector.rs2
-rw-r--r--clippy_lints/src/vec_resize_to_zero.rs64
-rw-r--r--clippy_lints/src/verbose_file_reads.rs88
-rw-r--r--clippy_lints/src/write.rs129
105 files changed, 4207 insertions, 3104 deletions
diff --git a/clippy_lints/src/as_underscore.rs b/clippy_lints/src/as_underscore.rs
deleted file mode 100644
index 0bdef9d0a7e..00000000000
--- a/clippy_lints/src/as_underscore.rs
+++ /dev/null
@@ -1,74 +0,0 @@
-use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
-use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, TyKind};
-use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Check for the usage of `as _` conversion using inferred type.
-    ///
-    /// ### Why is this bad?
-    /// The conversion might include lossy conversion and dangerous cast that might go
-    /// undetected du to the type being inferred.
-    ///
-    /// The lint is allowed by default as using `_` is less wordy than always specifying the type.
-    ///
-    /// ### Example
-    /// ```rust
-    /// fn foo(n: usize) {}
-    /// let n: u16 = 256;
-    /// foo(n as _);
-    /// ```
-    /// Use instead:
-    /// ```rust
-    /// fn foo(n: usize) {}
-    /// let n: u16 = 256;
-    /// foo(n as usize);
-    /// ```
-    #[clippy::version = "1.63.0"]
-    pub AS_UNDERSCORE,
-    restriction,
-    "detects `as _` conversion"
-}
-declare_lint_pass!(AsUnderscore => [AS_UNDERSCORE]);
-
-impl<'tcx> LateLintPass<'tcx> for AsUnderscore {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
-        if in_external_macro(cx.sess(), expr.span) {
-            return;
-        }
-
-        if let ExprKind::Cast(_, ty) = expr.kind && let TyKind::Infer = ty.kind {
-
-            let ty_resolved = cx.typeck_results().expr_ty(expr);
-            if let ty::Error(_) = ty_resolved.kind() {
-                span_lint_and_help(
-                    cx,
-                AS_UNDERSCORE,
-                expr.span,
-                "using `as _` conversion",
-                None,
-                "consider giving the type explicitly",
-                );
-            } else {
-            span_lint_and_then(
-                cx,
-                AS_UNDERSCORE,
-                expr.span,
-                "using `as _` conversion",
-                |diag| {
-                    diag.span_suggestion(
-                        ty.span,
-                        "consider giving the type explicitly",
-                        ty_resolved,
-                        Applicability::MachineApplicable,
-                    );
-            }
-            );
-        }
-        }
-    }
-}
diff --git a/clippy_lints/src/borrow_as_ptr.rs b/clippy_lints/src/borrow_as_ptr.rs
deleted file mode 100644
index 0993adbae2e..00000000000
--- a/clippy_lints/src/borrow_as_ptr.rs
+++ /dev/null
@@ -1,99 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::is_no_std_crate;
-use clippy_utils::source::snippet_opt;
-use clippy_utils::{meets_msrv, msrvs};
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, TyKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_semver::RustcVersion;
-use rustc_session::{declare_tool_lint, impl_lint_pass};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for the usage of `&expr as *const T` or
-    /// `&mut expr as *mut T`, and suggest using `ptr::addr_of` or
-    /// `ptr::addr_of_mut` instead.
-    ///
-    /// ### Why is this bad?
-    /// This would improve readability and avoid creating a reference
-    /// that points to an uninitialized value or unaligned place.
-    /// Read the `ptr::addr_of` docs for more information.
-    ///
-    /// ### Example
-    /// ```rust
-    /// let val = 1;
-    /// let p = &val as *const i32;
-    ///
-    /// let mut val_mut = 1;
-    /// let p_mut = &mut val_mut as *mut i32;
-    /// ```
-    /// Use instead:
-    /// ```rust
-    /// let val = 1;
-    /// let p = std::ptr::addr_of!(val);
-    ///
-    /// let mut val_mut = 1;
-    /// let p_mut = std::ptr::addr_of_mut!(val_mut);
-    /// ```
-    #[clippy::version = "1.60.0"]
-    pub BORROW_AS_PTR,
-    pedantic,
-    "borrowing just to cast to a raw pointer"
-}
-
-impl_lint_pass!(BorrowAsPtr => [BORROW_AS_PTR]);
-
-pub struct BorrowAsPtr {
-    msrv: Option<RustcVersion>,
-}
-
-impl BorrowAsPtr {
-    #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
-        Self { msrv }
-    }
-}
-
-impl<'tcx> LateLintPass<'tcx> for BorrowAsPtr {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if !meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) {
-            return;
-        }
-
-        if expr.span.from_expansion() {
-            return;
-        }
-
-        if_chain! {
-            if let ExprKind::Cast(left_expr, ty) = &expr.kind;
-            if let TyKind::Ptr(_) = ty.kind;
-            if let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = &left_expr.kind;
-
-            then {
-                let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" };
-                let macro_name = match mutability {
-                    Mutability::Not => "addr_of",
-                    Mutability::Mut => "addr_of_mut",
-                };
-
-                span_lint_and_sugg(
-                    cx,
-                    BORROW_AS_PTR,
-                    expr.span,
-                    "borrow as raw pointer",
-                    "try",
-                    format!(
-                        "{}::ptr::{}!({})",
-                        core_or_std,
-                        macro_name,
-                        snippet_opt(cx, e.span).unwrap()
-                    ),
-                    Applicability::MachineApplicable,
-                );
-            }
-        }
-    }
-
-    extract_msrv_attr!(LateContext);
-}
diff --git a/clippy_lints/src/borrow_deref_ref.rs b/clippy_lints/src/borrow_deref_ref.rs
index 937765b6614..c4520d00392 100644
--- a/clippy_lints/src/borrow_deref_ref.rs
+++ b/clippy_lints/src/borrow_deref_ref.rs
@@ -29,22 +29,17 @@ declare_clippy_lint! {
     ///
     /// ### Example
     /// ```rust
-    /// fn foo(_x: &str) {}
-    ///
     /// let s = &String::new();
     ///
     /// let a: &String = &* s;
-    /// foo(&*s);
     /// ```
     ///
     /// Use instead:
     /// ```rust
-    /// # fn foo(_x: &str) {}
     /// # let s = &String::new();
     /// let a: &String = s;
-    /// foo(&**s);
     /// ```
-    #[clippy::version = "1.59.0"]
+    #[clippy::version = "1.63.0"]
     pub BORROW_DEREF_REF,
     complexity,
     "deref on an immutable reference returns the same type as itself"
diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs
deleted file mode 100644
index 326ce34082a..00000000000
--- a/clippy_lints/src/bytecount.rs
+++ /dev/null
@@ -1,103 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::ty::match_type;
-use clippy_utils::visitors::is_local_used;
-use clippy_utils::{path_to_local_id, paths, peel_blocks, peel_ref_operators, strip_pat_refs};
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, PatKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::{self, UintTy};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for naive byte counts
-    ///
-    /// ### Why is this bad?
-    /// The [`bytecount`](https://crates.io/crates/bytecount)
-    /// crate has methods to count your bytes faster, especially for large slices.
-    ///
-    /// ### Known problems
-    /// If you have predominantly small slices, the
-    /// `bytecount::count(..)` method may actually be slower. However, if you can
-    /// ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be
-    /// faster in those cases.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let vec = vec![1_u8];
-    /// let count = vec.iter().filter(|x| **x == 0u8).count();
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust,ignore
-    /// # let vec = vec![1_u8];
-    /// let count = bytecount::count(&vec, 0u8);
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub NAIVE_BYTECOUNT,
-    pedantic,
-    "use of naive `<slice>.filter(|&x| x == y).count()` to count byte values"
-}
-
-declare_lint_pass!(ByteCount => [NAIVE_BYTECOUNT]);
-
-impl<'tcx> LateLintPass<'tcx> for ByteCount {
-    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
-        if_chain! {
-            if let ExprKind::MethodCall(count, [count_recv], _) = expr.kind;
-            if count.ident.name == sym::count;
-            if let ExprKind::MethodCall(filter, [filter_recv, filter_arg], _) = count_recv.kind;
-            if filter.ident.name == sym!(filter);
-            if let ExprKind::Closure(&Closure { body, .. }) = filter_arg.kind;
-            let body = cx.tcx.hir().body(body);
-            if let [param] = body.params;
-            if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind;
-            if let ExprKind::Binary(ref op, l, r) = body.value.kind;
-            if op.node == BinOpKind::Eq;
-            if match_type(cx,
-                       cx.typeck_results().expr_ty(filter_recv).peel_refs(),
-                       &paths::SLICE_ITER);
-            let operand_is_arg = |expr| {
-                let expr = peel_ref_operators(cx, peel_blocks(expr));
-                path_to_local_id(expr, arg_id)
-            };
-            let needle = if operand_is_arg(l) {
-                r
-            } else if operand_is_arg(r) {
-                l
-            } else {
-                return;
-            };
-            if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind();
-            if !is_local_used(cx, needle, arg_id);
-            then {
-                let haystack = if let ExprKind::MethodCall(path, args, _) =
-                        filter_recv.kind {
-                    let p = path.ident.name;
-                    if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
-                        &args[0]
-                    } else {
-                        filter_recv
-                    }
-                } else {
-                    filter_recv
-                };
-                let mut applicability = Applicability::MaybeIncorrect;
-                span_lint_and_sugg(
-                    cx,
-                    NAIVE_BYTECOUNT,
-                    expr.span,
-                    "you appear to be counting bytes the naive way",
-                    "consider using the bytecount crate",
-                    format!("bytecount::count({}, {})",
-                            snippet_with_applicability(cx, haystack.span, "..", &mut applicability),
-                            snippet_with_applicability(cx, needle.span, "..", &mut applicability)),
-                    applicability,
-                );
-            }
-        };
-    }
-}
diff --git a/clippy_lints/src/bytes_count_to_len.rs b/clippy_lints/src/bytes_count_to_len.rs
deleted file mode 100644
index d70dbf5b239..00000000000
--- a/clippy_lints/src/bytes_count_to_len.rs
+++ /dev/null
@@ -1,70 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{match_def_path, paths};
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir as hir;
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// It checks for `str::bytes().count()` and suggests replacing it with
-    /// `str::len()`.
-    ///
-    /// ### Why is this bad?
-    /// `str::bytes().count()` is longer and may not be as performant as using
-    /// `str::len()`.
-    ///
-    /// ### Example
-    /// ```rust
-    /// "hello".bytes().count();
-    /// String::from("hello").bytes().count();
-    /// ```
-    /// Use instead:
-    /// ```rust
-    /// "hello".len();
-    /// String::from("hello").len();
-    /// ```
-    #[clippy::version = "1.62.0"]
-    pub BYTES_COUNT_TO_LEN,
-    complexity,
-    "Using `bytes().count()` when `len()` performs the same functionality"
-}
-
-declare_lint_pass!(BytesCountToLen => [BYTES_COUNT_TO_LEN]);
-
-impl<'tcx> LateLintPass<'tcx> for BytesCountToLen {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-        if_chain! {
-            if let hir::ExprKind::MethodCall(_, expr_args, _) = &expr.kind;
-            if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
-            if match_def_path(cx, expr_def_id, &paths::ITER_COUNT);
-
-            if let [bytes_expr] = &**expr_args;
-            if let hir::ExprKind::MethodCall(_, bytes_args, _) = &bytes_expr.kind;
-            if let Some(bytes_def_id) = cx.typeck_results().type_dependent_def_id(bytes_expr.hir_id);
-            if match_def_path(cx, bytes_def_id, &paths::STR_BYTES);
-
-            if let [str_expr] = &**bytes_args;
-            let ty = cx.typeck_results().expr_ty(str_expr).peel_refs();
-
-            if is_type_diagnostic_item(cx, ty, sym::String) || ty.kind() == &ty::Str;
-            then {
-                let mut applicability = Applicability::MachineApplicable;
-                span_lint_and_sugg(
-                    cx,
-                    BYTES_COUNT_TO_LEN,
-                    expr.span,
-                    "using long and hard to read `.bytes().count()`",
-                    "consider calling `.len()` instead",
-                    format!("{}.len()", snippet_with_applicability(cx, str_expr.span, "..", &mut applicability)),
-                    applicability
-                );
-            }
-        };
-    }
-}
diff --git a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs
deleted file mode 100644
index 7eff71d5007..00000000000
--- a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs
+++ /dev/null
@@ -1,86 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_help;
-use if_chain::if_chain;
-use rustc_ast::ast::LitKind;
-use rustc_hir::{Expr, ExprKind, PathSegment};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::{source_map::Spanned, symbol::sym, Span};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for calls to `ends_with` with possible file extensions
-    /// and suggests to use a case-insensitive approach instead.
-    ///
-    /// ### Why is this bad?
-    /// `ends_with` is case-sensitive and may not detect files with a valid extension.
-    ///
-    /// ### Example
-    /// ```rust
-    /// fn is_rust_file(filename: &str) -> bool {
-    ///     filename.ends_with(".rs")
-    /// }
-    /// ```
-    /// Use instead:
-    /// ```rust
-    /// fn is_rust_file(filename: &str) -> bool {
-    ///     let filename = std::path::Path::new(filename);
-    ///     filename.extension()
-    ///         .map(|ext| ext.eq_ignore_ascii_case("rs"))
-    ///         .unwrap_or(false)
-    /// }
-    /// ```
-    #[clippy::version = "1.51.0"]
-    pub CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
-    pedantic,
-    "Checks for calls to ends_with with case-sensitive file extensions"
-}
-
-declare_lint_pass!(CaseSensitiveFileExtensionComparisons => [CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS]);
-
-fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Span> {
-    if_chain! {
-        if let ExprKind::MethodCall(PathSegment { ident, .. }, [obj, extension, ..], span) = expr.kind;
-        if ident.as_str() == "ends_with";
-        if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = extension.kind;
-        if (2..=6).contains(&ext_literal.as_str().len());
-        if ext_literal.as_str().starts_with('.');
-        if ext_literal.as_str().chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit())
-            || ext_literal.as_str().chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit());
-        then {
-            let mut ty = ctx.typeck_results().expr_ty(obj);
-            ty = match ty.kind() {
-                ty::Ref(_, ty, ..) => *ty,
-                _ => ty
-            };
-
-            match ty.kind() {
-                ty::Str => {
-                    return Some(span);
-                },
-                ty::Adt(def, _) => {
-                    if ctx.tcx.is_diagnostic_item(sym::String, def.did()) {
-                        return Some(span);
-                    }
-                },
-                _ => { return None; }
-            }
-        }
-    }
-    None
-}
-
-impl<'tcx> LateLintPass<'tcx> for CaseSensitiveFileExtensionComparisons {
-    fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-        if let Some(span) = check_case_sensitive_file_extension_comparison(ctx, expr) {
-            span_lint_and_help(
-                ctx,
-                CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
-                span,
-                "case-sensitive file extension comparison",
-                None,
-                "consider using a case-insensitive comparison instead",
-            );
-        }
-    }
-}
diff --git a/clippy_lints/src/casts/as_underscore.rs b/clippy_lints/src/casts/as_underscore.rs
new file mode 100644
index 00000000000..56e894c6261
--- /dev/null
+++ b/clippy_lints/src/casts/as_underscore.rs
@@ -0,0 +1,25 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, Ty, TyKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+
+use super::AS_UNDERSCORE;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ty: &'tcx Ty<'_>) {
+    if matches!(ty.kind, TyKind::Infer) {
+        span_lint_and_then(cx, AS_UNDERSCORE, expr.span, "using `as _` conversion", |diag| {
+            let ty_resolved = cx.typeck_results().expr_ty(expr);
+            if let ty::Error(_) = ty_resolved.kind() {
+                diag.help("consider giving the type explicitly");
+            } else {
+                diag.span_suggestion(
+                    ty.span,
+                    "consider giving the type explicitly",
+                    ty_resolved,
+                    Applicability::MachineApplicable,
+                );
+            }
+        });
+    }
+}
diff --git a/clippy_lints/src/casts/borrow_as_ptr.rs b/clippy_lints/src/casts/borrow_as_ptr.rs
new file mode 100644
index 00000000000..6e1f8cd64f0
--- /dev/null
+++ b/clippy_lints/src/casts/borrow_as_ptr.rs
@@ -0,0 +1,37 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_no_std_crate;
+use clippy_utils::source::snippet_with_context;
+use rustc_errors::Applicability;
+use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Ty, TyKind};
+use rustc_lint::LateContext;
+
+use super::BORROW_AS_PTR;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    cast_expr: &'tcx Expr<'_>,
+    cast_to: &'tcx Ty<'_>,
+) {
+    if matches!(cast_to.kind, TyKind::Ptr(_))
+        && let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = cast_expr.kind
+    {
+        let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" };
+        let macro_name = match mutability {
+            Mutability::Not => "addr_of",
+            Mutability::Mut => "addr_of_mut",
+        };
+        let mut app = Applicability::MachineApplicable;
+        let snip = snippet_with_context(cx, e.span, cast_expr.span.ctxt(), "..", &mut app).0;
+
+        span_lint_and_sugg(
+            cx,
+            BORROW_AS_PTR,
+            expr.span,
+            "borrow as raw pointer",
+            "try",
+            format!("{}::ptr::{}!({})", core_or_std, macro_name, snip),
+            Applicability::MachineApplicable,
+        );
+    }
+}
diff --git a/clippy_lints/src/casts/cast_slice_from_raw_parts.rs b/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
new file mode 100644
index 00000000000..284ef165998
--- /dev/null
+++ b/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
@@ -0,0 +1,63 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{match_def_path, meets_msrv, msrvs, paths};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{def_id::DefId, Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, Ty};
+use rustc_semver::RustcVersion;
+
+use super::CAST_SLICE_FROM_RAW_PARTS;
+
+enum RawPartsKind {
+    Immutable,
+    Mutable,
+}
+
+fn raw_parts_kind(cx: &LateContext<'_>, did: DefId) -> Option<RawPartsKind> {
+    if match_def_path(cx, did, &paths::SLICE_FROM_RAW_PARTS) {
+        Some(RawPartsKind::Immutable)
+    } else if match_def_path(cx, did, &paths::SLICE_FROM_RAW_PARTS_MUT) {
+        Some(RawPartsKind::Mutable)
+    } else {
+        None
+    }
+}
+
+pub(super) fn check(
+    cx: &LateContext<'_>,
+    expr: &Expr<'_>,
+    cast_expr: &Expr<'_>,
+    cast_to: Ty<'_>,
+    msrv: Option<RustcVersion>,
+) {
+    if_chain! {
+        if meets_msrv(msrv, msrvs::PTR_SLICE_RAW_PARTS);
+        if let ty::RawPtr(ptrty) = cast_to.kind();
+        if let ty::Slice(_) = ptrty.ty.kind();
+        if let ExprKind::Call(fun, [ptr_arg, len_arg]) = cast_expr.peel_blocks().kind;
+        if let ExprKind::Path(ref qpath) = fun.kind;
+        if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
+        if let Some(rpk) = raw_parts_kind(cx, fun_def_id);
+        then {
+            let func = match rpk {
+                RawPartsKind::Immutable => "from_raw_parts",
+                RawPartsKind::Mutable => "from_raw_parts_mut"
+            };
+            let span = expr.span;
+            let mut applicability = Applicability::MachineApplicable;
+            let ptr = snippet_with_applicability(cx, ptr_arg.span, "ptr", &mut applicability);
+            let len = snippet_with_applicability(cx, len_arg.span, "len", &mut applicability);
+            span_lint_and_sugg(
+                cx,
+                CAST_SLICE_FROM_RAW_PARTS,
+                span,
+                &format!("casting the result of `{func}` to {cast_to}"),
+                "replace with",
+                format!("core::ptr::slice_{func}({ptr}, {len})"),
+                applicability
+            );
+        }
+    }
+}
diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs
index af3798a0cc8..cc5d346b954 100644
--- a/clippy_lints/src/casts/mod.rs
+++ b/clippy_lints/src/casts/mod.rs
@@ -1,3 +1,5 @@
+mod as_underscore;
+mod borrow_as_ptr;
 mod cast_abs_to_unsigned;
 mod cast_enum_constructor;
 mod cast_lossless;
@@ -8,6 +10,7 @@ mod cast_ptr_alignment;
 mod cast_ref_to_mut;
 mod cast_sign_loss;
 mod cast_slice_different_sizes;
+mod cast_slice_from_raw_parts;
 mod char_lit_as_u8;
 mod fn_to_numeric_cast;
 mod fn_to_numeric_cast_any;
@@ -16,7 +19,7 @@ mod ptr_as_ptr;
 mod unnecessary_cast;
 mod utils;
 
-use clippy_utils::is_hir_ty_cfg_dependant;
+use clippy_utils::{is_hir_ty_cfg_dependant, meets_msrv, msrvs};
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
@@ -506,6 +509,93 @@ declare_clippy_lint! {
     "casting the result of `abs()` to an unsigned integer can panic"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Check for the usage of `as _` conversion using inferred type.
+    ///
+    /// ### Why is this bad?
+    /// The conversion might include lossy conversion and dangerous cast that might go
+    /// undetected due to the type being inferred.
+    ///
+    /// The lint is allowed by default as using `_` is less wordy than always specifying the type.
+    ///
+    /// ### Example
+    /// ```rust
+    /// fn foo(n: usize) {}
+    /// let n: u16 = 256;
+    /// foo(n as _);
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// fn foo(n: usize) {}
+    /// let n: u16 = 256;
+    /// foo(n as usize);
+    /// ```
+    #[clippy::version = "1.63.0"]
+    pub AS_UNDERSCORE,
+    restriction,
+    "detects `as _` conversion"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for the usage of `&expr as *const T` or
+    /// `&mut expr as *mut T`, and suggest using `ptr::addr_of` or
+    /// `ptr::addr_of_mut` instead.
+    ///
+    /// ### Why is this bad?
+    /// This would improve readability and avoid creating a reference
+    /// that points to an uninitialized value or unaligned place.
+    /// Read the `ptr::addr_of` docs for more information.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let val = 1;
+    /// let p = &val as *const i32;
+    ///
+    /// let mut val_mut = 1;
+    /// let p_mut = &mut val_mut as *mut i32;
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let val = 1;
+    /// let p = std::ptr::addr_of!(val);
+    ///
+    /// let mut val_mut = 1;
+    /// let p_mut = std::ptr::addr_of_mut!(val_mut);
+    /// ```
+    #[clippy::version = "1.60.0"]
+    pub BORROW_AS_PTR,
+    pedantic,
+    "borrowing just to cast to a raw pointer"
+}
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for a raw slice being cast to a slice pointer
+    ///
+    /// ### Why is this bad?
+    /// This can result in multiple `&mut` references to the same location when only a pointer is
+    /// required.
+    /// `ptr::slice_from_raw_parts` is a safe alternative that doesn't require
+    /// the same [safety requirements] to be upheld.
+    ///
+    /// ### Example
+    /// ```rust,ignore
+    /// let _: *const [u8] = std::slice::from_raw_parts(ptr, len) as *const _;
+    /// let _: *mut [u8] = std::slice::from_raw_parts_mut(ptr, len) as *mut _;
+    /// ```
+    /// Use instead:
+    /// ```rust,ignore
+    /// let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, len);
+    /// let _: *mut [u8] = std::ptr::slice_from_raw_parts_mut(ptr, len);
+    /// ```
+    /// [safety requirements]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety
+    #[clippy::version = "1.64.0"]
+    pub CAST_SLICE_FROM_RAW_PARTS,
+    suspicious,
+    "casting a slice created from a pointer and length to a slice pointer"
+}
+
 pub struct Casts {
     msrv: Option<RustcVersion>,
 }
@@ -534,7 +624,10 @@ impl_lint_pass!(Casts => [
     PTR_AS_PTR,
     CAST_ENUM_TRUNCATION,
     CAST_ENUM_CONSTRUCTOR,
-    CAST_ABS_TO_UNSIGNED
+    CAST_ABS_TO_UNSIGNED,
+    AS_UNDERSCORE,
+    BORROW_AS_PTR,
+    CAST_SLICE_FROM_RAW_PARTS
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Casts {
@@ -547,8 +640,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
             return;
         }
 
-        if let ExprKind::Cast(cast_expr, cast_to) = expr.kind {
-            if is_hir_ty_cfg_dependant(cx, cast_to) {
+        if let ExprKind::Cast(cast_expr, cast_to_hir) = expr.kind {
+            if is_hir_ty_cfg_dependant(cx, cast_to_hir) {
                 return;
             }
             let (cast_from, cast_to) = (
@@ -559,7 +652,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
             if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
                 return;
             }
-
+            cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, self.msrv);
             fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to);
             fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);
             fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
@@ -575,6 +668,12 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
                 cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
                 cast_enum_constructor::check(cx, expr, cast_expr, cast_from);
             }
+
+            as_underscore::check(cx, expr, cast_to_hir);
+
+            if meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) {
+                borrow_as_ptr::check(cx, expr, cast_expr, cast_to_hir);
+            }
         }
 
         cast_ref_to_mut::check(cx, expr);
diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs
index fff7da8e33f..19d2e6e1d12 100644
--- a/clippy_lints/src/casts/unnecessary_cast.rs
+++ b/clippy_lints/src/casts/unnecessary_cast.rs
@@ -90,13 +90,20 @@ pub(super) fn check<'tcx>(
 
 fn lint_unnecessary_cast(cx: &LateContext<'_>, expr: &Expr<'_>, literal_str: &str, cast_from: Ty<'_>, cast_to: Ty<'_>) {
     let literal_kind_name = if cast_from.is_integral() { "integer" } else { "float" };
+    let replaced_literal;
+    let matchless = if literal_str.contains(['(', ')']) {
+        replaced_literal = literal_str.replace(['(', ')'], "");
+        &replaced_literal
+    } else {
+        literal_str
+    };
     span_lint_and_sugg(
         cx,
         UNNECESSARY_CAST,
         expr.span,
         &format!("casting {} literal to `{}` is unnecessary", literal_kind_name, cast_to),
         "try",
-        format!("{}_{}", literal_str.trim_end_matches('.'), cast_to),
+        format!("{}_{}", matchless.trim_end_matches('.'), cast_to),
         Applicability::MachineApplicable,
     );
 }
diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs
index 59f10247a11..1506ea604f0 100644
--- a/clippy_lints/src/dereference.rs
+++ b/clippy_lints/src/dereference.rs
@@ -1,24 +1,34 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
 use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 use clippy_utils::sugg::has_enclosing_paren;
-use clippy_utils::ty::{expr_sig, peel_mid_ty_refs, ty_sig, variant_of_res};
-use clippy_utils::{get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, path_to_local, walk_to_expr_usage};
+use clippy_utils::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res};
+use clippy_utils::{
+    fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, meets_msrv, msrvs, path_to_local,
+    walk_to_expr_usage,
+};
 use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_ty, Visitor};
 use rustc_hir::{
-    self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy, GenericArg, HirId,
-    ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem,
-    TraitItemKind, TyKind, UnOp,
+    self as hir, def_id::DefId, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy,
+    GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind,
+    Path, QPath, TraitItem, TraitItemKind, TyKind, UnOp,
 };
+use rustc_index::bit_set::BitSet;
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
-use rustc_middle::ty::{self, Binder, BoundVariableKind, List, Ty, TyCtxt, TypeVisitable, TypeckResults};
+use rustc_middle::ty::{
+    self, subst::Subst, Binder, BoundVariableKind, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind,
+    ProjectionPredicate, Ty, TyCtxt, TypeVisitable, TypeckResults,
+};
+use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP};
-use rustc_trait_selection::infer::InferCtxtExt;
+use rustc_trait_selection::infer::InferCtxtExt as _;
+use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause};
+use std::collections::VecDeque;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -151,6 +161,7 @@ pub struct Dereferencing {
     /// been finished. Note we can't lint at the end of every body as they can be nested within each
     /// other.
     current_body: Option<BodyId>,
+
     /// The list of locals currently being checked by the lint.
     /// If the value is `None`, then the binding has been seen as a ref pattern, but is not linted.
     /// This is needed for or patterns where one of the branches can be linted, but another can not
@@ -158,6 +169,19 @@ pub struct Dereferencing {
     ///
     /// e.g. `m!(x) | Foo::Bar(ref x)`
     ref_locals: FxIndexMap<HirId, Option<RefPat>>,
+
+    // `IntoIterator` for arrays requires Rust 1.53.
+    msrv: Option<RustcVersion>,
+}
+
+impl Dereferencing {
+    #[must_use]
+    pub fn new(msrv: Option<RustcVersion>) -> Self {
+        Self {
+            msrv,
+            ..Dereferencing::default()
+        }
+    }
 }
 
 struct StateData {
@@ -170,6 +194,7 @@ struct StateData {
 struct DerefedBorrow {
     count: usize,
     msg: &'static str,
+    snip_expr: Option<HirId>,
 }
 
 enum State {
@@ -250,7 +275,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
         match (self.state.take(), kind) {
             (None, kind) => {
                 let expr_ty = typeck.expr_ty(expr);
-                let (position, adjustments) = walk_parents(cx, expr);
+                let (position, adjustments) = walk_parents(cx, expr, self.msrv);
 
                 match kind {
                     RefOp::Deref => {
@@ -331,20 +356,23 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
                         let deref_msg =
                             "this expression creates a reference which is immediately dereferenced by the compiler";
                         let borrow_msg = "this expression borrows a value the compiler would automatically borrow";
+                        let impl_msg = "the borrowed expression implements the required traits";
 
-                        let (required_refs, msg) = if position.can_auto_borrow() {
-                            (1, if deref_count == 1 { borrow_msg } else { deref_msg })
+                        let (required_refs, msg, snip_expr) = if position.can_auto_borrow() {
+                            (1, if deref_count == 1 { borrow_msg } else { deref_msg }, None)
+                        } else if let Position::ImplArg(hir_id) = position {
+                            (0, impl_msg, Some(hir_id))
                         } else if let Some(&Adjust::Borrow(AutoBorrow::Ref(_, mutability))) =
                             next_adjust.map(|a| &a.kind)
                         {
                             if matches!(mutability, AutoBorrowMutability::Mut { .. }) && !position.is_reborrow_stable()
                             {
-                                (3, deref_msg)
+                                (3, deref_msg, None)
                             } else {
-                                (2, deref_msg)
+                                (2, deref_msg, None)
                             }
                         } else {
-                            (2, deref_msg)
+                            (2, deref_msg, None)
                         };
 
                         if deref_count >= required_refs {
@@ -354,6 +382,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
                                     // can't be removed without breaking the code. See earlier comment.
                                     count: deref_count - required_refs,
                                     msg,
+                                    snip_expr,
                                 }),
                                 StateData { span: expr.span, hir_id: expr.hir_id, position },
                             ));
@@ -510,7 +539,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
                             spans: vec![pat.span],
                             app,
                             replacements: vec![(pat.span, snip.into())],
-                            hir_id: pat.hir_id
+                            hir_id: pat.hir_id,
                         }),
                     );
                 }
@@ -542,6 +571,8 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
             self.current_body = None;
         }
     }
+
+    extract_msrv_attr!(LateContext);
 }
 
 fn try_parse_ref_op<'tcx>(
@@ -594,6 +625,7 @@ enum Position {
     /// The method is defined on a reference type. e.g. `impl Foo for &T`
     MethodReceiverRefImpl,
     Callee,
+    ImplArg(HirId),
     FieldAccess(Symbol),
     Postfix,
     Deref,
@@ -630,7 +662,7 @@ impl Position {
             | Self::Callee
             | Self::FieldAccess(_)
             | Self::Postfix => PREC_POSTFIX,
-            Self::Deref => PREC_PREFIX,
+            Self::ImplArg(_) | Self::Deref => PREC_PREFIX,
             Self::DerefStable(p, _) | Self::ReborrowStable(p) | Self::Other(p) => p,
         }
     }
@@ -639,8 +671,12 @@ impl Position {
 /// Walks up the parent expressions attempting to determine both how stable the auto-deref result
 /// is, and which adjustments will be applied to it. Note this will not consider auto-borrow
 /// locations as those follow different rules.
-#[allow(clippy::too_many_lines)]
-fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &'tcx [Adjustment<'tcx>]) {
+#[expect(clippy::too_many_lines)]
+fn walk_parents<'tcx>(
+    cx: &LateContext<'tcx>,
+    e: &'tcx Expr<'_>,
+    msrv: Option<RustcVersion>,
+) -> (Position, &'tcx [Adjustment<'tcx>]) {
     let mut adjustments = [].as_slice();
     let mut precedence = 0i8;
     let ctxt = e.span.ctxt();
@@ -745,13 +781,20 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
                     .iter()
                     .position(|arg| arg.hir_id == child_id)
                     .zip(expr_sig(cx, func))
-                    .and_then(|(i, sig)| sig.input_with_hir(i))
-                    .map(|(hir_ty, ty)| match hir_ty {
-                        // Type inference for closures can depend on how they're called. Only go by the explicit
-                        // types here.
-                        Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()),
-                        None => ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence)
-                            .position_for_arg(),
+                    .and_then(|(i, sig)| {
+                        sig.input_with_hir(i).map(|(hir_ty, ty)| match hir_ty {
+                            // Type inference for closures can depend on how they're called. Only go by the explicit
+                            // types here.
+                            Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()),
+                            None => {
+                                if let ty::Param(param_ty) = ty.skip_binder().kind() {
+                                    needless_borrow_impl_arg_position(cx, parent, i, *param_ty, e, precedence, msrv)
+                                } else {
+                                    ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence)
+                                        .position_for_arg()
+                                }
+                            },
+                        })
                     }),
                 ExprKind::MethodCall(_, args, _) => {
                     let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap();
@@ -773,7 +816,7 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
                                     .and_then(|subs| subs.get(1..))
                                 {
                                     Some(subs) => cx.tcx.mk_substs(subs.iter().copied()),
-                                    None => cx.tcx.mk_substs([].iter()),
+                                    None => cx.tcx.mk_substs(std::iter::empty::<ty::subst::GenericArg<'_>>()),
                                 } && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() {
                                     // Trait methods taking `&self`
                                     sub_ty
@@ -792,12 +835,17 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
                                 Position::MethodReceiver
                             }
                         } else {
-                            ty_auto_deref_stability(
-                                cx,
-                                cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i)),
-                                precedence,
-                            )
-                            .position_for_arg()
+                            let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i];
+                            if let ty::Param(param_ty) = ty.kind() {
+                                needless_borrow_impl_arg_position(cx, parent, i, *param_ty, e, precedence, msrv)
+                            } else {
+                                ty_auto_deref_stability(
+                                    cx,
+                                    cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i)),
+                                    precedence,
+                                )
+                                .position_for_arg()
+                            }
                         }
                     })
                 },
@@ -948,6 +996,205 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
     v.0
 }
 
+// Checks whether:
+// * child is an expression of the form `&e` in an argument position requiring an `impl Trait`
+// * `e`'s type implements `Trait` and is copyable
+// If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`.
+//   The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to
+// be moved, but it cannot be.
+fn needless_borrow_impl_arg_position<'tcx>(
+    cx: &LateContext<'tcx>,
+    parent: &Expr<'tcx>,
+    arg_index: usize,
+    param_ty: ParamTy,
+    mut expr: &Expr<'tcx>,
+    precedence: i8,
+    msrv: Option<RustcVersion>,
+) -> Position {
+    let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait();
+    let sized_trait_def_id = cx.tcx.lang_items().sized_trait();
+
+    let Some(callee_def_id) = fn_def_id(cx, parent) else { return Position::Other(precedence) };
+    let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
+    let substs_with_expr_ty = cx
+        .typeck_results()
+        .node_substs(if let ExprKind::Call(callee, _) = parent.kind {
+            callee.hir_id
+        } else {
+            parent.hir_id
+        });
+
+    let predicates = cx.tcx.param_env(callee_def_id).caller_bounds();
+    let projection_predicates = predicates
+        .iter()
+        .filter_map(|predicate| {
+            if let PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
+                Some(projection_predicate)
+            } else {
+                None
+            }
+        })
+        .collect::<Vec<_>>();
+
+    let mut trait_with_ref_mut_self_method = false;
+
+    // If no traits were found, or only the `Destruct`, `Sized`, or `Any` traits were found, return.
+    if predicates
+        .iter()
+        .filter_map(|predicate| {
+            if let PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder()
+                && trait_predicate.trait_ref.self_ty() == param_ty.to_ty(cx.tcx)
+            {
+                Some(trait_predicate.trait_ref.def_id)
+            } else {
+                None
+            }
+        })
+        .inspect(|trait_def_id| {
+            trait_with_ref_mut_self_method |= has_ref_mut_self_method(cx, *trait_def_id);
+        })
+        .all(|trait_def_id| {
+            Some(trait_def_id) == destruct_trait_def_id
+                || Some(trait_def_id) == sized_trait_def_id
+                || cx.tcx.is_diagnostic_item(sym::Any, trait_def_id)
+        })
+    {
+        return Position::Other(precedence);
+    }
+
+    // `substs_with_referent_ty` can be constructed outside of `check_referent` because the same
+    // elements are modified each time `check_referent` is called.
+    let mut substs_with_referent_ty = substs_with_expr_ty.to_vec();
+
+    let mut check_referent = |referent| {
+        let referent_ty = cx.typeck_results().expr_ty(referent);
+
+        if !is_copy(cx, referent_ty) {
+            return false;
+        }
+
+        // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
+        if trait_with_ref_mut_self_method && !matches!(referent_ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
+            return false;
+        }
+
+        if !replace_types(
+            cx,
+            param_ty,
+            referent_ty,
+            fn_sig,
+            arg_index,
+            &projection_predicates,
+            &mut substs_with_referent_ty,
+        ) {
+            return false;
+        }
+
+        predicates.iter().all(|predicate| {
+            if let PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder()
+                && cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id)
+                && let ty::Param(param_ty) = trait_predicate.self_ty().kind()
+                && let GenericArgKind::Type(ty) = substs_with_referent_ty[param_ty.index as usize].unpack()
+                && ty.is_array()
+                && !meets_msrv(msrv, msrvs::ARRAY_INTO_ITERATOR)
+            {
+                return false;
+            }
+
+            let predicate = EarlyBinder(predicate).subst(cx.tcx, &substs_with_referent_ty);
+            let obligation = Obligation::new(ObligationCause::dummy(), cx.param_env, predicate);
+            cx.tcx
+                .infer_ctxt()
+                .enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation))
+        })
+    };
+
+    let mut needless_borrow = false;
+    while let ExprKind::AddrOf(_, _, referent) = expr.kind {
+        if !check_referent(referent) {
+            break;
+        }
+        expr = referent;
+        needless_borrow = true;
+    }
+
+    if needless_borrow {
+        Position::ImplArg(expr.hir_id)
+    } else {
+        Position::Other(precedence)
+    }
+}
+
+fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
+    cx.tcx
+        .associated_items(trait_def_id)
+        .in_definition_order()
+        .any(|assoc_item| {
+            if assoc_item.fn_has_self_parameter {
+                let self_ty = cx.tcx.fn_sig(assoc_item.def_id).skip_binder().inputs()[0];
+                matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut))
+            } else {
+                false
+            }
+        })
+}
+
+// Iteratively replaces `param_ty` with `new_ty` in `substs`, and similarly for each resulting
+// projected type that is a type parameter. Returns `false` if replacing the types would have an
+// effect on the function signature beyond substituting `new_ty` for `param_ty`.
+// See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757
+fn replace_types<'tcx>(
+    cx: &LateContext<'tcx>,
+    param_ty: ParamTy,
+    new_ty: Ty<'tcx>,
+    fn_sig: FnSig<'tcx>,
+    arg_index: usize,
+    projection_predicates: &[ProjectionPredicate<'tcx>],
+    substs: &mut [ty::GenericArg<'tcx>],
+) -> bool {
+    let mut replaced = BitSet::new_empty(substs.len());
+
+    let mut deque = VecDeque::with_capacity(substs.len());
+    deque.push_back((param_ty, new_ty));
+
+    while let Some((param_ty, new_ty)) = deque.pop_front() {
+        // If `replaced.is_empty()`, then `param_ty` and `new_ty` are those initially passed in.
+        if !fn_sig
+            .inputs_and_output
+            .iter()
+            .enumerate()
+            .all(|(i, ty)| (replaced.is_empty() && i == arg_index) || !ty.contains(param_ty.to_ty(cx.tcx)))
+        {
+            return false;
+        }
+
+        substs[param_ty.index as usize] = ty::GenericArg::from(new_ty);
+
+        // The `replaced.insert(...)` check provides some protection against infinite loops.
+        if replaced.insert(param_ty.index) {
+            for projection_predicate in projection_predicates {
+                if projection_predicate.projection_ty.self_ty() == param_ty.to_ty(cx.tcx)
+                    && let ty::Term::Ty(term_ty) = projection_predicate.term
+                    && let ty::Param(term_param_ty) = term_ty.kind()
+                {
+                    let item_def_id = projection_predicate.projection_ty.item_def_id;
+                    let assoc_item = cx.tcx.associated_item(item_def_id);
+                    let projection = cx.tcx
+                        .mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(new_ty, &[]));
+
+                    if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection)
+                        && substs[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty)
+                    {
+                        deque.push_back((*term_param_ty, projected_ty));
+                    }
+                }
+            }
+        }
+    }
+
+    true
+}
+
 struct TyPosition<'tcx> {
     position: Position,
     ty: Option<Ty<'tcx>>,
@@ -1086,7 +1333,8 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
         },
         State::DerefedBorrow(state) => {
             let mut app = Applicability::MachineApplicable;
-            let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
+            let snip_expr = state.snip_expr.map_or(expr, |hir_id| cx.tcx.hir().expect_expr(hir_id));
+            let (snip, snip_is_macro) = snippet_with_context(cx, snip_expr.span, data.span.ctxt(), "..", &mut app);
             span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| {
                 let calls_field = matches!(expr.kind, ExprKind::Field(..)) && matches!(data.position, Position::Callee);
                 let sugg = if !snip_is_macro
diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs
index a982990e418..9ca443b7dff 100644
--- a/clippy_lints/src/derive.rs
+++ b/clippy_lints/src/derive.rs
@@ -516,7 +516,10 @@ fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) ->
         tcx.mk_predicates(ty_predicates.iter().map(|&(p, _)| p).chain(
             params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| {
                 tcx.mk_predicate(Binder::dummy(PredicateKind::Trait(TraitPredicate {
-                    trait_ref: TraitRef::new(eq_trait_id, tcx.mk_substs([tcx.mk_param_from_def(param)].into_iter())),
+                    trait_ref: TraitRef::new(
+                        eq_trait_id,
+                        tcx.mk_substs(std::iter::once(tcx.mk_param_from_def(param))),
+                    ),
                     constness: BoundConstness::NotConst,
                     polarity: ImplPolarity::Positive,
                 })))
diff --git a/clippy_lints/src/doc_link_with_quotes.rs b/clippy_lints/src/doc_link_with_quotes.rs
index cb07f57e870..0ff1d2755da 100644
--- a/clippy_lints/src/doc_link_with_quotes.rs
+++ b/clippy_lints/src/doc_link_with_quotes.rs
@@ -21,7 +21,7 @@ declare_clippy_lint! {
     /// /// See also: [`foo`]
     /// fn bar() {}
     /// ```
-    #[clippy::version = "1.60.0"]
+    #[clippy::version = "1.63.0"]
     pub DOC_LINK_WITH_QUOTES,
     pedantic,
     "possible typo for an intra-doc link"
diff --git a/clippy_lints/src/duplicate_mod.rs b/clippy_lints/src/duplicate_mod.rs
index e1eb3b6324c..7ff7068f0b0 100644
--- a/clippy_lints/src/duplicate_mod.rs
+++ b/clippy_lints/src/duplicate_mod.rs
@@ -39,7 +39,7 @@ declare_clippy_lint! {
     /// // a.rs
     /// use crate::b;
     /// ```
-    #[clippy::version = "1.62.0"]
+    #[clippy::version = "1.63.0"]
     pub DUPLICATE_MOD,
     suspicious,
     "file loaded as module multiple times"
diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs
index 1ac7bfba06b..327865e4c85 100644
--- a/clippy_lints/src/escape.rs
+++ b/clippy_lints/src/escape.rs
@@ -1,5 +1,4 @@
 use clippy_utils::diagnostics::span_lint_hir;
-use clippy_utils::ty::contains_ty;
 use rustc_hir::intravisit;
 use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind};
 use rustc_infer::infer::TyCtxtInferExt;
@@ -30,18 +29,12 @@ declare_clippy_lint! {
     ///
     /// ### Example
     /// ```rust
-    /// # fn foo(bar: usize) {}
-    /// let x = Box::new(1);
-    /// foo(*x);
-    /// println!("{}", *x);
+    /// fn foo(x: Box<u32>) {}
     /// ```
     ///
     /// Use instead:
     /// ```rust
-    /// # fn foo(bar: usize) {}
-    /// let x = 1;
-    /// foo(x);
-    /// println!("{}", x);
+    /// fn foo(x: u32) {}
     /// ```
     #[clippy::version = "pre 1.29.0"]
     pub BOXED_LOCAL,
@@ -172,7 +165,7 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
                 // skip if there is a `self` parameter binding to a type
                 // that contains `Self` (i.e.: `self: Box<Self>`), see #4804
                 if let Some(trait_self_ty) = self.trait_self_ty {
-                    if map.name(cmt.hir_id) == kw::SelfLower && contains_ty(cmt.place.ty(), trait_self_ty) {
+                    if map.name(cmt.hir_id) == kw::SelfLower && cmt.place.ty().contains(trait_self_ty) {
                         return;
                     }
                 }
diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs
index df9b41d2c98..bb50e8fcabb 100644
--- a/clippy_lints/src/floating_point_arithmetic.rs
+++ b/clippy_lints/src/floating_point_arithmetic.rs
@@ -172,7 +172,7 @@ fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
             expr.span,
             "logarithm for bases 2, 10 and e can be computed more accurately",
             "consider using",
-            format!("{}.{}()", Sugg::hir(cx, &args[0], ".."), method),
+            format!("{}.{}()", Sugg::hir(cx, &args[0], "..").maybe_par(), method),
             Applicability::MachineApplicable,
         );
     }
@@ -263,13 +263,13 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
             (
                 SUBOPTIMAL_FLOPS,
                 "square-root of a number can be computed more efficiently and accurately",
-                format!("{}.sqrt()", Sugg::hir(cx, &args[0], "..")),
+                format!("{}.sqrt()", Sugg::hir(cx, &args[0], "..").maybe_par()),
             )
         } else if F32(1.0 / 3.0) == value || F64(1.0 / 3.0) == value {
             (
                 IMPRECISE_FLOPS,
                 "cube-root of a number can be computed more accurately",
-                format!("{}.cbrt()", Sugg::hir(cx, &args[0], "..")),
+                format!("{}.cbrt()", Sugg::hir(cx, &args[0], "..").maybe_par()),
             )
         } else if let Some(exponent) = get_integer_from_float_constant(&value) {
             (
@@ -277,7 +277,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
                 "exponentiation with integer powers can be computed more efficiently",
                 format!(
                     "{}.powi({})",
-                    Sugg::hir(cx, &args[0], ".."),
+                    Sugg::hir(cx, &args[0], "..").maybe_par(),
                     numeric_literal::format(&exponent.to_string(), None, false)
                 ),
             )
@@ -327,7 +327,7 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
                         "consider using",
                         format!(
                             "{}.mul_add({}, {})",
-                            Sugg::hir(cx, &args[0], ".."),
+                            Sugg::hir(cx, &args[0], "..").maybe_par(),
                             Sugg::hir(cx, &args[0], ".."),
                             Sugg::hir(cx, other_addend, ".."),
                         ),
@@ -418,7 +418,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) {
                 "consider using",
                 format!(
                     "{}.exp_m1()",
-                    Sugg::hir(cx, self_arg, "..")
+                    Sugg::hir(cx, self_arg, "..").maybe_par()
                 ),
                 Applicability::MachineApplicable,
             );
@@ -550,11 +550,11 @@ fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) {
         then {
             let positive_abs_sugg = (
                 "manual implementation of `abs` method",
-                format!("{}.abs()", Sugg::hir(cx, body, "..")),
+                format!("{}.abs()", Sugg::hir(cx, body, "..").maybe_par()),
             );
             let negative_abs_sugg = (
                 "manual implementation of negation of `abs` method",
-                format!("-{}.abs()", Sugg::hir(cx, body, "..")),
+                format!("-{}.abs()", Sugg::hir(cx, body, "..").maybe_par()),
             );
             let sugg = if is_testing_positive(cx, cond, body) {
                 if if_expr_positive {
@@ -621,7 +621,7 @@ fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) {
                 expr.span,
                 "log base can be expressed more clearly",
                 "consider using",
-                format!("{}.log({})", Sugg::hir(cx, largs_self, ".."), Sugg::hir(cx, rargs_self, ".."),),
+                format!("{}.log({})", Sugg::hir(cx, largs_self, "..").maybe_par(), Sugg::hir(cx, rargs_self, ".."),),
                 Applicability::MachineApplicable,
             );
         }
@@ -651,7 +651,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
             if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) &&
                (F32(180_f32) == lvalue || F64(180_f64) == lvalue)
             {
-                let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, ".."));
+                let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..").maybe_par());
                 if_chain! {
                     if let ExprKind::Lit(ref literal) = mul_lhs.kind;
                     if let ast::LitKind::Float(ref value, float_type) = literal.node;
@@ -677,7 +677,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
                 (F32(180_f32) == rvalue || F64(180_f64) == rvalue) &&
                 (F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue)
             {
-                let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, ".."));
+                let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..").maybe_par());
                 if_chain! {
                     if let ExprKind::Lit(ref literal) = mul_lhs.kind;
                     if let ast::LitKind::Float(ref value, float_type) = literal.node;
diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs
index 925a8cb8dee..0c5851cdbed 100644
--- a/clippy_lints/src/format.rs
+++ b/clippy_lints/src/format.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn};
-use clippy_utils::source::{snippet_opt, snippet_with_applicability};
+use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::sugg::Sugg;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
@@ -56,29 +56,27 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
         };
 
         let mut applicability = Applicability::MachineApplicable;
-        if format_args.value_args.is_empty() {
-            match *format_args.format_string_parts {
+        if format_args.args.is_empty() {
+            match *format_args.format_string.parts {
                 [] => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability),
                 [_] => {
-                    if let Some(s_src) = snippet_opt(cx, format_args.format_string_span) {
-                        // Simulate macro expansion, converting {{ and }} to { and }.
-                        let s_expand = s_src.replace("{{", "{").replace("}}", "}");
-                        let sugg = format!("{}.to_string()", s_expand);
-                        span_useless_format(cx, call_site, sugg, applicability);
-                    }
+                    // Simulate macro expansion, converting {{ and }} to { and }.
+                    let s_expand = format_args.format_string.snippet.replace("{{", "{").replace("}}", "}");
+                    let sugg = format!("{}.to_string()", s_expand);
+                    span_useless_format(cx, call_site, sugg, applicability);
                 },
                 [..] => {},
             }
-        } else if let [value] = *format_args.value_args {
+        } else if let [arg] = &*format_args.args {
+            let value = arg.param.value;
             if_chain! {
-                if format_args.format_string_parts == [kw::Empty];
+                if format_args.format_string.parts == [kw::Empty];
                 if match cx.typeck_results().expr_ty(value).peel_refs().kind() {
                     ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did()),
                     ty::Str => true,
                     _ => false,
                 };
-                if let Some(args) = format_args.args();
-                if args.iter().all(|arg| arg.format_trait == sym::Display && !arg.has_string_formatting());
+                if !arg.format.has_string_formatting();
                 then {
                     let is_new_string = match value.kind {
                         ExprKind::Binary(..) => true,
diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs
index 1e6feaac26c..9fb9fd99748 100644
--- a/clippy_lints/src/format_args.rs
+++ b/clippy_lints/src/format_args.rs
@@ -1,11 +1,12 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::is_diag_trait_item;
-use clippy_utils::macros::{is_format_macro, FormatArgsArg, FormatArgsExpn};
+use clippy_utils::macros::{is_format_macro, FormatArgsExpn};
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::implements_trait;
 use if_chain::if_chain;
+use itertools::Itertools;
 use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind};
+use rustc_hir::{Expr, ExprKind, HirId};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::adjustment::{Adjust, Adjustment};
 use rustc_middle::ty::Ty;
@@ -74,20 +75,16 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
             if let Some(macro_def_id) = outermost_expn_data.macro_def_id;
             if is_format_macro(cx, macro_def_id);
             if let ExpnKind::Macro(_, name) = outermost_expn_data.kind;
-            if let Some(args) = format_args.args();
             then {
-                for (i, arg) in args.iter().enumerate() {
-                    if arg.format_trait != sym::Display {
+                for arg in &format_args.args {
+                    if arg.format.has_string_formatting() {
                         continue;
                     }
-                    if arg.has_string_formatting() {
+                    if is_aliased(&format_args, arg.param.value.hir_id) {
                         continue;
                     }
-                    if is_aliased(&args, i) {
-                        continue;
-                    }
-                    check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.value);
-                    check_to_string_in_format_args(cx, name, arg.value);
+                    check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value);
+                    check_to_string_in_format_args(cx, name, arg.param.value);
                 }
             }
         }
@@ -134,45 +131,56 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex
         if is_diag_trait_item(cx, method_def_id, sym::ToString);
         let receiver_ty = cx.typeck_results().expr_ty(receiver);
         if let Some(display_trait_id) = cx.tcx.get_diagnostic_item(sym::Display);
+        let (n_needed_derefs, target) =
+            count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter());
+        if implements_trait(cx, target, display_trait_id, &[]);
+        if let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait();
         if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
         then {
-            let (n_needed_derefs, target) = count_needed_derefs(
-                receiver_ty,
-                cx.typeck_results().expr_adjustments(receiver).iter(),
-            );
-            if implements_trait(cx, target, display_trait_id, &[]) {
-                if n_needed_derefs == 0 {
-                    span_lint_and_sugg(
-                        cx,
-                        TO_STRING_IN_FORMAT_ARGS,
-                        value.span.with_lo(receiver.span.hi()),
-                        &format!("`to_string` applied to a type that implements `Display` in `{}!` args", name),
-                        "remove this",
-                        String::new(),
-                        Applicability::MachineApplicable,
-                    );
-                } else {
-                    span_lint_and_sugg(
-                        cx,
-                        TO_STRING_IN_FORMAT_ARGS,
-                        value.span,
-                        &format!("`to_string` applied to a type that implements `Display` in `{}!` args", name),
-                        "use this",
-                        format!("{:*>width$}{}", "", receiver_snippet, width = n_needed_derefs),
-                        Applicability::MachineApplicable,
-                    );
-                }
+            let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]);
+            if n_needed_derefs == 0 && !needs_ref {
+                span_lint_and_sugg(
+                    cx,
+                    TO_STRING_IN_FORMAT_ARGS,
+                    value.span.with_lo(receiver.span.hi()),
+                    &format!(
+                        "`to_string` applied to a type that implements `Display` in `{}!` args",
+                        name
+                    ),
+                    "remove this",
+                    String::new(),
+                    Applicability::MachineApplicable,
+                );
+            } else {
+                span_lint_and_sugg(
+                    cx,
+                    TO_STRING_IN_FORMAT_ARGS,
+                    value.span,
+                    &format!(
+                        "`to_string` applied to a type that implements `Display` in `{}!` args",
+                        name
+                    ),
+                    "use this",
+                    format!(
+                        "{}{:*>width$}{}",
+                        if needs_ref { "&" } else { "" },
+                        "",
+                        receiver_snippet,
+                        width = n_needed_derefs
+                    ),
+                    Applicability::MachineApplicable,
+                );
             }
         }
     }
 }
 
-// Returns true if `args[i]` "refers to" or "is referred to by" another argument.
-fn is_aliased(args: &[FormatArgsArg<'_>], i: usize) -> bool {
-    let value = args[i].value;
-    args.iter()
-        .enumerate()
-        .any(|(j, arg)| i != j && std::ptr::eq(value, arg.value))
+// Returns true if `hir_id` is referred to by multiple format params
+fn is_aliased(args: &FormatArgsExpn<'_>, hir_id: HirId) -> bool {
+    args.params()
+        .filter(|param| param.value.hir_id == hir_id)
+        .at_most_one()
+        .is_err()
 }
 
 fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>)
diff --git a/clippy_lints/src/format_impl.rs b/clippy_lints/src/format_impl.rs
index 04b5be6c80e..d8bc0bf08f2 100644
--- a/clippy_lints/src/format_impl.rs
+++ b/clippy_lints/src/format_impl.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
-use clippy_utils::macros::{is_format_macro, root_macro_call_first_node, FormatArgsArg, FormatArgsExpn};
+use clippy_utils::macros::{is_format_macro, root_macro_call_first_node, FormatArg, FormatArgsExpn};
 use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
@@ -168,10 +168,9 @@ fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>,
         if let macro_def_id = outer_macro.def_id;
         if let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, outer_macro.expn);
         if is_format_macro(cx, macro_def_id);
-        if let Some(args) = format_args.args();
         then {
-            for arg in args {
-                if arg.format_trait != impl_trait.name {
+            for arg in format_args.args {
+                if arg.format.r#trait != impl_trait.name {
                     continue;
                 }
                 check_format_arg_self(cx, expr, &arg, impl_trait);
@@ -180,11 +179,11 @@ fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>,
     }
 }
 
-fn check_format_arg_self(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &FormatArgsArg<'_>, impl_trait: FormatTrait) {
+fn check_format_arg_self(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &FormatArg<'_>, impl_trait: FormatTrait) {
     // Handle multiple dereferencing of references e.g. &&self
     // Handle dereference of &self -> self that is equivalent (i.e. via *self in fmt() impl)
     // Since the argument to fmt is itself a reference: &self
-    let reference = peel_ref_operators(cx, arg.value);
+    let reference = peel_ref_operators(cx, arg.param.value);
     let map = cx.tcx.hir();
     // Is the reference self?
     if path_to_local(reference).map(|x| map.name(x)) == Some(kw::SelfLower) {
diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs
index 73261fb8a44..90911e0bf25 100644
--- a/clippy_lints/src/functions/mod.rs
+++ b/clippy_lints/src/functions/mod.rs
@@ -1,6 +1,6 @@
 mod must_use;
 mod not_unsafe_ptr_arg_deref;
-mod result_unit_err;
+mod result;
 mod too_many_arguments;
 mod too_many_lines;
 
@@ -217,17 +217,62 @@ declare_clippy_lint! {
     "public function returning `Result` with an `Err` type of `()`"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for functions that return `Result` with an unusually large
+    /// `Err`-variant.
+    ///
+    /// ### Why is this bad?
+    /// A `Result` is at least as large as the `Err`-variant. While we
+    /// expect that variant to be seldomly used, the compiler needs to reserve
+    /// and move that much memory every single time.
+    ///
+    /// ### Known problems
+    /// The size determined by Clippy is platform-dependent.
+    ///
+    /// ### Examples
+    /// ```rust
+    /// pub enum ParseError {
+    ///     UnparsedBytes([u8; 512]),
+    ///     UnexpectedEof,
+    /// }
+    ///
+    /// // The `Result` has at least 512 bytes, even in the `Ok`-case
+    /// pub fn parse() -> Result<(), ParseError> {
+    ///     Ok(())
+    /// }
+    /// ```
+    /// should be
+    /// ```
+    /// pub enum ParseError {
+    ///     UnparsedBytes(Box<[u8; 512]>),
+    ///     UnexpectedEof,
+    /// }
+    ///
+    /// // The `Result` is slightly larger than a pointer
+    /// pub fn parse() -> Result<(), ParseError> {
+    ///     Ok(())
+    /// }
+    /// ```
+    #[clippy::version = "1.64.0"]
+    pub RESULT_LARGE_ERR,
+    perf,
+    "function returning `Result` with large `Err` type"
+}
+
 #[derive(Copy, Clone)]
 pub struct Functions {
     too_many_arguments_threshold: u64,
     too_many_lines_threshold: u64,
+    large_error_threshold: u64,
 }
 
 impl Functions {
-    pub fn new(too_many_arguments_threshold: u64, too_many_lines_threshold: u64) -> Self {
+    pub fn new(too_many_arguments_threshold: u64, too_many_lines_threshold: u64, large_error_threshold: u64) -> Self {
         Self {
             too_many_arguments_threshold,
             too_many_lines_threshold,
+            large_error_threshold,
         }
     }
 }
@@ -240,6 +285,7 @@ impl_lint_pass!(Functions => [
     DOUBLE_MUST_USE,
     MUST_USE_CANDIDATE,
     RESULT_UNIT_ERR,
+    RESULT_LARGE_ERR,
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Functions {
@@ -259,18 +305,18 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
 
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
         must_use::check_item(cx, item);
-        result_unit_err::check_item(cx, item);
+        result::check_item(cx, item, self.large_error_threshold);
     }
 
     fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
         must_use::check_impl_item(cx, item);
-        result_unit_err::check_impl_item(cx, item);
+        result::check_impl_item(cx, item, self.large_error_threshold);
     }
 
     fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
         too_many_arguments::check_trait_item(cx, item, self.too_many_arguments_threshold);
         not_unsafe_ptr_arg_deref::check_trait_item(cx, item);
         must_use::check_trait_item(cx, item);
-        result_unit_err::check_trait_item(cx, item);
+        result::check_trait_item(cx, item, self.large_error_threshold);
     }
 }
diff --git a/clippy_lints/src/functions/result.rs b/clippy_lints/src/functions/result.rs
new file mode 100644
index 00000000000..af520a493ed
--- /dev/null
+++ b/clippy_lints/src/functions/result.rs
@@ -0,0 +1,100 @@
+use rustc_errors::Diagnostic;
+use rustc_hir as hir;
+use rustc_lint::{LateContext, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::{self, Ty};
+use rustc_span::{sym, Span};
+use rustc_typeck::hir_ty_to_ty;
+
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
+use clippy_utils::trait_ref_of_method;
+use clippy_utils::ty::{approx_ty_size, is_type_diagnostic_item};
+
+use super::{RESULT_LARGE_ERR, RESULT_UNIT_ERR};
+
+/// The type of the `Err`-variant in a `std::result::Result` returned by the
+/// given `FnDecl`
+fn result_err_ty<'tcx>(
+    cx: &LateContext<'tcx>,
+    decl: &hir::FnDecl<'tcx>,
+    item_span: Span,
+) -> Option<(&'tcx hir::Ty<'tcx>, Ty<'tcx>)> {
+    if !in_external_macro(cx.sess(), item_span)
+        && let hir::FnRetTy::Return(hir_ty) = decl.output
+        && let ty = hir_ty_to_ty(cx.tcx, hir_ty)
+        && is_type_diagnostic_item(cx, ty, sym::Result)
+        && let ty::Adt(_, substs) = ty.kind()
+    {
+        let err_ty = substs.type_at(1);
+        Some((hir_ty, err_ty))
+    } else {
+        None
+    }
+}
+
+pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx>, large_err_threshold: u64) {
+    if let hir::ItemKind::Fn(ref sig, _generics, _) = item.kind
+        && let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.span)
+    {
+        if cx.access_levels.is_exported(item.def_id) {
+            let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
+            check_result_unit_err(cx, err_ty, fn_header_span);
+        }
+        check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold);
+    }
+}
+
+pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::ImplItem<'tcx>, large_err_threshold: u64) {
+    // Don't lint if method is a trait's implementation, we can't do anything about those
+    if let hir::ImplItemKind::Fn(ref sig, _) = item.kind
+        && let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.span)
+        && trait_ref_of_method(cx, item.def_id).is_none()
+    {
+        if cx.access_levels.is_exported(item.def_id) {
+            let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
+            check_result_unit_err(cx, err_ty, fn_header_span);
+        }
+        check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold);
+    }
+}
+
+pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::TraitItem<'tcx>, large_err_threshold: u64) {
+    if let hir::TraitItemKind::Fn(ref sig, _) = item.kind {
+        let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
+        if let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.span) {
+            if cx.access_levels.is_exported(item.def_id) {
+                check_result_unit_err(cx, err_ty, fn_header_span);
+            }
+            check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold);
+        }
+    }
+}
+
+fn check_result_unit_err(cx: &LateContext<'_>, err_ty: Ty<'_>, fn_header_span: Span) {
+    if err_ty.is_unit() {
+        span_lint_and_help(
+            cx,
+            RESULT_UNIT_ERR,
+            fn_header_span,
+            "this returns a `Result<_, ()>`",
+            None,
+            "use a custom `Error` type instead",
+        );
+    }
+}
+
+fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty_span: Span, large_err_threshold: u64) {
+    let ty_size = approx_ty_size(cx, err_ty);
+    if ty_size >= large_err_threshold {
+        span_lint_and_then(
+            cx,
+            RESULT_LARGE_ERR,
+            hir_ty_span,
+            "the `Err`-variant returned from this function is very large",
+            |diag: &mut Diagnostic| {
+                diag.span_label(hir_ty_span, format!("the `Err`-variant is at least {ty_size} bytes"));
+                diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`"));
+            },
+        );
+    }
+}
diff --git a/clippy_lints/src/functions/result_unit_err.rs b/clippy_lints/src/functions/result_unit_err.rs
deleted file mode 100644
index 2e63a1f920d..00000000000
--- a/clippy_lints/src/functions/result_unit_err.rs
+++ /dev/null
@@ -1,66 +0,0 @@
-use rustc_hir as hir;
-use rustc_lint::{LateContext, LintContext};
-use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty;
-use rustc_span::{sym, Span};
-use rustc_typeck::hir_ty_to_ty;
-
-use if_chain::if_chain;
-
-use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::trait_ref_of_method;
-use clippy_utils::ty::is_type_diagnostic_item;
-
-use super::RESULT_UNIT_ERR;
-
-pub(super) fn check_item(cx: &LateContext<'_>, item: &hir::Item<'_>) {
-    if let hir::ItemKind::Fn(ref sig, _generics, _) = item.kind {
-        let is_public = cx.access_levels.is_exported(item.def_id);
-        let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
-        if is_public {
-            check_result_unit_err(cx, sig.decl, item.span, fn_header_span);
-        }
-    }
-}
-
-pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &hir::ImplItem<'_>) {
-    if let hir::ImplItemKind::Fn(ref sig, _) = item.kind {
-        let is_public = cx.access_levels.is_exported(item.def_id);
-        let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
-        if is_public && trait_ref_of_method(cx, item.def_id).is_none() {
-            check_result_unit_err(cx, sig.decl, item.span, fn_header_span);
-        }
-    }
-}
-
-pub(super) fn check_trait_item(cx: &LateContext<'_>, item: &hir::TraitItem<'_>) {
-    if let hir::TraitItemKind::Fn(ref sig, _) = item.kind {
-        let is_public = cx.access_levels.is_exported(item.def_id);
-        let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
-        if is_public {
-            check_result_unit_err(cx, sig.decl, item.span, fn_header_span);
-        }
-    }
-}
-
-fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span: Span, fn_header_span: Span) {
-    if_chain! {
-        if !in_external_macro(cx.sess(), item_span);
-        if let hir::FnRetTy::Return(ty) = decl.output;
-        let ty = hir_ty_to_ty(cx.tcx, ty);
-        if is_type_diagnostic_item(cx, ty, sym::Result);
-        if let ty::Adt(_, substs) = ty.kind();
-        let err_ty = substs.type_at(1);
-        if err_ty.is_unit();
-        then {
-            span_lint_and_help(
-                cx,
-                RESULT_UNIT_ERR,
-                fn_header_span,
-                "this returns a `Result<_, ()>`",
-                None,
-                "use a custom `Error` type instead",
-            );
-        }
-    }
-}
diff --git a/clippy_lints/src/get_first.rs b/clippy_lints/src/get_first.rs
deleted file mode 100644
index 529f7babaa5..00000000000
--- a/clippy_lints/src/get_first.rs
+++ /dev/null
@@ -1,68 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{is_slice_of_primitives, match_def_path, paths};
-use if_chain::if_chain;
-use rustc_ast::LitKind;
-use rustc_errors::Applicability;
-use rustc_hir as hir;
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Spanned;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for using `x.get(0)` instead of
-    /// `x.first()`.
-    ///
-    /// ### Why is this bad?
-    /// Using `x.first()` is easier to read and has the same
-    /// result.
-    ///
-    /// ### Example
-    /// ```rust
-    /// let x = vec![2, 3, 5];
-    /// let first_element = x.get(0);
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust
-    /// let x = vec![2, 3, 5];
-    /// let first_element = x.first();
-    /// ```
-    #[clippy::version = "1.63.0"]
-    pub GET_FIRST,
-    style,
-    "Using `x.get(0)` when `x.first()` is simpler"
-}
-declare_lint_pass!(GetFirst => [GET_FIRST]);
-
-impl<'tcx> LateLintPass<'tcx> for GetFirst {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-        if_chain! {
-            if let hir::ExprKind::MethodCall(_, [struct_calling_on, method_arg], _) = &expr.kind;
-            if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
-            if match_def_path(cx, expr_def_id, &paths::SLICE_GET);
-
-            if let Some(_) = is_slice_of_primitives(cx, struct_calling_on);
-            if let hir::ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = method_arg.kind;
-
-            then {
-                let mut applicability = Applicability::MachineApplicable;
-                let slice_name = snippet_with_applicability(
-                    cx,
-                    struct_calling_on.span, "..",
-                    &mut applicability,
-                );
-                span_lint_and_sugg(
-                    cx,
-                    GET_FIRST,
-                    expr.span,
-                    &format!("accessing first element with `{0}.get(0)`", slice_name),
-                    "try",
-                    format!("{}.first()", slice_name),
-                    applicability,
-                );
-            }
-        }
-    }
-}
diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs
index e9501700784..4d703d691ac 100644
--- a/clippy_lints/src/if_let_mutex.rs
+++ b/clippy_lints/src/if_let_mutex.rs
@@ -1,8 +1,9 @@
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::higher;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::SpanlessEq;
 use if_chain::if_chain;
+use rustc_errors::Diagnostic;
 use rustc_hir::intravisit::{self as visit, Visitor};
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -45,16 +46,8 @@ declare_lint_pass!(IfLetMutex => [IF_LET_MUTEX]);
 
 impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-        let mut arm_visit = ArmVisitor {
-            mutex_lock_called: false,
-            found_mutex: None,
-            cx,
-        };
-        let mut op_visit = OppVisitor {
-            mutex_lock_called: false,
-            found_mutex: None,
-            cx,
-        };
+        let mut arm_visit = ArmVisitor { found_mutex: None, cx };
+        let mut op_visit = OppVisitor { found_mutex: None, cx };
         if let Some(higher::IfLet {
             let_expr,
             if_then,
@@ -63,18 +56,28 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
         }) = higher::IfLet::hir(cx, expr)
         {
             op_visit.visit_expr(let_expr);
-            if op_visit.mutex_lock_called {
+            if let Some(op_mutex) = op_visit.found_mutex {
                 arm_visit.visit_expr(if_then);
                 arm_visit.visit_expr(if_else);
 
-                if arm_visit.mutex_lock_called && arm_visit.same_mutex(cx, op_visit.found_mutex.unwrap()) {
-                    span_lint_and_help(
+                if let Some(arm_mutex) = arm_visit.found_mutex_if_same_as(op_mutex) {
+                    let diag = |diag: &mut Diagnostic| {
+                        diag.span_label(
+                            op_mutex.span,
+                            "this Mutex will remain locked for the entire `if let`-block...",
+                        );
+                        diag.span_label(
+                            arm_mutex.span,
+                            "... and is tried to lock again here, which will always deadlock.",
+                        );
+                        diag.help("move the lock call outside of the `if let ...` expression");
+                    };
+                    span_lint_and_then(
                         cx,
                         IF_LET_MUTEX,
                         expr.span,
                         "calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock",
-                        None,
-                        "move the lock call outside of the `if let ...` expression",
+                        diag,
                     );
                 }
             }
@@ -84,7 +87,6 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
 
 /// Checks if `Mutex::lock` is called in the `if let` expr.
 pub struct OppVisitor<'a, 'tcx> {
-    mutex_lock_called: bool,
     found_mutex: Option<&'tcx Expr<'tcx>>,
     cx: &'a LateContext<'tcx>,
 }
@@ -93,7 +95,6 @@ impl<'tcx> Visitor<'tcx> for OppVisitor<'_, 'tcx> {
     fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
         if let Some(mutex) = is_mutex_lock_call(self.cx, expr) {
             self.found_mutex = Some(mutex);
-            self.mutex_lock_called = true;
             return;
         }
         visit::walk_expr(self, expr);
@@ -102,7 +103,6 @@ impl<'tcx> Visitor<'tcx> for OppVisitor<'_, 'tcx> {
 
 /// Checks if `Mutex::lock` is called in any of the branches.
 pub struct ArmVisitor<'a, 'tcx> {
-    mutex_lock_called: bool,
     found_mutex: Option<&'tcx Expr<'tcx>>,
     cx: &'a LateContext<'tcx>,
 }
@@ -111,7 +111,6 @@ impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> {
     fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
         if let Some(mutex) = is_mutex_lock_call(self.cx, expr) {
             self.found_mutex = Some(mutex);
-            self.mutex_lock_called = true;
             return;
         }
         visit::walk_expr(self, expr);
@@ -119,9 +118,12 @@ impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> {
 }
 
 impl<'tcx, 'l> ArmVisitor<'tcx, 'l> {
-    fn same_mutex(&self, cx: &LateContext<'_>, op_mutex: &Expr<'_>) -> bool {
-        self.found_mutex
-            .map_or(false, |arm_mutex| SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex))
+    fn found_mutex_if_same_as(&self, op_mutex: &Expr<'_>) -> Option<&Expr<'_>> {
+        self.found_mutex.and_then(|arm_mutex| {
+            SpanlessEq::new(self.cx)
+                .eq_expr(op_mutex, arm_mutex)
+                .then_some(arm_mutex)
+        })
     }
 }
 
@@ -129,7 +131,7 @@ fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Opt
     if_chain! {
         if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind;
         if path.ident.as_str() == "lock";
-        let ty = cx.typeck_results().expr_ty(self_arg);
+        let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
         if is_type_diagnostic_item(cx, ty, sym::Mutex);
         then {
             Some(self_arg)
diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs
index b8d227855d9..11c43247868 100644
--- a/clippy_lints/src/if_then_some_else_none.rs
+++ b/clippy_lints/src/if_then_some_else_none.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::eager_or_lazy::switch_to_eager_eval;
 use clippy_utils::source::snippet_with_macro_callsite;
 use clippy_utils::{contains_return, higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs, peel_blocks};
-use if_chain::if_chain;
 use rustc_hir::LangItem::{OptionNone, OptionSome};
 use rustc_hir::{Expr, ExprKind, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -11,10 +11,12 @@ use rustc_session::{declare_tool_lint, impl_lint_pass};
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for if-else that could be written to `bool::then`.
+    /// Checks for if-else that could be written using either `bool::then` or `bool::then_some`.
     ///
     /// ### Why is this bad?
-    /// Looks a little redundant. Using `bool::then` helps it have less lines of code.
+    /// Looks a little redundant. Using `bool::then` is more concise and incurs no loss of clarity.
+    /// For simple calculations and known values, use `bool::then_some`, which is eagerly evaluated
+    /// in comparison to `bool::then`.
     ///
     /// ### Example
     /// ```rust
@@ -39,7 +41,7 @@ declare_clippy_lint! {
     #[clippy::version = "1.53.0"]
     pub IF_THEN_SOME_ELSE_NONE,
     restriction,
-    "Finds if-else that could be written using `bool::then`"
+    "Finds if-else that could be written using either `bool::then` or `bool::then_some`"
 }
 
 pub struct IfThenSomeElseNone {
@@ -56,7 +58,7 @@ impl IfThenSomeElseNone {
 impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]);
 
 impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
-    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
         if !meets_msrv(self.msrv, msrvs::BOOL_THEN) {
             return;
         }
@@ -70,43 +72,47 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
             return;
         }
 
-        if_chain! {
-            if let Some(higher::If { cond, then, r#else: Some(els) }) = higher::If::hir(expr);
-            if let ExprKind::Block(then_block, _) = then.kind;
-            if let Some(then_expr) = then_block.expr;
-            if let ExprKind::Call(then_call, [then_arg]) = then_expr.kind;
-            if let ExprKind::Path(ref then_call_qpath) = then_call.kind;
-            if is_lang_ctor(cx, then_call_qpath, OptionSome);
-            if let ExprKind::Path(ref qpath) = peel_blocks(els).kind;
-            if is_lang_ctor(cx, qpath, OptionNone);
-            if !stmts_contains_early_return(then_block.stmts);
-            then {
-                let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
-                let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {
-                    format!("({})", cond_snip)
-                } else {
-                    cond_snip.into_owned()
-                };
-                let arg_snip = snippet_with_macro_callsite(cx, then_arg.span, "");
-                let closure_body = if then_block.stmts.is_empty() {
-                    arg_snip.into_owned()
-                } else {
-                    format!("{{ /* snippet */ {} }}", arg_snip)
-                };
-                let help = format!(
-                    "consider using `bool::then` like: `{}.then(|| {})`",
-                    cond_snip,
-                    closure_body,
-                );
-                span_lint_and_help(
-                    cx,
-                    IF_THEN_SOME_ELSE_NONE,
-                    expr.span,
-                    "this could be simplified with `bool::then`",
-                    None,
-                    &help,
-                );
-            }
+        if let Some(higher::If { cond, then, r#else: Some(els) }) = higher::If::hir(expr)
+            && let ExprKind::Block(then_block, _) = then.kind
+            && let Some(then_expr) = then_block.expr
+            && let ExprKind::Call(then_call, [then_arg]) = then_expr.kind
+            && let ExprKind::Path(ref then_call_qpath) = then_call.kind
+            && is_lang_ctor(cx, then_call_qpath, OptionSome)
+            && let ExprKind::Path(ref qpath) = peel_blocks(els).kind
+            && is_lang_ctor(cx, qpath, OptionNone)
+            && !stmts_contains_early_return(then_block.stmts)
+        {
+            let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
+            let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {
+                format!("({})", cond_snip)
+            } else {
+                cond_snip.into_owned()
+            };
+            let arg_snip = snippet_with_macro_callsite(cx, then_arg.span, "");
+            let mut method_body = if then_block.stmts.is_empty() {
+                arg_snip.into_owned()
+            } else {
+                format!("{{ /* snippet */ {} }}", arg_snip)
+            };
+            let method_name = if switch_to_eager_eval(cx, expr) && meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) {
+                "then_some"
+            } else {
+                method_body.insert_str(0, "|| ");
+                "then"
+            };
+
+            let help = format!(
+                "consider using `bool::{}` like: `{}.{}({})`",
+                method_name, cond_snip, method_name, method_body,
+            );
+            span_lint_and_help(
+                cx,
+                IF_THEN_SOME_ELSE_NONE,
+                expr.span,
+                &format!("this could be simplified with `bool::{}`", method_name),
+                None,
+                &help,
+            );
         }
     }
 
diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs
index 01082cc8eeb..134cbbf7b5c 100644
--- a/clippy_lints/src/lib.register_all.rs
+++ b/clippy_lints/src/lib.register_all.rs
@@ -20,12 +20,12 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(booleans::NONMINIMAL_BOOL),
     LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR),
     LintId::of(borrow_deref_ref::BORROW_DEREF_REF),
-    LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
     LintId::of(casts::CAST_ABS_TO_UNSIGNED),
     LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
     LintId::of(casts::CAST_ENUM_TRUNCATION),
     LintId::of(casts::CAST_REF_TO_MUT),
     LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES),
+    LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS),
     LintId::of(casts::CHAR_LIT_AS_U8),
     LintId::of(casts::FN_TO_NUMERIC_CAST),
     LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
@@ -80,9 +80,9 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(functions::DOUBLE_MUST_USE),
     LintId::of(functions::MUST_USE_UNIT),
     LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
+    LintId::of(functions::RESULT_LARGE_ERR),
     LintId::of(functions::RESULT_UNIT_ERR),
     LintId::of(functions::TOO_MANY_ARGUMENTS),
-    LintId::of(get_first::GET_FIRST),
     LintId::of(if_let_mutex::IF_LET_MUTEX),
     LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
     LintId::of(infinite_iter::INFINITE_ITER),
@@ -128,7 +128,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID),
     LintId::of(manual_retain::MANUAL_RETAIN),
     LintId::of(manual_strip::MANUAL_STRIP),
-    LintId::of(map_clone::MAP_CLONE),
     LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
     LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
     LintId::of(match_result_ok::MATCH_RESULT_OK),
@@ -150,17 +149,20 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT),
     LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
     LintId::of(methods::BIND_INSTEAD_OF_MAP),
+    LintId::of(methods::BYTES_COUNT_TO_LEN),
     LintId::of(methods::BYTES_NTH),
     LintId::of(methods::CHARS_LAST_CMP),
     LintId::of(methods::CHARS_NEXT_CMP),
     LintId::of(methods::CLONE_DOUBLE_REF),
     LintId::of(methods::CLONE_ON_COPY),
+    LintId::of(methods::COLLAPSIBLE_STR_REPLACE),
     LintId::of(methods::ERR_EXPECT),
     LintId::of(methods::EXPECT_FUN_CALL),
     LintId::of(methods::EXTEND_WITH_DRAIN),
     LintId::of(methods::FILTER_MAP_IDENTITY),
     LintId::of(methods::FILTER_NEXT),
     LintId::of(methods::FLAT_MAP_IDENTITY),
+    LintId::of(methods::GET_FIRST),
     LintId::of(methods::GET_LAST_WITH_LEN),
     LintId::of(methods::INSPECT_FOR_EACH),
     LintId::of(methods::INTO_ITER_ON_REF),
@@ -178,13 +180,16 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
     LintId::of(methods::MANUAL_SPLIT_ONCE),
     LintId::of(methods::MANUAL_STR_REPEAT),
+    LintId::of(methods::MAP_CLONE),
     LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
     LintId::of(methods::MAP_FLATTEN),
     LintId::of(methods::MAP_IDENTITY),
+    LintId::of(methods::MUT_MUTEX_LOCK),
     LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
     LintId::of(methods::NEEDLESS_OPTION_TAKE),
     LintId::of(methods::NEEDLESS_SPLITN),
     LintId::of(methods::NEW_RET_NO_SELF),
+    LintId::of(methods::NONSENSICAL_OPEN_OPTIONS),
     LintId::of(methods::NO_EFFECT_REPLACE),
     LintId::of(methods::OBFUSCATED_IF_ELSE),
     LintId::of(methods::OK_EXPECT),
@@ -193,6 +198,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(methods::OPTION_MAP_OR_NONE),
     LintId::of(methods::OR_FUN_CALL),
     LintId::of(methods::OR_THEN_UNWRAP),
+    LintId::of(methods::RANGE_ZIP_WITH_LEN),
+    LintId::of(methods::REPEAT_ONCE),
     LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
     LintId::of(methods::SEARCH_IS_SOME),
     LintId::of(methods::SHOULD_IMPLEMENT_TRAIT),
@@ -202,14 +209,18 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(methods::STRING_EXTEND_CHARS),
     LintId::of(methods::SUSPICIOUS_MAP),
     LintId::of(methods::SUSPICIOUS_SPLITN),
+    LintId::of(methods::SUSPICIOUS_TO_OWNED),
     LintId::of(methods::UNINIT_ASSUMED_INIT),
+    LintId::of(methods::UNIT_HASH),
     LintId::of(methods::UNNECESSARY_FILTER_MAP),
     LintId::of(methods::UNNECESSARY_FIND_MAP),
     LintId::of(methods::UNNECESSARY_FOLD),
     LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
+    LintId::of(methods::UNNECESSARY_SORT_BY),
     LintId::of(methods::UNNECESSARY_TO_OWNED),
     LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
     LintId::of(methods::USELESS_ASREF),
+    LintId::of(methods::VEC_RESIZE_TO_ZERO),
     LintId::of(methods::WRONG_SELF_CONVENTION),
     LintId::of(methods::ZST_OFFSET),
     LintId::of(minmax::MIN_MAX),
@@ -224,8 +235,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
     LintId::of(misc_early::ZERO_PREFIXED_LITERAL),
     LintId::of(mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION),
+    LintId::of(multi_assignments::MULTI_ASSIGNMENTS),
     LintId::of(mut_key::MUTABLE_KEY_TYPE),
-    LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
     LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
     LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
     LintId::of(needless_bool::BOOL_COMPARISON),
@@ -245,7 +256,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
     LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
     LintId::of(octal_escapes::OCTAL_ESCAPES),
-    LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
+    LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
     LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
     LintId::of(operators::ASSIGN_OP_PATTERN),
     LintId::of(operators::BAD_BIT_MASK),
@@ -275,7 +286,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
     LintId::of(question_mark::QUESTION_MARK),
     LintId::of(ranges::MANUAL_RANGE_CONTAINS),
-    LintId::of(ranges::RANGE_ZIP_WITH_LEN),
     LintId::of(ranges::REVERSED_EMPTY_RANGES),
     LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT),
     LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC),
@@ -286,7 +296,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
     LintId::of(reference::DEREF_ADDROF),
     LintId::of(regex::INVALID_REGEX),
-    LintId::of(repeat_once::REPEAT_ONCE),
     LintId::of(returns::LET_AND_RETURN),
     LintId::of(returns::NEEDLESS_RETURN),
     LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
@@ -314,10 +323,10 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
     LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES),
     LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
+    LintId::of(transmute::TRANSMUTING_NULL),
     LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
     LintId::of(transmute::USELESS_TRANSMUTE),
     LintId::of(transmute::WRONG_TRANSMUTE),
-    LintId::of(transmuting_null::TRANSMUTING_NULL),
     LintId::of(types::BORROWED_BOX),
     LintId::of(types::BOX_COLLECTION),
     LintId::of(types::REDUNDANT_ALLOCATION),
@@ -325,7 +334,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(types::VEC_BOX),
     LintId::of(unicode::INVISIBLE_CHARACTERS),
     LintId::of(uninit_vec::UNINIT_VEC),
-    LintId::of(unit_hash::UNIT_HASH),
     LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
     LintId::of(unit_types::LET_UNIT_VALUE),
     LintId::of(unit_types::UNIT_ARG),
@@ -333,9 +341,9 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
     LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
     LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS),
-    LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
     LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
     LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
+    LintId::of(unused_peekable::UNUSED_PEEKABLE),
     LintId::of(unused_unit::UNUSED_UNIT),
     LintId::of(unwrap::PANICKING_UNWRAP),
     LintId::of(unwrap::UNNECESSARY_UNWRAP),
@@ -343,7 +351,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(useless_conversion::USELESS_CONVERSION),
     LintId::of(vec::USELESS_VEC),
     LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH),
-    LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
+    LintId::of(write::POSITIONAL_NAMED_FORMAT_PARAMETERS),
     LintId::of(write::PRINTLN_EMPTY_STRING),
     LintId::of(write::PRINT_LITERAL),
     LintId::of(write::PRINT_WITH_NEWLINE),
diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs
index 3784d3c68dc..aa247352f88 100644
--- a/clippy_lints/src/lib.register_complexity.rs
+++ b/clippy_lints/src/lib.register_complexity.rs
@@ -6,7 +6,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
     LintId::of(attrs::DEPRECATED_CFG_ATTR),
     LintId::of(booleans::NONMINIMAL_BOOL),
     LintId::of(borrow_deref_ref::BORROW_DEREF_REF),
-    LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
     LintId::of(casts::CHAR_LIT_AS_U8),
     LintId::of(casts::UNNECESSARY_CAST),
     LintId::of(dereference::EXPLICIT_AUTO_DEREF),
@@ -33,6 +32,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
     LintId::of(matches::NEEDLESS_MATCH),
     LintId::of(matches::WILDCARD_IN_OR_PATTERNS),
     LintId::of(methods::BIND_INSTEAD_OF_MAP),
+    LintId::of(methods::BYTES_COUNT_TO_LEN),
     LintId::of(methods::CLONE_ON_COPY),
     LintId::of(methods::FILTER_MAP_IDENTITY),
     LintId::of(methods::FILTER_NEXT),
@@ -51,10 +51,13 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
     LintId::of(methods::OPTION_AS_REF_DEREF),
     LintId::of(methods::OPTION_FILTER_MAP),
     LintId::of(methods::OR_THEN_UNWRAP),
+    LintId::of(methods::RANGE_ZIP_WITH_LEN),
+    LintId::of(methods::REPEAT_ONCE),
     LintId::of(methods::SEARCH_IS_SOME),
     LintId::of(methods::SKIP_WHILE_NEXT),
     LintId::of(methods::UNNECESSARY_FILTER_MAP),
     LintId::of(methods::UNNECESSARY_FIND_MAP),
+    LintId::of(methods::UNNECESSARY_SORT_BY),
     LintId::of(methods::USELESS_ASREF),
     LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
     LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
@@ -69,6 +72,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
     LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
     LintId::of(no_effect::NO_EFFECT),
     LintId::of(no_effect::UNNECESSARY_OPERATION),
+    LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
     LintId::of(operators::DOUBLE_COMPARISONS),
     LintId::of(operators::DURATION_SUBSEC),
     LintId::of(operators::IDENTITY_OP),
@@ -76,11 +80,9 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
     LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
     LintId::of(precedence::PRECEDENCE),
     LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
-    LintId::of(ranges::RANGE_ZIP_WITH_LEN),
     LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL),
     LintId::of(redundant_slicing::REDUNDANT_SLICING),
     LintId::of(reference::DEREF_ADDROF),
-    LintId::of(repeat_once::REPEAT_ONCE),
     LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
     LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
     LintId::of(swap::MANUAL_SWAP),
@@ -99,7 +101,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
     LintId::of(types::TYPE_COMPLEXITY),
     LintId::of(types::VEC_BOX),
     LintId::of(unit_types::UNIT_ARG),
-    LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
     LintId::of(unwrap::UNNECESSARY_UNWRAP),
     LintId::of(useless_conversion::USELESS_CONVERSION),
     LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO),
diff --git a/clippy_lints/src/lib.register_correctness.rs b/clippy_lints/src/lib.register_correctness.rs
index 006275d1383..bb94037ec2e 100644
--- a/clippy_lints/src/lib.register_correctness.rs
+++ b/clippy_lints/src/lib.register_correctness.rs
@@ -39,12 +39,14 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve
     LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
     LintId::of(methods::CLONE_DOUBLE_REF),
     LintId::of(methods::ITERATOR_STEP_BY_ZERO),
+    LintId::of(methods::NONSENSICAL_OPEN_OPTIONS),
     LintId::of(methods::SUSPICIOUS_SPLITN),
     LintId::of(methods::UNINIT_ASSUMED_INIT),
+    LintId::of(methods::UNIT_HASH),
+    LintId::of(methods::VEC_RESIZE_TO_ZERO),
     LintId::of(methods::ZST_OFFSET),
     LintId::of(minmax::MIN_MAX),
     LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
-    LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
     LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
     LintId::of(operators::BAD_BIT_MASK),
     LintId::of(operators::CMP_NAN),
@@ -62,17 +64,15 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve
     LintId::of(serde_api::SERDE_API_MISUSE),
     LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
     LintId::of(swap::ALMOST_SWAPPED),
+    LintId::of(transmute::TRANSMUTING_NULL),
     LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
     LintId::of(transmute::WRONG_TRANSMUTE),
-    LintId::of(transmuting_null::TRANSMUTING_NULL),
     LintId::of(unicode::INVISIBLE_CHARACTERS),
     LintId::of(uninit_vec::UNINIT_VEC),
-    LintId::of(unit_hash::UNIT_HASH),
     LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
     LintId::of(unit_types::UNIT_CMP),
     LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
     LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
     LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
     LintId::of(unwrap::PANICKING_UNWRAP),
-    LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
 ])
diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs
index c540573b802..fd20e016578 100644
--- a/clippy_lints/src/lib.register_lints.rs
+++ b/clippy_lints/src/lib.register_lints.rs
@@ -38,7 +38,6 @@ store.register_lints(&[
     almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE,
     approx_const::APPROX_CONSTANT,
     as_conversions::AS_CONVERSIONS,
-    as_underscore::AS_UNDERSCORE,
     asm_syntax::INLINE_ASM_X86_ATT_SYNTAX,
     asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX,
     assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
@@ -59,16 +58,14 @@ store.register_lints(&[
     bool_assert_comparison::BOOL_ASSERT_COMPARISON,
     booleans::NONMINIMAL_BOOL,
     booleans::OVERLY_COMPLEX_BOOL_EXPR,
-    borrow_as_ptr::BORROW_AS_PTR,
     borrow_deref_ref::BORROW_DEREF_REF,
-    bytecount::NAIVE_BYTECOUNT,
-    bytes_count_to_len::BYTES_COUNT_TO_LEN,
     cargo::CARGO_COMMON_METADATA,
     cargo::MULTIPLE_CRATE_VERSIONS,
     cargo::NEGATIVE_FEATURE_NAMES,
     cargo::REDUNDANT_FEATURE_NAMES,
     cargo::WILDCARD_DEPENDENCIES,
-    case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
+    casts::AS_UNDERSCORE,
+    casts::BORROW_AS_PTR,
     casts::CAST_ABS_TO_UNSIGNED,
     casts::CAST_ENUM_CONSTRUCTOR,
     casts::CAST_ENUM_TRUNCATION,
@@ -80,6 +77,7 @@ store.register_lints(&[
     casts::CAST_REF_TO_MUT,
     casts::CAST_SIGN_LOSS,
     casts::CAST_SLICE_DIFFERENT_SIZES,
+    casts::CAST_SLICE_FROM_RAW_PARTS,
     casts::CHAR_LIT_AS_U8,
     casts::FN_TO_NUMERIC_CAST,
     casts::FN_TO_NUMERIC_CAST_ANY,
@@ -173,11 +171,11 @@ store.register_lints(&[
     functions::MUST_USE_CANDIDATE,
     functions::MUST_USE_UNIT,
     functions::NOT_UNSAFE_PTR_ARG_DEREF,
+    functions::RESULT_LARGE_ERR,
     functions::RESULT_UNIT_ERR,
     functions::TOO_MANY_ARGUMENTS,
     functions::TOO_MANY_LINES,
     future_not_send::FUTURE_NOT_SEND,
-    get_first::GET_FIRST,
     if_let_mutex::IF_LET_MUTEX,
     if_not_else::IF_NOT_ELSE,
     if_then_some_else_none::IF_THEN_SOME_ELSE_NONE,
@@ -246,12 +244,10 @@ store.register_lints(&[
     manual_bits::MANUAL_BITS,
     manual_instant_elapsed::MANUAL_INSTANT_ELAPSED,
     manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
-    manual_ok_or::MANUAL_OK_OR,
     manual_rem_euclid::MANUAL_REM_EUCLID,
     manual_retain::MANUAL_RETAIN,
+    manual_string_new::MANUAL_STRING_NEW,
     manual_strip::MANUAL_STRIP,
-    map_clone::MAP_CLONE,
-    map_err_ignore::MAP_ERR_IGNORE,
     map_unit_fn::OPTION_MAP_UNIT_FN,
     map_unit_fn::RESULT_MAP_UNIT_FN,
     match_result_ok::MATCH_RESULT_OK,
@@ -284,13 +280,16 @@ store.register_lints(&[
     mem_replace::MEM_REPLACE_WITH_DEFAULT,
     mem_replace::MEM_REPLACE_WITH_UNINIT,
     methods::BIND_INSTEAD_OF_MAP,
+    methods::BYTES_COUNT_TO_LEN,
     methods::BYTES_NTH,
+    methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
     methods::CHARS_LAST_CMP,
     methods::CHARS_NEXT_CMP,
     methods::CLONED_INSTEAD_OF_COPIED,
     methods::CLONE_DOUBLE_REF,
     methods::CLONE_ON_COPY,
     methods::CLONE_ON_REF_PTR,
+    methods::COLLAPSIBLE_STR_REPLACE,
     methods::ERR_EXPECT,
     methods::EXPECT_FUN_CALL,
     methods::EXPECT_USED,
@@ -302,6 +301,7 @@ store.register_lints(&[
     methods::FLAT_MAP_IDENTITY,
     methods::FLAT_MAP_OPTION,
     methods::FROM_ITER_INSTEAD_OF_COLLECT,
+    methods::GET_FIRST,
     methods::GET_LAST_WITH_LEN,
     methods::GET_UNWRAP,
     methods::IMPLICIT_CLONE,
@@ -315,22 +315,30 @@ store.register_lints(&[
     methods::ITER_NEXT_SLICE,
     methods::ITER_NTH,
     methods::ITER_NTH_ZERO,
+    methods::ITER_ON_EMPTY_COLLECTIONS,
+    methods::ITER_ON_SINGLE_ITEMS,
     methods::ITER_OVEREAGER_CLONED,
     methods::ITER_SKIP_NEXT,
     methods::ITER_WITH_DRAIN,
     methods::MANUAL_FILTER_MAP,
     methods::MANUAL_FIND_MAP,
+    methods::MANUAL_OK_OR,
     methods::MANUAL_SATURATING_ARITHMETIC,
     methods::MANUAL_SPLIT_ONCE,
     methods::MANUAL_STR_REPEAT,
+    methods::MAP_CLONE,
     methods::MAP_COLLECT_RESULT_UNIT,
+    methods::MAP_ERR_IGNORE,
     methods::MAP_FLATTEN,
     methods::MAP_IDENTITY,
     methods::MAP_UNWRAP_OR,
+    methods::MUT_MUTEX_LOCK,
+    methods::NAIVE_BYTECOUNT,
     methods::NEEDLESS_OPTION_AS_DEREF,
     methods::NEEDLESS_OPTION_TAKE,
     methods::NEEDLESS_SPLITN,
     methods::NEW_RET_NO_SELF,
+    methods::NONSENSICAL_OPEN_OPTIONS,
     methods::NO_EFFECT_REPLACE,
     methods::OBFUSCATED_IF_ELSE,
     methods::OK_EXPECT,
@@ -339,25 +347,34 @@ store.register_lints(&[
     methods::OPTION_MAP_OR_NONE,
     methods::OR_FUN_CALL,
     methods::OR_THEN_UNWRAP,
+    methods::PATH_BUF_PUSH_OVERWRITE,
+    methods::RANGE_ZIP_WITH_LEN,
+    methods::REPEAT_ONCE,
     methods::RESULT_MAP_OR_INTO_OPTION,
     methods::SEARCH_IS_SOME,
     methods::SHOULD_IMPLEMENT_TRAIT,
     methods::SINGLE_CHAR_ADD_STR,
     methods::SINGLE_CHAR_PATTERN,
     methods::SKIP_WHILE_NEXT,
+    methods::STABLE_SORT_PRIMITIVE,
     methods::STRING_EXTEND_CHARS,
     methods::SUSPICIOUS_MAP,
     methods::SUSPICIOUS_SPLITN,
+    methods::SUSPICIOUS_TO_OWNED,
     methods::UNINIT_ASSUMED_INIT,
+    methods::UNIT_HASH,
     methods::UNNECESSARY_FILTER_MAP,
     methods::UNNECESSARY_FIND_MAP,
     methods::UNNECESSARY_FOLD,
     methods::UNNECESSARY_JOIN,
     methods::UNNECESSARY_LAZY_EVALUATIONS,
+    methods::UNNECESSARY_SORT_BY,
     methods::UNNECESSARY_TO_OWNED,
     methods::UNWRAP_OR_ELSE_DEFAULT,
     methods::UNWRAP_USED,
     methods::USELESS_ASREF,
+    methods::VEC_RESIZE_TO_ZERO,
+    methods::VERBOSE_FILE_READS,
     methods::WRONG_SELF_CONVENTION,
     methods::ZST_OFFSET,
     minmax::MIN_MAX,
@@ -384,9 +401,9 @@ store.register_lints(&[
     mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION,
     module_style::MOD_MODULE_FILES,
     module_style::SELF_NAMED_MODULE_FILES,
+    multi_assignments::MULTI_ASSIGNMENTS,
     mut_key::MUTABLE_KEY_TYPE,
     mut_mut::MUT_MUT,
-    mut_mutex_lock::MUT_MUTEX_LOCK,
     mut_reference::UNNECESSARY_MUT_PASSED,
     mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL,
     mutex_atomic::MUTEX_ATOMIC,
@@ -418,7 +435,6 @@ store.register_lints(&[
     nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
     octal_escapes::OCTAL_ESCAPES,
     only_used_in_recursion::ONLY_USED_IN_RECURSION,
-    open_options::NONSENSICAL_OPEN_OPTIONS,
     operators::ABSURD_EXTREME_COMPARISONS,
     operators::ARITHMETIC,
     operators::ASSIGN_OP_PATTERN,
@@ -457,7 +473,6 @@ store.register_lints(&[
     partialeq_to_none::PARTIALEQ_TO_NONE,
     pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE,
     pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF,
-    path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
     pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
     precedence::PRECEDENCE,
     ptr::CMP_NULL,
@@ -470,7 +485,6 @@ store.register_lints(&[
     ranges::MANUAL_RANGE_CONTAINS,
     ranges::RANGE_MINUS_ONE,
     ranges::RANGE_PLUS_ONE,
-    ranges::RANGE_ZIP_WITH_LEN,
     ranges::REVERSED_EMPTY_RANGES,
     rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT,
     read_zero_byte_vec::READ_ZERO_BYTE_VEC,
@@ -486,7 +500,6 @@ store.register_lints(&[
     reference::DEREF_ADDROF,
     regex::INVALID_REGEX,
     regex::TRIVIAL_REGEX,
-    repeat_once::REPEAT_ONCE,
     return_self_not_must_use::RETURN_SELF_NOT_MUST_USE,
     returns::LET_AND_RETURN,
     returns::NEEDLESS_RETURN,
@@ -501,7 +514,6 @@ store.register_lints(&[
     single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
     size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT,
     slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
-    stable_sort_primitive::STABLE_SORT_PRIMITIVE,
     std_instead_of_core::ALLOC_INSTEAD_OF_CORE,
     std_instead_of_core::STD_INSTEAD_OF_ALLOC,
     std_instead_of_core::STD_INSTEAD_OF_CORE,
@@ -537,10 +549,10 @@ store.register_lints(&[
     transmute::TRANSMUTE_PTR_TO_PTR,
     transmute::TRANSMUTE_PTR_TO_REF,
     transmute::TRANSMUTE_UNDEFINED_REPR,
+    transmute::TRANSMUTING_NULL,
     transmute::UNSOUND_COLLECTION_TRANSMUTE,
     transmute::USELESS_TRANSMUTE,
     transmute::WRONG_TRANSMUTE,
-    transmuting_null::TRANSMUTING_NULL,
     types::BORROWED_BOX,
     types::BOX_COLLECTION,
     types::LINKEDLIST,
@@ -555,7 +567,6 @@ store.register_lints(&[
     unicode::NON_ASCII_LITERAL,
     unicode::UNICODE_NOT_NFC,
     uninit_vec::UNINIT_VEC,
-    unit_hash::UNIT_HASH,
     unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD,
     unit_types::LET_UNIT_VALUE,
     unit_types::UNIT_ARG,
@@ -564,12 +575,12 @@ store.register_lints(&[
     unnamed_address::VTABLE_ADDRESS_COMPARISONS,
     unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS,
     unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
-    unnecessary_sort_by::UNNECESSARY_SORT_BY,
     unnecessary_wraps::UNNECESSARY_WRAPS,
     unnested_or_patterns::UNNESTED_OR_PATTERNS,
     unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
     unused_async::UNUSED_ASYNC,
     unused_io_amount::UNUSED_IO_AMOUNT,
+    unused_peekable::UNUSED_PEEKABLE,
     unused_rounding::UNUSED_ROUNDING,
     unused_self::UNUSED_SELF,
     unused_unit::UNUSED_UNIT,
@@ -581,10 +592,9 @@ store.register_lints(&[
     useless_conversion::USELESS_CONVERSION,
     vec::USELESS_VEC,
     vec_init_then_push::VEC_INIT_THEN_PUSH,
-    vec_resize_to_zero::VEC_RESIZE_TO_ZERO,
-    verbose_file_reads::VERBOSE_FILE_READS,
     wildcard_imports::ENUM_GLOB_USE,
     wildcard_imports::WILDCARD_IMPORTS,
+    write::POSITIONAL_NAMED_FORMAT_PARAMETERS,
     write::PRINTLN_EMPTY_STRING,
     write::PRINT_LITERAL,
     write::PRINT_STDERR,
diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs
index 91210b23afe..e319e7ee72c 100644
--- a/clippy_lints/src/lib.register_nursery.rs
+++ b/clippy_lints/src/lib.register_nursery.rs
@@ -14,16 +14,17 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
     LintId::of(index_refutable_slice::INDEX_REFUTABLE_SLICE),
     LintId::of(let_if_seq::USELESS_LET_IF_SEQ),
     LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE),
+    LintId::of(methods::ITER_ON_EMPTY_COLLECTIONS),
+    LintId::of(methods::ITER_ON_SINGLE_ITEMS),
     LintId::of(methods::ITER_WITH_DRAIN),
+    LintId::of(methods::PATH_BUF_PUSH_OVERWRITE),
     LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN),
     LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
     LintId::of(mutex_atomic::MUTEX_ATOMIC),
     LintId::of(mutex_atomic::MUTEX_INTEGER),
     LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
     LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
-    LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
     LintId::of(option_if_let_else::OPTION_IF_LET_ELSE),
-    LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE),
     LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE),
     LintId::of(regex::TRIVIAL_REGEX),
     LintId::of(strings::STRING_LIT_AS_BYTES),
diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs
index bd7d1a15ab4..584ccf55e51 100644
--- a/clippy_lints/src/lib.register_pedantic.rs
+++ b/clippy_lints/src/lib.register_pedantic.rs
@@ -4,9 +4,7 @@
 
 store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
     LintId::of(attrs::INLINE_ALWAYS),
-    LintId::of(borrow_as_ptr::BORROW_AS_PTR),
-    LintId::of(bytecount::NAIVE_BYTECOUNT),
-    LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
+    LintId::of(casts::BORROW_AS_PTR),
     LintId::of(casts::CAST_LOSSLESS),
     LintId::of(casts::CAST_POSSIBLE_TRUNCATION),
     LintId::of(casts::CAST_POSSIBLE_WRAP),
@@ -50,20 +48,24 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
     LintId::of(macro_use::MACRO_USE_IMPORTS),
     LintId::of(manual_assert::MANUAL_ASSERT),
     LintId::of(manual_instant_elapsed::MANUAL_INSTANT_ELAPSED),
-    LintId::of(manual_ok_or::MANUAL_OK_OR),
+    LintId::of(manual_string_new::MANUAL_STRING_NEW),
     LintId::of(matches::MATCH_BOOL),
     LintId::of(matches::MATCH_ON_VEC_ITEMS),
     LintId::of(matches::MATCH_SAME_ARMS),
     LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
     LintId::of(matches::MATCH_WILD_ERR_ARM),
     LintId::of(matches::SINGLE_MATCH_ELSE),
+    LintId::of(methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
     LintId::of(methods::CLONED_INSTEAD_OF_COPIED),
     LintId::of(methods::FILTER_MAP_NEXT),
     LintId::of(methods::FLAT_MAP_OPTION),
     LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
     LintId::of(methods::IMPLICIT_CLONE),
     LintId::of(methods::INEFFICIENT_TO_STRING),
+    LintId::of(methods::MANUAL_OK_OR),
     LintId::of(methods::MAP_UNWRAP_OR),
+    LintId::of(methods::NAIVE_BYTECOUNT),
+    LintId::of(methods::STABLE_SORT_PRIMITIVE),
     LintId::of(methods::UNNECESSARY_JOIN),
     LintId::of(misc::USED_UNDERSCORE_BINDING),
     LintId::of(mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER),
@@ -85,7 +87,6 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
     LintId::of(ref_option_ref::REF_OPTION_REF),
     LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE),
     LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED),
-    LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
     LintId::of(strings::STRING_ADD_ASSIGN),
     LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
     LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
diff --git a/clippy_lints/src/lib.register_perf.rs b/clippy_lints/src/lib.register_perf.rs
index e1b90acb93c..195ce41e31e 100644
--- a/clippy_lints/src/lib.register_perf.rs
+++ b/clippy_lints/src/lib.register_perf.rs
@@ -7,12 +7,14 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
     LintId::of(escape::BOXED_LOCAL),
     LintId::of(format_args::FORMAT_IN_FORMAT_ARGS),
     LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS),
+    LintId::of(functions::RESULT_LARGE_ERR),
     LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),
     LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
     LintId::of(loops::MANUAL_MEMCPY),
     LintId::of(loops::MISSING_SPIN_LOOP),
     LintId::of(loops::NEEDLESS_COLLECT),
     LintId::of(manual_retain::MANUAL_RETAIN),
+    LintId::of(methods::COLLAPSIBLE_STR_REPLACE),
     LintId::of(methods::EXPECT_FUN_CALL),
     LintId::of(methods::EXTEND_WITH_DRAIN),
     LintId::of(methods::ITER_NTH),
diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs
index a7339ef2721..dd1e1e1a8e3 100644
--- a/clippy_lints/src/lib.register_restriction.rs
+++ b/clippy_lints/src/lib.register_restriction.rs
@@ -4,11 +4,11 @@
 
 store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
     LintId::of(as_conversions::AS_CONVERSIONS),
-    LintId::of(as_underscore::AS_UNDERSCORE),
     LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX),
     LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
     LintId::of(assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES),
     LintId::of(attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON),
+    LintId::of(casts::AS_UNDERSCORE),
     LintId::of(casts::FN_TO_NUMERIC_CAST_ANY),
     LintId::of(create_dir::CREATE_DIR),
     LintId::of(dbg_macro::DBG_MACRO),
@@ -30,7 +30,6 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
     LintId::of(large_include_file::LARGE_INCLUDE_FILE),
     LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE),
     LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION),
-    LintId::of(map_err_ignore::MAP_ERR_IGNORE),
     LintId::of(matches::REST_PAT_IN_FULLY_BOUND_STRUCTS),
     LintId::of(matches::TRY_ERR),
     LintId::of(matches::WILDCARD_ENUM_MATCH_ARM),
@@ -39,7 +38,9 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
     LintId::of(methods::EXPECT_USED),
     LintId::of(methods::FILETYPE_IS_FILE),
     LintId::of(methods::GET_UNWRAP),
+    LintId::of(methods::MAP_ERR_IGNORE),
     LintId::of(methods::UNWRAP_USED),
+    LintId::of(methods::VERBOSE_FILE_READS),
     LintId::of(misc_early::SEPARATED_LITERAL_SUFFIX),
     LintId::of(misc_early::UNNEEDED_FIELD_PATTERN),
     LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX),
@@ -81,7 +82,6 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
     LintId::of(unicode::NON_ASCII_LITERAL),
     LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS),
     LintId::of(unwrap_in_result::UNWRAP_IN_RESULT),
-    LintId::of(verbose_file_reads::VERBOSE_FILE_READS),
     LintId::of(write::PRINT_STDERR),
     LintId::of(write::PRINT_STDOUT),
     LintId::of(write::USE_DEBUG),
diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs
index bfa654238f1..b5cb078e7a3 100644
--- a/clippy_lints/src/lib.register_style.rs
+++ b/clippy_lints/src/lib.register_style.rs
@@ -29,7 +29,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
     LintId::of(functions::DOUBLE_MUST_USE),
     LintId::of(functions::MUST_USE_UNIT),
     LintId::of(functions::RESULT_UNIT_ERR),
-    LintId::of(get_first::GET_FIRST),
     LintId::of(inherent_to_string::INHERENT_TO_STRING),
     LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
     LintId::of(len_zero::COMPARISON_TO_EMPTY),
@@ -45,7 +44,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
     LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
     LintId::of(manual_bits::MANUAL_BITS),
     LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
-    LintId::of(map_clone::MAP_CLONE),
     LintId::of(match_result_ok::MATCH_RESULT_OK),
     LintId::of(matches::COLLAPSIBLE_MATCH),
     LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
@@ -61,6 +59,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
     LintId::of(methods::CHARS_LAST_CMP),
     LintId::of(methods::CHARS_NEXT_CMP),
     LintId::of(methods::ERR_EXPECT),
+    LintId::of(methods::GET_FIRST),
     LintId::of(methods::INTO_ITER_ON_REF),
     LintId::of(methods::IS_DIGIT_ASCII_RADIX),
     LintId::of(methods::ITER_CLONED_COLLECT),
@@ -68,7 +67,9 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
     LintId::of(methods::ITER_NTH_ZERO),
     LintId::of(methods::ITER_SKIP_NEXT),
     LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
+    LintId::of(methods::MAP_CLONE),
     LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
+    LintId::of(methods::MUT_MUTEX_LOCK),
     LintId::of(methods::NEW_RET_NO_SELF),
     LintId::of(methods::OBFUSCATED_IF_ELSE),
     LintId::of(methods::OK_EXPECT),
@@ -88,7 +89,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
     LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
     LintId::of(misc_early::MIXED_CASE_HEX_LITERALS),
     LintId::of(misc_early::REDUNDANT_PATTERN),
-    LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
     LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
     LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
     LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS),
diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs
index 964992bd94f..8f131bbf98b 100644
--- a/clippy_lints/src/lib.register_suspicious.rs
+++ b/clippy_lints/src/lib.register_suspicious.rs
@@ -11,6 +11,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
     LintId::of(casts::CAST_ABS_TO_UNSIGNED),
     LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
     LintId::of(casts::CAST_ENUM_TRUNCATION),
+    LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS),
     LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
     LintId::of(drop_forget_ref::DROP_NON_DROP),
     LintId::of(drop_forget_ref::FORGET_NON_DROP),
@@ -24,6 +25,8 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
     LintId::of(loops::MUT_RANGE_BOUND),
     LintId::of(methods::NO_EFFECT_REPLACE),
     LintId::of(methods::SUSPICIOUS_MAP),
+    LintId::of(methods::SUSPICIOUS_TO_OWNED),
+    LintId::of(multi_assignments::MULTI_ASSIGNMENTS),
     LintId::of(mut_key::MUTABLE_KEY_TYPE),
     LintId::of(octal_escapes::OCTAL_ESCAPES),
     LintId::of(operators::FLOAT_EQUALITY_WITHOUT_ABS),
@@ -32,4 +35,6 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
     LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
     LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
     LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF),
+    LintId::of(unused_peekable::UNUSED_PEEKABLE),
+    LintId::of(write::POSITIONAL_NAMED_FORMAT_PARAMETERS),
 ])
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index ec5c73c1357..dfdaf90f09f 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -171,7 +171,6 @@ mod renamed_lints;
 mod almost_complete_letter_range;
 mod approx_const;
 mod as_conversions;
-mod as_underscore;
 mod asm_syntax;
 mod assertions_on_constants;
 mod assertions_on_result_states;
@@ -181,12 +180,8 @@ mod await_holding_invalid;
 mod blocks_in_if_conditions;
 mod bool_assert_comparison;
 mod booleans;
-mod borrow_as_ptr;
 mod borrow_deref_ref;
-mod bytecount;
-mod bytes_count_to_len;
 mod cargo;
-mod case_sensitive_file_extension_comparisons;
 mod casts;
 mod checked_conversions;
 mod cognitive_complexity;
@@ -239,7 +234,6 @@ mod from_over_into;
 mod from_str_radix_10;
 mod functions;
 mod future_not_send;
-mod get_first;
 mod if_let_mutex;
 mod if_not_else;
 mod if_then_some_else_none;
@@ -276,12 +270,10 @@ mod manual_async_fn;
 mod manual_bits;
 mod manual_instant_elapsed;
 mod manual_non_exhaustive;
-mod manual_ok_or;
 mod manual_rem_euclid;
 mod manual_retain;
+mod manual_string_new;
 mod manual_strip;
-mod map_clone;
-mod map_err_ignore;
 mod map_unit_fn;
 mod match_result_ok;
 mod matches;
@@ -298,9 +290,9 @@ mod missing_enforced_import_rename;
 mod missing_inline;
 mod mixed_read_write_in_expression;
 mod module_style;
+mod multi_assignments;
 mod mut_key;
 mod mut_mut;
-mod mut_mutex_lock;
 mod mut_reference;
 mod mutable_debug_assertion;
 mod mutex_atomic;
@@ -325,7 +317,6 @@ mod non_send_fields_in_send_ty;
 mod nonstandard_macro_braces;
 mod octal_escapes;
 mod only_used_in_recursion;
-mod open_options;
 mod operators;
 mod option_env_unwrap;
 mod option_if_let_else;
@@ -335,7 +326,6 @@ mod panic_unimplemented;
 mod partialeq_ne_impl;
 mod partialeq_to_none;
 mod pass_by_ref_or_value;
-mod path_buf_push_overwrite;
 mod pattern_type_mismatch;
 mod precedence;
 mod ptr;
@@ -355,7 +345,6 @@ mod redundant_static_lifetimes;
 mod ref_option_ref;
 mod reference;
 mod regex;
-mod repeat_once;
 mod return_self_not_must_use;
 mod returns;
 mod same_name_method;
@@ -367,7 +356,6 @@ mod single_char_lifetime_names;
 mod single_component_path_imports;
 mod size_of_in_element_count;
 mod slow_vector_initialization;
-mod stable_sort_primitive;
 mod std_instead_of_core;
 mod strings;
 mod strlen_on_c_strings;
@@ -381,23 +369,21 @@ mod to_digit_is_some;
 mod trailing_empty_array;
 mod trait_bounds;
 mod transmute;
-mod transmuting_null;
 mod types;
 mod undocumented_unsafe_blocks;
 mod unicode;
 mod uninit_vec;
-mod unit_hash;
 mod unit_return_expecting_ord;
 mod unit_types;
 mod unnamed_address;
 mod unnecessary_owned_empty_strings;
 mod unnecessary_self_imports;
-mod unnecessary_sort_by;
 mod unnecessary_wraps;
 mod unnested_or_patterns;
 mod unsafe_removed_from_name;
 mod unused_async;
 mod unused_io_amount;
+mod unused_peekable;
 mod unused_rounding;
 mod unused_self;
 mod unused_unit;
@@ -408,8 +394,6 @@ mod use_self;
 mod useless_conversion;
 mod vec;
 mod vec_init_then_push;
-mod vec_resize_to_zero;
-mod verbose_file_reads;
 mod wildcard_imports;
 mod write;
 mod zero_div_zero;
@@ -597,7 +581,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(blocks_in_if_conditions::BlocksInIfConditions));
     store.register_late_pass(|| Box::new(unicode::Unicode));
     store.register_late_pass(|| Box::new(uninit_vec::UninitVec));
-    store.register_late_pass(|| Box::new(unit_hash::UnitHash));
     store.register_late_pass(|| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd));
     store.register_late_pass(|| Box::new(strings::StringAdd));
     store.register_late_pass(|| Box::new(implicit_return::ImplicitReturn));
@@ -635,8 +618,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move || Box::new(needless_question_mark::NeedlessQuestionMark));
     store.register_late_pass(move || Box::new(casts::Casts::new(msrv)));
     store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv)));
-    store.register_late_pass(move || Box::new(map_clone::MapClone::new(msrv)));
-
     store.register_late_pass(|| Box::new(size_of_in_element_count::SizeOfInElementCount));
     store.register_late_pass(|| Box::new(same_name_method::SameNameMethod));
     let max_suggested_slice_pattern_length = conf.max_suggested_slice_pattern_length;
@@ -646,7 +627,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
             msrv,
         ))
     });
-    store.register_late_pass(|| Box::new(map_err_ignore::MapErrIgnore));
     store.register_late_pass(|| Box::new(shadow::Shadow::default()));
     store.register_late_pass(|| Box::new(unit_types::UnitTypes));
     store.register_late_pass(|| Box::new(loops::Loops));
@@ -654,7 +634,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(lifetimes::Lifetimes));
     store.register_late_pass(|| Box::new(entry::HashMapPass));
     store.register_late_pass(|| Box::new(minmax::MinMaxPass));
-    store.register_late_pass(|| Box::new(open_options::OpenOptions));
     store.register_late_pass(|| Box::new(zero_div_zero::ZeroDiv));
     store.register_late_pass(|| Box::new(mutex_atomic::Mutex));
     store.register_late_pass(|| Box::new(needless_update::NeedlessUpdate));
@@ -690,10 +669,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move || Box::new(disallowed_names::DisallowedNames::new(disallowed_names.clone())));
     let too_many_arguments_threshold = conf.too_many_arguments_threshold;
     let too_many_lines_threshold = conf.too_many_lines_threshold;
+    let large_error_threshold = conf.large_error_threshold;
     store.register_late_pass(move || {
         Box::new(functions::Functions::new(
             too_many_arguments_threshold,
             too_many_lines_threshold,
+            large_error_threshold,
         ))
     });
     let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::<FxHashSet<_>>();
@@ -720,7 +701,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     );
     store.register_late_pass(move || Box::new(pass_by_ref_or_value));
     store.register_late_pass(|| Box::new(ref_option_ref::RefOptionRef));
-    store.register_late_pass(|| Box::new(bytecount::ByteCount));
     store.register_late_pass(|| Box::new(infinite_iter::InfiniteIter));
     store.register_late_pass(|| Box::new(inline_fn_without_body::InlineFnWithoutBody));
     store.register_late_pass(|| Box::new(useless_conversion::UselessConversion::default()));
@@ -738,12 +718,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(ptr_offset_with_cast::PtrOffsetWithCast));
     store.register_late_pass(|| Box::new(redundant_clone::RedundantClone));
     store.register_late_pass(|| Box::new(slow_vector_initialization::SlowVectorInit));
-    store.register_late_pass(|| Box::new(unnecessary_sort_by::UnnecessarySortBy));
     store.register_late_pass(move || Box::new(unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api)));
     store.register_late_pass(|| Box::new(assertions_on_constants::AssertionsOnConstants));
     store.register_late_pass(|| Box::new(assertions_on_result_states::AssertionsOnResultStates));
-    store.register_late_pass(|| Box::new(transmuting_null::TransmutingNull));
-    store.register_late_pass(|| Box::new(path_buf_push_overwrite::PathBufPushOverwrite));
     store.register_late_pass(|| Box::new(inherent_to_string::InherentToString));
     let max_trait_bounds = conf.max_trait_bounds;
     store.register_late_pass(move || Box::new(trait_bounds::TraitBounds::new(max_trait_bounds)));
@@ -819,18 +796,15 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap));
     let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports;
     store.register_late_pass(move || Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)));
-    store.register_late_pass(|| Box::new(verbose_file_reads::VerboseFileReads));
     store.register_late_pass(|| Box::new(redundant_pub_crate::RedundantPubCrate::default()));
     store.register_late_pass(|| Box::new(unnamed_address::UnnamedAddress));
-    store.register_late_pass(|| Box::new(dereference::Dereferencing::default()));
+    store.register_late_pass(move || Box::new(dereference::Dereferencing::new(msrv)));
     store.register_late_pass(|| Box::new(option_if_let_else::OptionIfLetElse));
     store.register_late_pass(|| Box::new(future_not_send::FutureNotSend));
     store.register_late_pass(|| Box::new(if_let_mutex::IfLetMutex));
     store.register_late_pass(|| Box::new(if_not_else::IfNotElse));
     store.register_late_pass(|| Box::new(equatable_if_let::PatternEquality));
-    store.register_late_pass(|| Box::new(mut_mutex_lock::MutMutexLock));
     store.register_late_pass(|| Box::new(manual_async_fn::ManualAsyncFn));
-    store.register_late_pass(|| Box::new(vec_resize_to_zero::VecResizeToZero));
     store.register_late_pass(|| Box::new(panic_in_result_fn::PanicInResultFn));
     let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
     store.register_early_pass(move || {
@@ -842,10 +816,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(&macro_matcher)));
     store.register_late_pass(|| Box::new(macro_use::MacroUseImports::default()));
     store.register_late_pass(|| Box::new(pattern_type_mismatch::PatternTypeMismatch));
-    store.register_late_pass(|| Box::new(stable_sort_primitive::StableSortPrimitive));
-    store.register_late_pass(|| Box::new(repeat_once::RepeatOnce));
     store.register_late_pass(|| Box::new(unwrap_in_result::UnwrapInResult));
-    store.register_late_pass(|| Box::new(manual_ok_or::ManualOkOr));
     store.register_late_pass(|| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
     store.register_late_pass(|| Box::new(async_yields_async::AsyncYieldsAsync));
     let disallowed_methods = conf.disallowed_methods.clone();
@@ -857,9 +828,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(strings::StringToString));
     store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues));
     store.register_late_pass(|| Box::new(vec_init_then_push::VecInitThenPush::default()));
-    store.register_late_pass(|| {
-        Box::new(case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons)
-    });
     store.register_late_pass(|| Box::new(redundant_slicing::RedundantSlicing));
     store.register_late_pass(|| Box::new(from_str_radix_10::FromStrRadix10));
     store.register_late_pass(move || Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv)));
@@ -894,11 +862,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(return_self_not_must_use::ReturnSelfNotMustUse));
     store.register_late_pass(|| Box::new(init_numbered_fields::NumberedFields));
     store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames));
-    store.register_late_pass(move || Box::new(borrow_as_ptr::BorrowAsPtr::new(msrv)));
     store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv)));
     store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation));
     store.register_early_pass(|| Box::new(doc_link_with_quotes::DocLinkWithQuotes));
-    store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion));
+    store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion::default()));
     let allow_dbg_in_tests = conf.allow_dbg_in_tests;
     store.register_late_pass(move || Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests)));
     let cargo_ignore_publish = conf.cargo_ignore_publish;
@@ -912,18 +879,15 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings));
     store.register_early_pass(|| Box::new(pub_use::PubUse));
     store.register_late_pass(|| Box::new(format_push_string::FormatPushString));
-    store.register_late_pass(|| Box::new(bytes_count_to_len::BytesCountToLen));
     let max_include_file_size = conf.max_include_file_size;
     store.register_late_pass(move || Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size)));
     store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace));
     store.register_late_pass(|| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
     store.register_early_pass(|| Box::new(duplicate_mod::DuplicateMod::default()));
-    store.register_late_pass(|| Box::new(get_first::GetFirst));
     store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding));
     store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv)));
     store.register_late_pass(|| Box::new(swap_ptr_to_ref::SwapPtrToRef));
     store.register_late_pass(|| Box::new(mismatching_type_param_order::TypeParamMismatch));
-    store.register_late_pass(|| Box::new(as_underscore::AsUnderscore));
     store.register_late_pass(|| Box::new(read_zero_byte_vec::ReadZeroByteVec));
     store.register_late_pass(|| Box::new(default_instead_of_iter_empty::DefaultIterEmpty));
     store.register_late_pass(move || Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv)));
@@ -934,6 +898,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(std_instead_of_core::StdReexports::default()));
     store.register_late_pass(|| Box::new(manual_instant_elapsed::ManualInstantElapsed));
     store.register_late_pass(|| Box::new(partialeq_to_none::PartialeqToNone));
+    store.register_late_pass(|| Box::new(manual_string_new::ManualStringNew));
+    store.register_late_pass(|| Box::new(unused_peekable::UnusedPeekable));
+    store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs
index ddaffc75188..6d987f393fa 100644
--- a/clippy_lints/src/loops/needless_collect.rs
+++ b/clippy_lints/src/loops/needless_collect.rs
@@ -1,5 +1,6 @@
 use super::NEEDLESS_COLLECT;
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
+use clippy_utils::higher;
 use clippy_utils::source::{snippet, snippet_with_applicability};
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
@@ -184,10 +185,19 @@ struct IterFunctionVisitor<'a, 'tcx> {
 impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
     fn visit_block(&mut self, block: &'tcx Block<'tcx>) {
         for (expr, hir_id) in block.stmts.iter().filter_map(get_expr_and_hir_id_from_stmt) {
+            if check_loop_kind(expr).is_some() {
+                continue;
+            }
             self.visit_block_expr(expr, hir_id);
         }
         if let Some(expr) = block.expr {
-            self.visit_block_expr(expr, None);
+            if let Some(loop_kind) = check_loop_kind(expr) {
+                if let LoopKind::Conditional(block_expr) = loop_kind {
+                    self.visit_block_expr(block_expr, None);
+                }
+            } else {
+                self.visit_block_expr(expr, None);
+            }
         }
     }
 
@@ -264,6 +274,28 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
     }
 }
 
+enum LoopKind<'tcx> {
+    Conditional(&'tcx Expr<'tcx>),
+    Loop,
+}
+
+fn check_loop_kind<'tcx>(expr: &Expr<'tcx>) -> Option<LoopKind<'tcx>> {
+    if let Some(higher::WhileLet { let_expr, .. }) = higher::WhileLet::hir(expr) {
+        return Some(LoopKind::Conditional(let_expr));
+    }
+    if let Some(higher::While { condition, .. }) = higher::While::hir(expr) {
+        return Some(LoopKind::Conditional(condition));
+    }
+    if let Some(higher::ForLoop { arg, .. }) = higher::ForLoop::hir(expr) {
+        return Some(LoopKind::Conditional(arg));
+    }
+    if let ExprKind::Loop { .. } = expr.kind {
+        return Some(LoopKind::Loop);
+    }
+
+    None
+}
+
 impl<'tcx> IterFunctionVisitor<'_, 'tcx> {
     fn visit_block_expr(&mut self, expr: &'tcx Expr<'tcx>, hir_id: Option<HirId>) {
         self.current_statement_hir_id = hir_id;
diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs
index a0ca7e6ff1e..2502c8f880d 100644
--- a/clippy_lints/src/manual_async_fn.rs
+++ b/clippy_lints/src/manual_async_fn.rs
@@ -192,7 +192,7 @@ fn suggested_ret(cx: &LateContext<'_>, output: &Ty<'_>) -> Option<(&'static str,
     match output.kind {
         TyKind::Tup(tys) if tys.is_empty() => {
             let sugg = "remove the return type";
-            Some((sugg, "".into()))
+            Some((sugg, String::new()))
         },
         _ => {
             let sugg = "return the output of the future directly";
diff --git a/clippy_lints/src/manual_ok_or.rs b/clippy_lints/src/manual_ok_or.rs
deleted file mode 100644
index cf5004399b8..00000000000
--- a/clippy_lints/src/manual_ok_or.rs
+++ /dev/null
@@ -1,95 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
-use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{is_lang_ctor, path_to_local_id};
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::LangItem::{ResultErr, ResultOk};
-use rustc_hir::{Closure, Expr, ExprKind, PatKind};
-use rustc_lint::LintContext;
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::lint::in_external_macro;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::sym;
-
-declare_clippy_lint! {
-    /// ### What it does
-    ///
-    /// Finds patterns that reimplement `Option::ok_or`.
-    ///
-    /// ### Why is this bad?
-    ///
-    /// Concise code helps focusing on behavior instead of boilerplate.
-    ///
-    /// ### Examples
-    /// ```rust
-    /// let foo: Option<i32> = None;
-    /// foo.map_or(Err("error"), |v| Ok(v));
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust
-    /// let foo: Option<i32> = None;
-    /// foo.ok_or("error");
-    /// ```
-    #[clippy::version = "1.49.0"]
-    pub MANUAL_OK_OR,
-    pedantic,
-    "finds patterns that can be encoded more concisely with `Option::ok_or`"
-}
-
-declare_lint_pass!(ManualOkOr => [MANUAL_OK_OR]);
-
-impl<'tcx> LateLintPass<'tcx> for ManualOkOr {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'tcx>) {
-        if in_external_macro(cx.sess(), scrutinee.span) {
-            return;
-        }
-
-        if_chain! {
-            if let ExprKind::MethodCall(method_segment, [receiver, or_expr, map_expr], _) = scrutinee.kind;
-            if method_segment.ident.name == sym!(map_or);
-            let ty = cx.typeck_results().expr_ty(receiver);
-            if is_type_diagnostic_item(cx, ty, sym::Option);
-            if is_ok_wrapping(cx, map_expr);
-            if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind;
-            if is_lang_ctor(cx, err_path, ResultErr);
-            if let Some(method_receiver_snippet) = snippet_opt(cx, receiver.span);
-            if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span);
-            if let Some(indent) = indent_of(cx, scrutinee.span);
-            then {
-                let reindented_err_arg_snippet =
-                    reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4));
-                span_lint_and_sugg(
-                    cx,
-                    MANUAL_OK_OR,
-                    scrutinee.span,
-                    "this pattern reimplements `Option::ok_or`",
-                    "replace with",
-                    format!(
-                        "{}.ok_or({})",
-                        method_receiver_snippet,
-                        reindented_err_arg_snippet
-                    ),
-                    Applicability::MachineApplicable,
-                );
-            }
-        }
-    }
-}
-
-fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
-    if let ExprKind::Path(ref qpath) = map_expr.kind {
-        if is_lang_ctor(cx, qpath, ResultOk) {
-            return true;
-        }
-    }
-    if_chain! {
-        if let ExprKind::Closure(&Closure { body, .. }) = map_expr.kind;
-        let body = cx.tcx.hir().body(body);
-        if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind;
-        if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind;
-        if is_lang_ctor(cx, ok_path, ResultOk);
-        then { path_to_local_id(ok_arg, param_id) } else { false }
-    }
-}
diff --git a/clippy_lints/src/manual_string_new.rs b/clippy_lints/src/manual_string_new.rs
new file mode 100644
index 00000000000..a90eaa8fdcb
--- /dev/null
+++ b/clippy_lints/src/manual_string_new.rs
@@ -0,0 +1,140 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use rustc_ast::LitKind;
+use rustc_errors::Applicability::MachineApplicable;
+use rustc_hir::{Expr, ExprKind, PathSegment, QPath, TyKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::{sym, symbol, Span};
+
+declare_clippy_lint! {
+    /// ### What it does
+    ///
+    /// Checks for usage of `""` to create a `String`, such as `"".to_string()`, `"".to_owned()`,
+    /// `String::from("")` and others.
+    ///
+    /// ### Why is this bad?
+    ///
+    /// Different ways of creating an empty string makes your code less standardized, which can
+    /// be confusing.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let a = "".to_string();
+    /// let b: String = "".into();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let a = String::new();
+    /// let b = String::new();
+    /// ```
+    #[clippy::version = "1.65.0"]
+    pub MANUAL_STRING_NEW,
+    pedantic,
+    "empty String is being created manually"
+}
+declare_lint_pass!(ManualStringNew => [MANUAL_STRING_NEW]);
+
+impl LateLintPass<'_> for ManualStringNew {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+        if expr.span.from_expansion() {
+            return;
+        }
+
+        let ty = cx.typeck_results().expr_ty(expr);
+        match ty.kind() {
+            ty::Adt(adt_def, _) if adt_def.is_struct() => {
+                if !cx.tcx.is_diagnostic_item(sym::String, adt_def.did()) {
+                    return;
+                }
+            },
+            _ => return,
+        }
+
+        match expr.kind {
+            ExprKind::Call(func, args) => {
+                parse_call(cx, expr.span, func, args);
+            },
+            ExprKind::MethodCall(path_segment, args, _) => {
+                parse_method_call(cx, expr.span, path_segment, args);
+            },
+            _ => (),
+        }
+    }
+}
+
+/// Checks if an expression's kind corresponds to an empty &str.
+fn is_expr_kind_empty_str(expr_kind: &ExprKind<'_>) -> bool {
+    if  let ExprKind::Lit(lit) = expr_kind &&
+        let LitKind::Str(value, _) = lit.node &&
+        value == symbol::kw::Empty
+    {
+        return true;
+    }
+
+    false
+}
+
+fn warn_then_suggest(cx: &LateContext<'_>, span: Span) {
+    span_lint_and_sugg(
+        cx,
+        MANUAL_STRING_NEW,
+        span,
+        "empty String is being created manually",
+        "consider using",
+        "String::new()".into(),
+        MachineApplicable,
+    );
+}
+
+/// Tries to parse an expression as a method call, emitting the warning if necessary.
+fn parse_method_call(cx: &LateContext<'_>, span: Span, path_segment: &PathSegment<'_>, args: &[Expr<'_>]) {
+    if args.is_empty() {
+        // When parsing TryFrom::try_from(...).expect(...), we will have more than 1 arg.
+        return;
+    }
+
+    let ident = path_segment.ident.as_str();
+    let method_arg_kind = &args[0].kind;
+    if ["to_string", "to_owned", "into"].contains(&ident) && is_expr_kind_empty_str(method_arg_kind) {
+        warn_then_suggest(cx, span);
+    } else if let ExprKind::Call(func, args) = method_arg_kind {
+        // If our first argument is a function call itself, it could be an `unwrap`-like function.
+        // E.g. String::try_from("hello").unwrap(), TryFrom::try_from("").expect("hello"), etc.
+        parse_call(cx, span, func, args);
+    }
+}
+
+/// Tries to parse an expression as a function call, emitting the warning if necessary.
+fn parse_call(cx: &LateContext<'_>, span: Span, func: &Expr<'_>, args: &[Expr<'_>]) {
+    if args.len() != 1 {
+        return;
+    }
+
+    let arg_kind = &args[0].kind;
+    if let ExprKind::Path(qpath) = &func.kind {
+        if let QPath::TypeRelative(_, _) = qpath {
+            // String::from(...) or String::try_from(...)
+            if  let QPath::TypeRelative(ty, path_seg) = qpath &&
+                [sym::from, sym::try_from].contains(&path_seg.ident.name) &&
+                let TyKind::Path(qpath) = &ty.kind &&
+                let QPath::Resolved(_, path) = qpath &&
+                let [path_seg] = path.segments &&
+                path_seg.ident.name == sym::String &&
+                is_expr_kind_empty_str(arg_kind)
+            {
+                warn_then_suggest(cx, span);
+            }
+        } else if let QPath::Resolved(_, path) = qpath {
+            // From::from(...) or TryFrom::try_from(...)
+            if  let [path_seg1, path_seg2] = path.segments &&
+                is_expr_kind_empty_str(arg_kind) && (
+                    (path_seg1.ident.name == sym::From && path_seg2.ident.name == sym::from) ||
+                    (path_seg1.ident.name == sym::TryFrom && path_seg2.ident.name == sym::try_from)
+                )
+            {
+                warn_then_suggest(cx, span);
+            }
+        }
+    }
+}
diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs
deleted file mode 100644
index 95c312f1fe2..00000000000
--- a/clippy_lints/src/map_clone.rs
+++ /dev/null
@@ -1,167 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
-use clippy_utils::{is_trait_method, meets_msrv, msrvs, peel_blocks};
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir as hir;
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::mir::Mutability;
-use rustc_middle::ty;
-use rustc_middle::ty::adjustment::Adjust;
-use rustc_semver::RustcVersion;
-use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::symbol::Ident;
-use rustc_span::{sym, Span};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for usage of `map(|x| x.clone())` or
-    /// dereferencing closures for `Copy` types, on `Iterator` or `Option`,
-    /// and suggests `cloned()` or `copied()` instead
-    ///
-    /// ### Why is this bad?
-    /// Readability, this can be written more concisely
-    ///
-    /// ### Example
-    /// ```rust
-    /// let x = vec![42, 43];
-    /// let y = x.iter();
-    /// let z = y.map(|i| *i);
-    /// ```
-    ///
-    /// The correct use would be:
-    ///
-    /// ```rust
-    /// let x = vec![42, 43];
-    /// let y = x.iter();
-    /// let z = y.cloned();
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub MAP_CLONE,
-    style,
-    "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types"
-}
-
-pub struct MapClone {
-    msrv: Option<RustcVersion>,
-}
-
-impl_lint_pass!(MapClone => [MAP_CLONE]);
-
-impl MapClone {
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
-        Self { msrv }
-    }
-}
-
-impl<'tcx> LateLintPass<'tcx> for MapClone {
-    fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) {
-        if e.span.from_expansion() {
-            return;
-        }
-
-        if_chain! {
-            if let hir::ExprKind::MethodCall(method, args, _) = e.kind;
-            if args.len() == 2;
-            if method.ident.name == sym::map;
-            let ty = cx.typeck_results().expr_ty(&args[0]);
-            if is_type_diagnostic_item(cx, ty, sym::Option) || is_trait_method(cx, e, sym::Iterator);
-            if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = args[1].kind;
-            then {
-                let closure_body = cx.tcx.hir().body(body);
-                let closure_expr = peel_blocks(&closure_body.value);
-                match closure_body.params[0].pat.kind {
-                    hir::PatKind::Ref(inner, hir::Mutability::Not) => if let hir::PatKind::Binding(
-                        hir::BindingAnnotation::Unannotated, .., name, None
-                    ) = inner.kind {
-                        if ident_eq(name, closure_expr) {
-                            self.lint_explicit_closure(cx, e.span, args[0].span, true);
-                        }
-                    },
-                    hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, .., name, None) => {
-                        match closure_expr.kind {
-                            hir::ExprKind::Unary(hir::UnOp::Deref, inner) => {
-                                if ident_eq(name, inner) {
-                                    if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() {
-                                        self.lint_explicit_closure(cx, e.span, args[0].span, true);
-                                    }
-                                }
-                            },
-                            hir::ExprKind::MethodCall(method, [obj], _) => if_chain! {
-                                if ident_eq(name, obj) && method.ident.name == sym::clone;
-                                if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id);
-                                if let Some(trait_id) = cx.tcx.trait_of_item(fn_id);
-                                if cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id);
-                                // no autoderefs
-                                if !cx.typeck_results().expr_adjustments(obj).iter()
-                                    .any(|a| matches!(a.kind, Adjust::Deref(Some(..))));
-                                then {
-                                    let obj_ty = cx.typeck_results().expr_ty(obj);
-                                    if let ty::Ref(_, ty, mutability) = obj_ty.kind() {
-                                        if matches!(mutability, Mutability::Not) {
-                                            let copy = is_copy(cx, *ty);
-                                            self.lint_explicit_closure(cx, e.span, args[0].span, copy);
-                                        }
-                                    } else {
-                                        lint_needless_cloning(cx, e.span, args[0].span);
-                                    }
-                                }
-                            },
-                            _ => {},
-                        }
-                    },
-                    _ => {},
-                }
-            }
-        }
-    }
-
-    extract_msrv_attr!(LateContext);
-}
-
-fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool {
-    if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = path.kind {
-        path.segments.len() == 1 && path.segments[0].ident == name
-    } else {
-        false
-    }
-}
-
-fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
-    span_lint_and_sugg(
-        cx,
-        MAP_CLONE,
-        root.trim_start(receiver).unwrap(),
-        "you are needlessly cloning iterator elements",
-        "remove the `map` call",
-        String::new(),
-        Applicability::MachineApplicable,
-    );
-}
-
-impl MapClone {
-    fn lint_explicit_closure(&self, cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool) {
-        let mut applicability = Applicability::MachineApplicable;
-
-        let (message, sugg_method) = if is_copy && meets_msrv(self.msrv, msrvs::ITERATOR_COPIED) {
-            ("you are using an explicit closure for copying elements", "copied")
-        } else {
-            ("you are using an explicit closure for cloning elements", "cloned")
-        };
-
-        span_lint_and_sugg(
-            cx,
-            MAP_CLONE,
-            replace,
-            message,
-            &format!("consider calling the dedicated `{}` method", sugg_method),
-            format!(
-                "{}.{}()",
-                snippet_with_applicability(cx, root, "..", &mut applicability),
-                sugg_method,
-            ),
-            applicability,
-        );
-    }
-}
diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs
deleted file mode 100644
index 1e542447c96..00000000000
--- a/clippy_lints/src/map_err_ignore.rs
+++ /dev/null
@@ -1,154 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_help;
-use rustc_hir::{CaptureBy, Closure, Expr, ExprKind, PatKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for instances of `map_err(|_| Some::Enum)`
-    ///
-    /// ### Why is this bad?
-    /// This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error
-    ///
-    /// ### Example
-    /// Before:
-    /// ```rust
-    /// use std::fmt;
-    ///
-    /// #[derive(Debug)]
-    /// enum Error {
-    ///     Indivisible,
-    ///     Remainder(u8),
-    /// }
-    ///
-    /// impl fmt::Display for Error {
-    ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-    ///         match self {
-    ///             Error::Indivisible => write!(f, "could not divide input by three"),
-    ///             Error::Remainder(remainder) => write!(
-    ///                 f,
-    ///                 "input is not divisible by three, remainder = {}",
-    ///                 remainder
-    ///             ),
-    ///         }
-    ///     }
-    /// }
-    ///
-    /// impl std::error::Error for Error {}
-    ///
-    /// fn divisible_by_3(input: &str) -> Result<(), Error> {
-    ///     input
-    ///         .parse::<i32>()
-    ///         .map_err(|_| Error::Indivisible)
-    ///         .map(|v| v % 3)
-    ///         .and_then(|remainder| {
-    ///             if remainder == 0 {
-    ///                 Ok(())
-    ///             } else {
-    ///                 Err(Error::Remainder(remainder as u8))
-    ///             }
-    ///         })
-    /// }
-    ///  ```
-    ///
-    ///  After:
-    ///  ```rust
-    /// use std::{fmt, num::ParseIntError};
-    ///
-    /// #[derive(Debug)]
-    /// enum Error {
-    ///     Indivisible(ParseIntError),
-    ///     Remainder(u8),
-    /// }
-    ///
-    /// impl fmt::Display for Error {
-    ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-    ///         match self {
-    ///             Error::Indivisible(_) => write!(f, "could not divide input by three"),
-    ///             Error::Remainder(remainder) => write!(
-    ///                 f,
-    ///                 "input is not divisible by three, remainder = {}",
-    ///                 remainder
-    ///             ),
-    ///         }
-    ///     }
-    /// }
-    ///
-    /// impl std::error::Error for Error {
-    ///     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
-    ///         match self {
-    ///             Error::Indivisible(source) => Some(source),
-    ///             _ => None,
-    ///         }
-    ///     }
-    /// }
-    ///
-    /// fn divisible_by_3(input: &str) -> Result<(), Error> {
-    ///     input
-    ///         .parse::<i32>()
-    ///         .map_err(Error::Indivisible)
-    ///         .map(|v| v % 3)
-    ///         .and_then(|remainder| {
-    ///             if remainder == 0 {
-    ///                 Ok(())
-    ///             } else {
-    ///                 Err(Error::Remainder(remainder as u8))
-    ///             }
-    ///         })
-    /// }
-    /// ```
-    #[clippy::version = "1.48.0"]
-    pub MAP_ERR_IGNORE,
-    restriction,
-    "`map_err` should not ignore the original error"
-}
-
-declare_lint_pass!(MapErrIgnore => [MAP_ERR_IGNORE]);
-
-impl<'tcx> LateLintPass<'tcx> for MapErrIgnore {
-    // do not try to lint if this is from a macro or desugaring
-    fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) {
-        if e.span.from_expansion() {
-            return;
-        }
-
-        // check if this is a method call (e.g. x.foo())
-        if let ExprKind::MethodCall(method, [_, arg], _) = e.kind {
-            // only work if the method name is `map_err` and there are only 2 arguments (e.g. x.map_err(|_|[1]
-            // Enum::Variant[2]))
-            if method.ident.name == sym!(map_err) {
-                // make sure the first argument is a closure, and grab the CaptureRef, BodyId, and fn_decl_span
-                // fields
-                if let ExprKind::Closure(&Closure {
-                    capture_clause,
-                    body,
-                    fn_decl_span,
-                    ..
-                }) = arg.kind
-                {
-                    // check if this is by Reference (meaning there's no move statement)
-                    if capture_clause == CaptureBy::Ref {
-                        // Get the closure body to check the parameters and values
-                        let closure_body = cx.tcx.hir().body(body);
-                        // make sure there's only one parameter (`|_|`)
-                        if closure_body.params.len() == 1 {
-                            // make sure that parameter is the wild token (`_`)
-                            if let PatKind::Wild = closure_body.params[0].pat.kind {
-                                // span the area of the closure capture and warn that the
-                                // original error will be thrown away
-                                span_lint_and_help(
-                                    cx,
-                                    MAP_ERR_IGNORE,
-                                    fn_decl_span,
-                                    "`map_err(|_|...` wildcard pattern discards the original error",
-                                    None,
-                                    "consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)",
-                                );
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs
index 0da4833f1df..34cc082687e 100644
--- a/clippy_lints/src/matches/match_like_matches.rs
+++ b/clippy_lints/src/matches/match_like_matches.rs
@@ -1,10 +1,11 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::is_wild;
 use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::span_contains_comment;
 use rustc_ast::{Attribute, LitKind};
 use rustc_errors::Applicability;
 use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Guard, Pat};
-use rustc_lint::LateContext;
+use rustc_lint::{LateContext, LintContext};
 use rustc_middle::ty;
 use rustc_span::source_map::Spanned;
 
@@ -76,6 +77,7 @@ where
         >,
 {
     if_chain! {
+        if !span_contains_comment(cx.sess().source_map(), expr.span);
         if iter.len() >= 2;
         if cx.typeck_results().expr_ty(expr).is_bool();
         if let Some((_, last_pat_opt, last_expr, _)) = iter.next_back();
diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs
index fa19cddd35e..6f037339ec7 100644
--- a/clippy_lints/src/matches/needless_match.rs
+++ b/clippy_lints/src/matches/needless_match.rs
@@ -8,7 +8,7 @@ use clippy_utils::{
 };
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::OptionNone;
-use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, FnRetTy, Node, Pat, PatKind, Path, QPath};
+use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, FnRetTy, Guard, Node, Pat, PatKind, Path, QPath};
 use rustc_lint::LateContext;
 use rustc_span::sym;
 use rustc_typeck::hir_ty_to_ty;
@@ -65,8 +65,26 @@ pub(crate) fn check_if_let<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'_>, if_let:
 fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>]) -> bool {
     for arm in arms {
         let arm_expr = peel_blocks_with_stmt(arm.body);
+
+        if let Some(guard_expr) = &arm.guard {
+            match guard_expr {
+                // gives up if `pat if expr` can have side effects
+                Guard::If(if_cond) => {
+                    if if_cond.can_have_side_effects() {
+                        return false;
+                    }
+                },
+                // gives up `pat if let ...` arm
+                Guard::IfLet(_) => {
+                    return false;
+                },
+            };
+        }
+
         if let PatKind::Wild = arm.pat.kind {
-            return eq_expr_value(cx, match_expr, strip_return(arm_expr));
+            if !eq_expr_value(cx, match_expr, strip_return(arm_expr)) {
+                return false;
+            }
         } else if !pat_same_as_expr(arm.pat, arm_expr) {
             return false;
         }
diff --git a/clippy_lints/src/methods/bytecount.rs b/clippy_lints/src/methods/bytecount.rs
new file mode 100644
index 00000000000..6a7c63d76f7
--- /dev/null
+++ b/clippy_lints/src/methods/bytecount.rs
@@ -0,0 +1,70 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::match_type;
+use clippy_utils::visitors::is_local_used;
+use clippy_utils::{path_to_local_id, paths, peel_blocks, peel_ref_operators, strip_pat_refs};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, PatKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, UintTy};
+use rustc_span::sym;
+
+use super::NAIVE_BYTECOUNT;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    filter_recv: &'tcx Expr<'_>,
+    filter_arg: &'tcx Expr<'_>,
+) {
+    if_chain! {
+        if let ExprKind::Closure(&Closure { body, .. }) = filter_arg.kind;
+        let body = cx.tcx.hir().body(body);
+        if let [param] = body.params;
+        if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind;
+        if let ExprKind::Binary(ref op, l, r) = body.value.kind;
+        if op.node == BinOpKind::Eq;
+        if match_type(cx,
+                    cx.typeck_results().expr_ty(filter_recv).peel_refs(),
+                    &paths::SLICE_ITER);
+        let operand_is_arg = |expr| {
+            let expr = peel_ref_operators(cx, peel_blocks(expr));
+            path_to_local_id(expr, arg_id)
+        };
+        let needle = if operand_is_arg(l) {
+            r
+        } else if operand_is_arg(r) {
+            l
+        } else {
+            return;
+        };
+        if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind();
+        if !is_local_used(cx, needle, arg_id);
+        then {
+            let haystack = if let ExprKind::MethodCall(path, args, _) =
+                    filter_recv.kind {
+                let p = path.ident.name;
+                if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
+                    &args[0]
+                } else {
+                    filter_recv
+                }
+            } else {
+                filter_recv
+            };
+            let mut applicability = Applicability::MaybeIncorrect;
+            span_lint_and_sugg(
+                cx,
+                NAIVE_BYTECOUNT,
+                expr.span,
+                "you appear to be counting bytes the naive way",
+                "consider using the bytecount crate",
+                format!("bytecount::count({}, {})",
+                        snippet_with_applicability(cx, haystack.span, "..", &mut applicability),
+                        snippet_with_applicability(cx, needle.span, "..", &mut applicability)),
+                applicability,
+            );
+        }
+    };
+}
diff --git a/clippy_lints/src/methods/bytes_count_to_len.rs b/clippy_lints/src/methods/bytes_count_to_len.rs
new file mode 100644
index 00000000000..fcfc25b523d
--- /dev/null
+++ b/clippy_lints/src/methods/bytes_count_to_len.rs
@@ -0,0 +1,37 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_type_diagnostic_item;
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::BYTES_COUNT_TO_LEN;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx hir::Expr<'_>,
+    count_recv: &'tcx hir::Expr<'_>,
+    bytes_recv: &'tcx hir::Expr<'_>,
+) {
+    if_chain! {
+        if let Some(bytes_id) = cx.typeck_results().type_dependent_def_id(count_recv.hir_id);
+        if let Some(impl_id) = cx.tcx.impl_of_method(bytes_id);
+        if cx.tcx.type_of(impl_id).is_str();
+        let ty = cx.typeck_results().expr_ty(bytes_recv).peel_refs();
+        if ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String);
+        then {
+            let mut applicability = Applicability::MachineApplicable;
+            span_lint_and_sugg(
+                cx,
+                BYTES_COUNT_TO_LEN,
+                expr.span,
+                "using long and hard to read `.bytes().count()`",
+                "consider calling `.len()` instead",
+                format!("{}.len()", snippet_with_applicability(cx, bytes_recv.span, "..", &mut applicability)),
+                applicability
+            );
+        }
+    };
+}
diff --git a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs
new file mode 100644
index 00000000000..b3c2c7c9a2d
--- /dev/null
+++ b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs
@@ -0,0 +1,41 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::is_type_diagnostic_item;
+use if_chain::if_chain;
+use rustc_ast::ast::LitKind;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_span::{source_map::Spanned, symbol::sym, Span};
+
+use super::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    call_span: Span,
+    recv: &'tcx Expr<'_>,
+    arg: &'tcx Expr<'_>,
+) {
+    if_chain! {
+        if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+        if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
+        if cx.tcx.type_of(impl_id).is_str();
+        if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = arg.kind;
+        if (2..=6).contains(&ext_literal.as_str().len());
+        let ext_str = ext_literal.as_str();
+        if ext_str.starts_with('.');
+        if ext_str.chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit())
+            || ext_str.chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit());
+        let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs();
+        if recv_ty.is_str() || is_type_diagnostic_item(cx, recv_ty, sym::String);
+        then {
+            span_lint_and_help(
+                cx,
+                CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
+                call_span,
+                "case-sensitive file extension comparison",
+                None,
+                "consider using a case-insensitive comparison instead",
+            );
+        }
+    }
+}
diff --git a/clippy_lints/src/methods/collapsible_str_replace.rs b/clippy_lints/src/methods/collapsible_str_replace.rs
new file mode 100644
index 00000000000..561033be5b6
--- /dev/null
+++ b/clippy_lints/src/methods/collapsible_str_replace.rs
@@ -0,0 +1,96 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::visitors::for_each_expr;
+use clippy_utils::{eq_expr_value, get_parent_expr};
+use core::ops::ControlFlow;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use std::collections::VecDeque;
+
+use super::method_call;
+use super::COLLAPSIBLE_STR_REPLACE;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx hir::Expr<'tcx>,
+    from: &'tcx hir::Expr<'tcx>,
+    to: &'tcx hir::Expr<'tcx>,
+) {
+    let replace_methods = collect_replace_calls(cx, expr, to);
+    if replace_methods.methods.len() > 1 {
+        let from_kind = cx.typeck_results().expr_ty(from).peel_refs().kind();
+        // If the parent node's `to` argument is the same as the `to` argument
+        // of the last replace call in the current chain, don't lint as it was already linted
+        if let Some(parent) = get_parent_expr(cx, expr)
+            && let Some(("replace", [_, current_from, current_to], _)) = method_call(parent)
+            && eq_expr_value(cx, to, current_to)
+            && from_kind == cx.typeck_results().expr_ty(current_from).peel_refs().kind()
+        {
+            return;
+        }
+
+        check_consecutive_replace_calls(cx, expr, &replace_methods, to);
+    }
+}
+
+struct ReplaceMethods<'tcx> {
+    methods: VecDeque<&'tcx hir::Expr<'tcx>>,
+    from_args: VecDeque<&'tcx hir::Expr<'tcx>>,
+}
+
+fn collect_replace_calls<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx hir::Expr<'tcx>,
+    to_arg: &'tcx hir::Expr<'tcx>,
+) -> ReplaceMethods<'tcx> {
+    let mut methods = VecDeque::new();
+    let mut from_args = VecDeque::new();
+
+    let _: Option<()> = for_each_expr(expr, |e| {
+        if let Some(("replace", [_, from, to], _)) = method_call(e) {
+            if eq_expr_value(cx, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() {
+                methods.push_front(e);
+                from_args.push_front(from);
+                ControlFlow::Continue(())
+            } else {
+                ControlFlow::BREAK
+            }
+        } else {
+            ControlFlow::Continue(())
+        }
+    });
+
+    ReplaceMethods { methods, from_args }
+}
+
+/// Check a chain of `str::replace` calls for `collapsible_str_replace` lint.
+fn check_consecutive_replace_calls<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx hir::Expr<'tcx>,
+    replace_methods: &ReplaceMethods<'tcx>,
+    to_arg: &'tcx hir::Expr<'tcx>,
+) {
+    let from_args = &replace_methods.from_args;
+    let from_arg_reprs: Vec<String> = from_args
+        .iter()
+        .map(|from_arg| snippet(cx, from_arg.span, "..").to_string())
+        .collect();
+    let app = Applicability::MachineApplicable;
+    let earliest_replace_call = replace_methods.methods.front().unwrap();
+    if let Some((_, [..], span_lo)) = method_call(earliest_replace_call) {
+        span_lint_and_sugg(
+            cx,
+            COLLAPSIBLE_STR_REPLACE,
+            expr.span.with_lo(span_lo.lo()),
+            "used consecutive `str::replace` call",
+            "replace with",
+            format!(
+                "replace([{}], {})",
+                from_arg_reprs.join(", "),
+                snippet(cx, to_arg.span, ".."),
+            ),
+            app,
+        );
+    }
+}
diff --git a/clippy_lints/src/methods/expect_used.rs b/clippy_lints/src/methods/expect_used.rs
index 5ef08ca6290..d59fefa1ddc 100644
--- a/clippy_lints/src/methods/expect_used.rs
+++ b/clippy_lints/src/methods/expect_used.rs
@@ -7,18 +7,26 @@ use rustc_span::sym;
 
 use super::EXPECT_USED;
 
-/// lint use of `expect()` for `Option`s and `Result`s
-pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, allow_expect_in_tests: bool) {
+/// 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) {
+    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", "Err", "an "))
+        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) {
         return;
     }
@@ -28,7 +36,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
             cx,
             lint,
             expr.span,
-            &format!("used `expect()` on `{kind}` value"),
+            &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/get_first.rs b/clippy_lints/src/methods/get_first.rs
new file mode 100644
index 00000000000..4de77de7404
--- /dev/null
+++ b/clippy_lints/src/methods/get_first.rs
@@ -0,0 +1,39 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_slice_of_primitives;
+use clippy_utils::source::snippet_with_applicability;
+use if_chain::if_chain;
+use rustc_ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_span::source_map::Spanned;
+
+use super::GET_FIRST;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx hir::Expr<'_>,
+    recv: &'tcx hir::Expr<'_>,
+    arg: &'tcx hir::Expr<'_>,
+) {
+    if_chain! {
+        if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+        if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
+        if cx.tcx.type_of(impl_id).is_slice();
+        if let Some(_) = is_slice_of_primitives(cx, recv);
+        if let hir::ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = arg.kind;
+        then {
+            let mut app = Applicability::MachineApplicable;
+            let slice_name = snippet_with_applicability(cx, recv.span, "..", &mut app);
+            span_lint_and_sugg(
+                cx,
+                GET_FIRST,
+                expr.span,
+                &format!("accessing first element with `{0}.get(0)`", slice_name),
+                "try",
+                format!("{}.first()", slice_name),
+                app,
+            );
+        }
+    }
+}
diff --git a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs
new file mode 100644
index 00000000000..cea7b0d82ff
--- /dev/null
+++ b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs
@@ -0,0 +1,107 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::{get_expr_use_or_unification_node, is_lang_ctor, is_no_std_crate};
+
+use rustc_errors::Applicability;
+use rustc_hir::LangItem::{OptionNone, OptionSome};
+use rustc_hir::{Expr, ExprKind, Node};
+use rustc_lint::LateContext;
+
+use super::{ITER_ON_EMPTY_COLLECTIONS, ITER_ON_SINGLE_ITEMS};
+
+enum IterType {
+    Iter,
+    IterMut,
+    IntoIter,
+}
+
+impl IterType {
+    fn ref_prefix(&self) -> &'static str {
+        match self {
+            Self::Iter => "&",
+            Self::IterMut => "&mut ",
+            Self::IntoIter => "",
+        }
+    }
+}
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, method_name: &str, recv: &Expr<'_>) {
+    let item = match &recv.kind {
+        ExprKind::Array(v) if v.len() <= 1 => v.first(),
+        ExprKind::Path(p) => {
+            if is_lang_ctor(cx, p, OptionNone) {
+                None
+            } else {
+                return;
+            }
+        },
+        ExprKind::Call(f, some_args) if some_args.len() == 1 => {
+            if let ExprKind::Path(p) = &f.kind {
+                if is_lang_ctor(cx, p, OptionSome) {
+                    Some(&some_args[0])
+                } else {
+                    return;
+                }
+            } else {
+                return;
+            }
+        },
+        _ => return,
+    };
+    let iter_type = match method_name {
+        "iter" => IterType::Iter,
+        "iter_mut" => IterType::IterMut,
+        "into_iter" => IterType::IntoIter,
+        _ => return,
+    };
+
+    let is_unified = match get_expr_use_or_unification_node(cx.tcx, expr) {
+        Some((Node::Expr(parent), child_id)) => match parent.kind {
+            ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id == child_id => false,
+            ExprKind::If(_, _, _)
+            | ExprKind::Match(_, _, _)
+            | ExprKind::Closure(_)
+            | ExprKind::Ret(_)
+            | ExprKind::Break(_, _) => true,
+            _ => false,
+        },
+        Some((Node::Stmt(_) | Node::Local(_), _)) => false,
+        _ => true,
+    };
+
+    if is_unified {
+        return;
+    }
+
+    if let Some(i) = item {
+        let sugg = format!(
+            "{}::iter::once({}{})",
+            if is_no_std_crate(cx) { "core" } else { "std" },
+            iter_type.ref_prefix(),
+            snippet(cx, i.span, "...")
+        );
+        span_lint_and_sugg(
+            cx,
+            ITER_ON_SINGLE_ITEMS,
+            expr.span,
+            &format!("`{method_name}` call on a collection with only one item"),
+            "try",
+            sugg,
+            Applicability::MaybeIncorrect,
+        );
+    } else {
+        span_lint_and_sugg(
+            cx,
+            ITER_ON_EMPTY_COLLECTIONS,
+            expr.span,
+            &format!("`{method_name}` call on an empty collection"),
+            "try",
+            if is_no_std_crate(cx) {
+                "core::iter::empty()".to_string()
+            } else {
+                "std::iter::empty()".to_string()
+            },
+            Applicability::MaybeIncorrect,
+        );
+    }
+}
diff --git a/clippy_lints/src/methods/manual_ok_or.rs b/clippy_lints/src/methods/manual_ok_or.rs
new file mode 100644
index 00000000000..ffd2f4a38b8
--- /dev/null
+++ b/clippy_lints/src/methods/manual_ok_or.rs
@@ -0,0 +1,64 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{is_lang_ctor, path_to_local_id};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::LangItem::{ResultErr, ResultOk};
+use rustc_hir::{Closure, Expr, ExprKind, PatKind};
+use rustc_lint::LateContext;
+use rustc_span::symbol::sym;
+
+use super::MANUAL_OK_OR;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+    recv: &'tcx Expr<'_>,
+    or_expr: &'tcx Expr<'_>,
+    map_expr: &'tcx Expr<'_>,
+) {
+    if_chain! {
+        if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+        if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
+        if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Option);
+        if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, [err_arg]) = or_expr.kind;
+        if is_lang_ctor(cx, err_path, ResultErr);
+        if is_ok_wrapping(cx, map_expr);
+        if let Some(recv_snippet) = snippet_opt(cx, recv.span);
+        if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span);
+        if let Some(indent) = indent_of(cx, expr.span);
+        then {
+            let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4));
+            span_lint_and_sugg(
+                cx,
+                MANUAL_OK_OR,
+                expr.span,
+                "this pattern reimplements `Option::ok_or`",
+                "replace with",
+                format!(
+                    "{}.ok_or({})",
+                    recv_snippet,
+                    reindented_err_arg_snippet
+                ),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+}
+
+fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
+    if let ExprKind::Path(ref qpath) = map_expr.kind {
+        if is_lang_ctor(cx, qpath, ResultOk) {
+            return true;
+        }
+    }
+    if_chain! {
+        if let ExprKind::Closure(&Closure { body, .. }) = map_expr.kind;
+        let body = cx.tcx.hir().body(body);
+        if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind;
+        if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind;
+        if is_lang_ctor(cx, ok_path, ResultOk);
+        then { path_to_local_id(ok_arg, param_id) } else { false }
+    }
+}
diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs
new file mode 100644
index 00000000000..ffedda95ff8
--- /dev/null
+++ b/clippy_lints/src/methods/map_clone.rs
@@ -0,0 +1,122 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
+use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs, peel_blocks};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_middle::mir::Mutability;
+use rustc_middle::ty;
+use rustc_middle::ty::adjustment::Adjust;
+use rustc_semver::RustcVersion;
+use rustc_span::symbol::Ident;
+use rustc_span::{sym, Span};
+
+use super::MAP_CLONE;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'_>,
+    e: &hir::Expr<'_>,
+    recv: &hir::Expr<'_>,
+    arg: &'tcx hir::Expr<'_>,
+    msrv: Option<RustcVersion>,
+) {
+    if_chain! {
+        if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id);
+        if cx.tcx.impl_of_method(method_id)
+            .map_or(false, |id| is_type_diagnostic_item(cx, cx.tcx.type_of(id), sym::Option))
+            || is_diag_trait_item(cx, method_id, sym::Iterator);
+        if let hir::ExprKind::Closure(&hir::Closure{ body, .. }) = arg.kind;
+        then {
+            let closure_body = cx.tcx.hir().body(body);
+            let closure_expr = peel_blocks(&closure_body.value);
+            match closure_body.params[0].pat.kind {
+                hir::PatKind::Ref(inner, hir::Mutability::Not) => if let hir::PatKind::Binding(
+                    hir::BindingAnnotation::Unannotated, .., name, None
+                ) = inner.kind {
+                    if ident_eq(name, closure_expr) {
+                        lint_explicit_closure(cx, e.span, recv.span, true, msrv);
+                    }
+                },
+                hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, .., name, None) => {
+                    match closure_expr.kind {
+                        hir::ExprKind::Unary(hir::UnOp::Deref, inner) => {
+                            if ident_eq(name, inner) {
+                                if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() {
+                                    lint_explicit_closure(cx, e.span, recv.span, true, msrv);
+                                }
+                            }
+                        },
+                        hir::ExprKind::MethodCall(method, [obj], _) => if_chain! {
+                            if ident_eq(name, obj) && method.ident.name == sym::clone;
+                            if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id);
+                            if let Some(trait_id) = cx.tcx.trait_of_item(fn_id);
+                            if cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id);
+                            // no autoderefs
+                            if !cx.typeck_results().expr_adjustments(obj).iter()
+                                .any(|a| matches!(a.kind, Adjust::Deref(Some(..))));
+                            then {
+                                let obj_ty = cx.typeck_results().expr_ty(obj);
+                                if let ty::Ref(_, ty, mutability) = obj_ty.kind() {
+                                    if matches!(mutability, Mutability::Not) {
+                                        let copy = is_copy(cx, *ty);
+                                        lint_explicit_closure(cx, e.span, recv.span, copy, msrv);
+                                    }
+                                } else {
+                                    lint_needless_cloning(cx, e.span, recv.span);
+                                }
+                            }
+                        },
+                        _ => {},
+                    }
+                },
+                _ => {},
+            }
+        }
+    }
+}
+
+fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool {
+    if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = path.kind {
+        path.segments.len() == 1 && path.segments[0].ident == name
+    } else {
+        false
+    }
+}
+
+fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
+    span_lint_and_sugg(
+        cx,
+        MAP_CLONE,
+        root.trim_start(receiver).unwrap(),
+        "you are needlessly cloning iterator elements",
+        "remove the `map` call",
+        String::new(),
+        Applicability::MachineApplicable,
+    );
+}
+
+fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: Option<RustcVersion>) {
+    let mut applicability = Applicability::MachineApplicable;
+
+    let (message, sugg_method) = if is_copy && meets_msrv(msrv, msrvs::ITERATOR_COPIED) {
+        ("you are using an explicit closure for copying elements", "copied")
+    } else {
+        ("you are using an explicit closure for cloning elements", "cloned")
+    };
+
+    span_lint_and_sugg(
+        cx,
+        MAP_CLONE,
+        replace,
+        message,
+        &format!("consider calling the dedicated `{}` method", sugg_method),
+        format!(
+            "{}.{}()",
+            snippet_with_applicability(cx, root, "..", &mut applicability),
+            sugg_method,
+        ),
+        applicability,
+    );
+}
diff --git a/clippy_lints/src/methods/map_err_ignore.rs b/clippy_lints/src/methods/map_err_ignore.rs
new file mode 100644
index 00000000000..1fb6617145e
--- /dev/null
+++ b/clippy_lints/src/methods/map_err_ignore.rs
@@ -0,0 +1,34 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::is_type_diagnostic_item;
+use rustc_hir::{CaptureBy, Closure, Expr, ExprKind, PatKind};
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::MAP_ERR_IGNORE;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'_>, e: &Expr<'_>, arg: &'tcx Expr<'_>) {
+    if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
+        && let Some(impl_id) = cx.tcx.impl_of_method(method_id)
+        && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Result)
+        && let ExprKind::Closure(&Closure {
+            capture_clause: CaptureBy::Ref,
+            body,
+            fn_decl_span,
+            ..
+        }) = arg.kind
+        && let closure_body = cx.tcx.hir().body(body)
+        && let [param] = closure_body.params
+        && let PatKind::Wild = param.pat.kind
+    {
+        // span the area of the closure capture and warn that the
+        // original error will be thrown away
+        span_lint_and_help(
+            cx,
+            MAP_ERR_IGNORE,
+            fn_decl_span,
+            "`map_err(|_|...` wildcard pattern discards the original error",
+            None,
+            "consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)",
+        );
+    }
+}
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index 5ac6b09f0aa..a0d190a58af 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -1,5 +1,8 @@
 mod bind_instead_of_map;
+mod bytecount;
+mod bytes_count_to_len;
 mod bytes_nth;
+mod case_sensitive_file_extension_comparisons;
 mod chars_cmp;
 mod chars_cmp_with_unwrap;
 mod chars_last_cmp;
@@ -9,6 +12,7 @@ mod chars_next_cmp_with_unwrap;
 mod clone_on_copy;
 mod clone_on_ref_ptr;
 mod cloned_instead_of_copied;
+mod collapsible_str_replace;
 mod err_expect;
 mod expect_fun_call;
 mod expect_used;
@@ -21,6 +25,7 @@ mod filter_next;
 mod flat_map_identity;
 mod flat_map_option;
 mod from_iter_instead_of_collect;
+mod get_first;
 mod get_last_with_len;
 mod get_unwrap;
 mod implicit_clone;
@@ -33,55 +38,72 @@ mod iter_count;
 mod iter_next_slice;
 mod iter_nth;
 mod iter_nth_zero;
+mod iter_on_single_or_empty_collections;
 mod iter_overeager_cloned;
 mod iter_skip_next;
 mod iter_with_drain;
 mod iterator_step_by_zero;
+mod manual_ok_or;
 mod manual_saturating_arithmetic;
 mod manual_str_repeat;
+mod map_clone;
 mod map_collect_result_unit;
+mod map_err_ignore;
 mod map_flatten;
 mod map_identity;
 mod map_unwrap_or;
+mod mut_mutex_lock;
 mod needless_option_as_deref;
 mod needless_option_take;
 mod no_effect_replace;
 mod obfuscated_if_else;
 mod ok_expect;
+mod open_options;
 mod option_as_ref_deref;
 mod option_map_or_none;
 mod option_map_unwrap_or;
 mod or_fun_call;
 mod or_then_unwrap;
+mod path_buf_push_overwrite;
+mod range_zip_with_len;
+mod repeat_once;
 mod search_is_some;
 mod single_char_add_str;
 mod single_char_insert_string;
 mod single_char_pattern;
 mod single_char_push_string;
 mod skip_while_next;
+mod stable_sort_primitive;
 mod str_splitn;
 mod string_extend_chars;
 mod suspicious_map;
 mod suspicious_splitn;
+mod suspicious_to_owned;
 mod uninit_assumed_init;
+mod unit_hash;
 mod unnecessary_filter_map;
 mod unnecessary_fold;
 mod unnecessary_iter_cloned;
 mod unnecessary_join;
 mod unnecessary_lazy_eval;
+mod unnecessary_sort_by;
 mod unnecessary_to_owned;
 mod unwrap_or_else_default;
 mod unwrap_used;
 mod useless_asref;
 mod utils;
+mod vec_resize_to_zero;
+mod verbose_file_reads;
 mod wrong_self_convention;
 mod zst_offset;
 
 use bind_instead_of_map::BindInsteadOfMap;
 use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
-use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item};
-use clippy_utils::{contains_return, get_trait_def_id, iter_input_pats, meets_msrv, msrvs, paths, return_ty};
+use clippy_utils::ty::{contains_adt_constructor, implements_trait, is_copy, is_type_diagnostic_item};
+use clippy_utils::{
+    contains_return, get_trait_def_id, is_trait_method, iter_input_pats, meets_msrv, msrvs, paths, return_ty,
+};
 use if_chain::if_chain;
 use rustc_hir as hir;
 use rustc_hir::def::Res;
@@ -119,6 +141,32 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
+    /// Checks for consecutive calls to `str::replace` (2 or more)
+    /// that can be collapsed into a single call.
+    ///
+    /// ### Why is this bad?
+    /// Consecutive `str::replace` calls scan the string multiple times
+    /// with repetitive code.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let hello = "hesuo worpd"
+    ///     .replace('s', "l")
+    ///     .replace("u", "l")
+    ///     .replace('p', "l");
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let hello = "hesuo worpd".replace(&['s', 'u', 'p'], "l");
+    /// ```
+    #[clippy::version = "1.64.0"]
+    pub COLLAPSIBLE_STR_REPLACE,
+    perf,
+    "collapse consecutive calls to str::replace (2 or more) into a single call"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
     /// Checks for usage of `_.cloned().<func>()` where call to `.cloned()` can be postponed.
     ///
     /// ### Why is this bad?
@@ -173,7 +221,7 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for `.unwrap()` calls on `Option`s and on `Result`s.
+    /// Checks for `.unwrap()` or `.unwrap_err()` calls on `Result`s and `.unwrap()` call on `Option`s.
     ///
     /// ### Why is this bad?
     /// It is better to handle the `None` or `Err` case,
@@ -223,7 +271,7 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for `.expect()` calls on `Option`s and `Result`s.
+    /// Checks for `.expect()` or `.expect_err()` calls on `Result`s and `.expect()` call on `Option`s.
     ///
     /// ### Why is this bad?
     /// Usually it is better to handle the `None` or `Err` case.
@@ -2008,6 +2056,55 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
+    /// Checks for the usage of `_.to_owned()`, on a `Cow<'_, _>`.
+    ///
+    /// ### Why is this bad?
+    /// Calling `to_owned()` on a `Cow` creates a clone of the `Cow`
+    /// itself, without taking ownership of the `Cow` contents (i.e.
+    /// it's equivalent to calling `Cow::clone`).
+    /// The similarly named `into_owned` method, on the other hand,
+    /// clones the `Cow` contents, effectively turning any `Cow::Borrowed`
+    /// into a `Cow::Owned`.
+    ///
+    /// Given the potential ambiguity, consider replacing `to_owned`
+    /// with `clone` for better readability or, if getting a `Cow::Owned`
+    /// was the original intent, using `into_owned` instead.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # use std::borrow::Cow;
+    /// let s = "Hello world!";
+    /// let cow = Cow::Borrowed(s);
+    ///
+    /// let data = cow.to_owned();
+    /// assert!(matches!(data, Cow::Borrowed(_)))
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # use std::borrow::Cow;
+    /// let s = "Hello world!";
+    /// let cow = Cow::Borrowed(s);
+    ///
+    /// let data = cow.clone();
+    /// assert!(matches!(data, Cow::Borrowed(_)))
+    /// ```
+    /// or
+    /// ```rust
+    /// # use std::borrow::Cow;
+    /// let s = "Hello world!";
+    /// let cow = Cow::Borrowed(s);
+    ///
+    /// let data = cow.into_owned();
+    /// assert!(matches!(data, String))
+    /// ```
+    #[clippy::version = "1.65.0"]
+    pub SUSPICIOUS_TO_OWNED,
+    suspicious,
+    "calls to `to_owned` on a `Cow<'_, _>` might not do what they are expected"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
     /// Checks for calls to [`splitn`]
     /// (https://doc.rust-lang.org/std/primitive.str.html#method.splitn) and
     /// related functions with either zero or one splits.
@@ -2269,7 +2366,7 @@ declare_clippy_lint! {
     /// "1234".replace("12", "12");
     /// "1234".replacen("12", "12", 1);
     /// ```
-    #[clippy::version = "1.62.0"]
+    #[clippy::version = "1.63.0"]
     pub NO_EFFECT_REPLACE,
     suspicious,
     "replace with no effect"
@@ -2304,6 +2401,640 @@ declare_clippy_lint! {
     more clearly with `if .. else ..`"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    ///
+    /// Checks for calls to `iter`, `iter_mut` or `into_iter` on collections containing a single item
+    ///
+    /// ### Why is this bad?
+    ///
+    /// It is simpler to use the once function from the standard library:
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// let a = [123].iter();
+    /// let b = Some(123).into_iter();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// use std::iter;
+    /// let a = iter::once(&123);
+    /// let b = iter::once(123);
+    /// ```
+    ///
+    /// ### Known problems
+    ///
+    /// The type of the resulting iterator might become incompatible with its usage
+    #[clippy::version = "1.64.0"]
+    pub ITER_ON_SINGLE_ITEMS,
+    nursery,
+    "Iterator for array of length 1"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    ///
+    /// Checks for calls to `iter`, `iter_mut` or `into_iter` on empty collections
+    ///
+    /// ### Why is this bad?
+    ///
+    /// It is simpler to use the empty function from the standard library:
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// use std::{slice, option};
+    /// let a: slice::Iter<i32> = [].iter();
+    /// let f: option::IntoIter<i32> = None.into_iter();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// use std::iter;
+    /// let a: iter::Empty<i32> = iter::empty();
+    /// let b: iter::Empty<i32> = iter::empty();
+    /// ```
+    ///
+    /// ### Known problems
+    ///
+    /// The type of the resulting iterator might become incompatible with its usage
+    #[clippy::version = "1.64.0"]
+    pub ITER_ON_EMPTY_COLLECTIONS,
+    nursery,
+    "Iterator for empty array"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for naive byte counts
+    ///
+    /// ### Why is this bad?
+    /// The [`bytecount`](https://crates.io/crates/bytecount)
+    /// crate has methods to count your bytes faster, especially for large slices.
+    ///
+    /// ### Known problems
+    /// If you have predominantly small slices, the
+    /// `bytecount::count(..)` method may actually be slower. However, if you can
+    /// ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be
+    /// faster in those cases.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let vec = vec![1_u8];
+    /// let count = vec.iter().filter(|x| **x == 0u8).count();
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust,ignore
+    /// # let vec = vec![1_u8];
+    /// let count = bytecount::count(&vec, 0u8);
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub NAIVE_BYTECOUNT,
+    pedantic,
+    "use of naive `<slice>.filter(|&x| x == y).count()` to count byte values"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// It checks for `str::bytes().count()` and suggests replacing it with
+    /// `str::len()`.
+    ///
+    /// ### Why is this bad?
+    /// `str::bytes().count()` is longer and may not be as performant as using
+    /// `str::len()`.
+    ///
+    /// ### Example
+    /// ```rust
+    /// "hello".bytes().count();
+    /// String::from("hello").bytes().count();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// "hello".len();
+    /// String::from("hello").len();
+    /// ```
+    #[clippy::version = "1.62.0"]
+    pub BYTES_COUNT_TO_LEN,
+    complexity,
+    "Using `bytes().count()` when `len()` performs the same functionality"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for calls to `ends_with` with possible file extensions
+    /// and suggests to use a case-insensitive approach instead.
+    ///
+    /// ### Why is this bad?
+    /// `ends_with` is case-sensitive and may not detect files with a valid extension.
+    ///
+    /// ### Example
+    /// ```rust
+    /// fn is_rust_file(filename: &str) -> bool {
+    ///     filename.ends_with(".rs")
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// fn is_rust_file(filename: &str) -> bool {
+    ///     let filename = std::path::Path::new(filename);
+    ///     filename.extension()
+    ///         .map_or(false, |ext| ext.eq_ignore_ascii_case("rs"))
+    /// }
+    /// ```
+    #[clippy::version = "1.51.0"]
+    pub CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
+    pedantic,
+    "Checks for calls to ends_with with case-sensitive file extensions"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for using `x.get(0)` instead of
+    /// `x.first()`.
+    ///
+    /// ### Why is this bad?
+    /// Using `x.first()` is easier to read and has the same
+    /// result.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let x = vec![2, 3, 5];
+    /// let first_element = x.get(0);
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// let x = vec![2, 3, 5];
+    /// let first_element = x.first();
+    /// ```
+    #[clippy::version = "1.63.0"]
+    pub GET_FIRST,
+    style,
+    "Using `x.get(0)` when `x.first()` is simpler"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    ///
+    /// Finds patterns that reimplement `Option::ok_or`.
+    ///
+    /// ### Why is this bad?
+    ///
+    /// Concise code helps focusing on behavior instead of boilerplate.
+    ///
+    /// ### Examples
+    /// ```rust
+    /// let foo: Option<i32> = None;
+    /// foo.map_or(Err("error"), |v| Ok(v));
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// let foo: Option<i32> = None;
+    /// foo.ok_or("error");
+    /// ```
+    #[clippy::version = "1.49.0"]
+    pub MANUAL_OK_OR,
+    pedantic,
+    "finds patterns that can be encoded more concisely with `Option::ok_or`"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for usage of `map(|x| x.clone())` or
+    /// dereferencing closures for `Copy` types, on `Iterator` or `Option`,
+    /// and suggests `cloned()` or `copied()` instead
+    ///
+    /// ### Why is this bad?
+    /// Readability, this can be written more concisely
+    ///
+    /// ### Example
+    /// ```rust
+    /// let x = vec![42, 43];
+    /// let y = x.iter();
+    /// let z = y.map(|i| *i);
+    /// ```
+    ///
+    /// The correct use would be:
+    ///
+    /// ```rust
+    /// let x = vec![42, 43];
+    /// let y = x.iter();
+    /// let z = y.cloned();
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub MAP_CLONE,
+    style,
+    "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for instances of `map_err(|_| Some::Enum)`
+    ///
+    /// ### Why is this bad?
+    /// This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error
+    ///
+    /// ### Example
+    /// Before:
+    /// ```rust
+    /// use std::fmt;
+    ///
+    /// #[derive(Debug)]
+    /// enum Error {
+    ///     Indivisible,
+    ///     Remainder(u8),
+    /// }
+    ///
+    /// impl fmt::Display for Error {
+    ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+    ///         match self {
+    ///             Error::Indivisible => write!(f, "could not divide input by three"),
+    ///             Error::Remainder(remainder) => write!(
+    ///                 f,
+    ///                 "input is not divisible by three, remainder = {}",
+    ///                 remainder
+    ///             ),
+    ///         }
+    ///     }
+    /// }
+    ///
+    /// impl std::error::Error for Error {}
+    ///
+    /// fn divisible_by_3(input: &str) -> Result<(), Error> {
+    ///     input
+    ///         .parse::<i32>()
+    ///         .map_err(|_| Error::Indivisible)
+    ///         .map(|v| v % 3)
+    ///         .and_then(|remainder| {
+    ///             if remainder == 0 {
+    ///                 Ok(())
+    ///             } else {
+    ///                 Err(Error::Remainder(remainder as u8))
+    ///             }
+    ///         })
+    /// }
+    ///  ```
+    ///
+    ///  After:
+    ///  ```rust
+    /// use std::{fmt, num::ParseIntError};
+    ///
+    /// #[derive(Debug)]
+    /// enum Error {
+    ///     Indivisible(ParseIntError),
+    ///     Remainder(u8),
+    /// }
+    ///
+    /// impl fmt::Display for Error {
+    ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+    ///         match self {
+    ///             Error::Indivisible(_) => write!(f, "could not divide input by three"),
+    ///             Error::Remainder(remainder) => write!(
+    ///                 f,
+    ///                 "input is not divisible by three, remainder = {}",
+    ///                 remainder
+    ///             ),
+    ///         }
+    ///     }
+    /// }
+    ///
+    /// impl std::error::Error for Error {
+    ///     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+    ///         match self {
+    ///             Error::Indivisible(source) => Some(source),
+    ///             _ => None,
+    ///         }
+    ///     }
+    /// }
+    ///
+    /// fn divisible_by_3(input: &str) -> Result<(), Error> {
+    ///     input
+    ///         .parse::<i32>()
+    ///         .map_err(Error::Indivisible)
+    ///         .map(|v| v % 3)
+    ///         .and_then(|remainder| {
+    ///             if remainder == 0 {
+    ///                 Ok(())
+    ///             } else {
+    ///                 Err(Error::Remainder(remainder as u8))
+    ///             }
+    ///         })
+    /// }
+    /// ```
+    #[clippy::version = "1.48.0"]
+    pub MAP_ERR_IGNORE,
+    restriction,
+    "`map_err` should not ignore the original error"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for `&mut Mutex::lock` calls
+    ///
+    /// ### Why is this bad?
+    /// `Mutex::lock` is less efficient than
+    /// calling `Mutex::get_mut`. In addition you also have a statically
+    /// guarantee that the mutex isn't locked, instead of just a runtime
+    /// guarantee.
+    ///
+    /// ### Example
+    /// ```rust
+    /// use std::sync::{Arc, Mutex};
+    ///
+    /// let mut value_rc = Arc::new(Mutex::new(42_u8));
+    /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
+    ///
+    /// let mut value = value_mutex.lock().unwrap();
+    /// *value += 1;
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// use std::sync::{Arc, Mutex};
+    ///
+    /// let mut value_rc = Arc::new(Mutex::new(42_u8));
+    /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
+    ///
+    /// let value = value_mutex.get_mut().unwrap();
+    /// *value += 1;
+    /// ```
+    #[clippy::version = "1.49.0"]
+    pub MUT_MUTEX_LOCK,
+    style,
+    "`&mut Mutex::lock` does unnecessary locking"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for duplicate open options as well as combinations
+    /// that make no sense.
+    ///
+    /// ### Why is this bad?
+    /// In the best case, the code will be harder to read than
+    /// necessary. I don't know the worst case.
+    ///
+    /// ### Example
+    /// ```rust
+    /// use std::fs::OpenOptions;
+    ///
+    /// OpenOptions::new().read(true).truncate(true);
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub NONSENSICAL_OPEN_OPTIONS,
+    correctness,
+    "nonsensical combination of options for opening a file"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    ///* Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push)
+    /// calls on `PathBuf` that can cause overwrites.
+    ///
+    /// ### Why is this bad?
+    /// Calling `push` with a root path at the start can overwrite the
+    /// previous defined path.
+    ///
+    /// ### Example
+    /// ```rust
+    /// use std::path::PathBuf;
+    ///
+    /// let mut x = PathBuf::from("/foo");
+    /// x.push("/bar");
+    /// assert_eq!(x, PathBuf::from("/bar"));
+    /// ```
+    /// Could be written:
+    ///
+    /// ```rust
+    /// use std::path::PathBuf;
+    ///
+    /// let mut x = PathBuf::from("/foo");
+    /// x.push("bar");
+    /// assert_eq!(x, PathBuf::from("/foo/bar"));
+    /// ```
+    #[clippy::version = "1.36.0"]
+    pub PATH_BUF_PUSH_OVERWRITE,
+    nursery,
+    "calling `push` with file system root on `PathBuf` can overwrite it"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for zipping a collection with the range of
+    /// `0.._.len()`.
+    ///
+    /// ### Why is this bad?
+    /// The code is better expressed with `.enumerate()`.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let x = vec![1];
+    /// let _ = x.iter().zip(0..x.len());
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// # let x = vec![1];
+    /// let _ = x.iter().enumerate();
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub RANGE_ZIP_WITH_LEN,
+    complexity,
+    "zipping iterator with a range when `enumerate()` would do"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for usage of `.repeat(1)` and suggest the following method for each types.
+    /// - `.to_string()` for `str`
+    /// - `.clone()` for `String`
+    /// - `.to_vec()` for `slice`
+    ///
+    /// The lint will evaluate constant expressions and values as arguments of `.repeat(..)` and emit a message if
+    /// they are equivalent to `1`. (Related discussion in [rust-clippy#7306](https://github.com/rust-lang/rust-clippy/issues/7306))
+    ///
+    /// ### Why is this bad?
+    /// For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning
+    /// the string is the intention behind this, `clone()` should be used.
+    ///
+    /// ### Example
+    /// ```rust
+    /// fn main() {
+    ///     let x = String::from("hello world").repeat(1);
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// fn main() {
+    ///     let x = String::from("hello world").clone();
+    /// }
+    /// ```
+    #[clippy::version = "1.47.0"]
+    pub REPEAT_ONCE,
+    complexity,
+    "using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` "
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// When sorting primitive values (integers, bools, chars, as well
+    /// as arrays, slices, and tuples of such items), it is typically better to
+    /// use an unstable sort than a stable sort.
+    ///
+    /// ### Why is this bad?
+    /// Typically, using a stable sort consumes more memory and cpu cycles.
+    /// Because values which compare equal are identical, preserving their
+    /// relative order (the guarantee that a stable sort provides) means
+    /// nothing, while the extra costs still apply.
+    ///
+    /// ### Known problems
+    ///
+    /// As pointed out in
+    /// [issue #8241](https://github.com/rust-lang/rust-clippy/issues/8241),
+    /// a stable sort can instead be significantly faster for certain scenarios
+    /// (eg. when a sorted vector is extended with new data and resorted).
+    ///
+    /// For more information and benchmarking results, please refer to the
+    /// issue linked above.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let mut vec = vec![2, 1, 3];
+    /// vec.sort();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let mut vec = vec![2, 1, 3];
+    /// vec.sort_unstable();
+    /// ```
+    #[clippy::version = "1.47.0"]
+    pub STABLE_SORT_PRIMITIVE,
+    pedantic,
+    "use of sort() when sort_unstable() is equivalent"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Detects `().hash(_)`.
+    ///
+    /// ### Why is this bad?
+    /// Hashing a unit value doesn't do anything as the implementation of `Hash` for `()` is a no-op.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # use std::hash::Hash;
+    /// # use std::collections::hash_map::DefaultHasher;
+    /// # enum Foo { Empty, WithValue(u8) }
+    /// # use Foo::*;
+    /// # let mut state = DefaultHasher::new();
+    /// # let my_enum = Foo::Empty;
+    /// match my_enum {
+    /// 	Empty => ().hash(&mut state),
+    /// 	WithValue(x) => x.hash(&mut state),
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # use std::hash::Hash;
+    /// # use std::collections::hash_map::DefaultHasher;
+    /// # enum Foo { Empty, WithValue(u8) }
+    /// # use Foo::*;
+    /// # let mut state = DefaultHasher::new();
+    /// # let my_enum = Foo::Empty;
+    /// match my_enum {
+    /// 	Empty => 0_u8.hash(&mut state),
+    /// 	WithValue(x) => x.hash(&mut state),
+    /// }
+    /// ```
+    #[clippy::version = "1.58.0"]
+    pub UNIT_HASH,
+    correctness,
+    "hashing a unit value, which does nothing"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Detects uses of `Vec::sort_by` passing in a closure
+    /// which compares the two arguments, either directly or indirectly.
+    ///
+    /// ### Why is this bad?
+    /// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if
+    /// possible) than to use `Vec::sort_by` and a more complicated
+    /// closure.
+    ///
+    /// ### Known problems
+    /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't already
+    /// imported by a use statement, then it will need to be added manually.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # struct A;
+    /// # impl A { fn foo(&self) {} }
+    /// # let mut vec: Vec<A> = Vec::new();
+    /// vec.sort_by(|a, b| a.foo().cmp(&b.foo()));
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # struct A;
+    /// # impl A { fn foo(&self) {} }
+    /// # let mut vec: Vec<A> = Vec::new();
+    /// vec.sort_by_key(|a| a.foo());
+    /// ```
+    #[clippy::version = "1.46.0"]
+    pub UNNECESSARY_SORT_BY,
+    complexity,
+    "Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Finds occurrences of `Vec::resize(0, an_int)`
+    ///
+    /// ### Why is this bad?
+    /// This is probably an argument inversion mistake.
+    ///
+    /// ### Example
+    /// ```rust
+    /// vec!(1, 2, 3, 4, 5).resize(0, 5)
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// vec!(1, 2, 3, 4, 5).clear()
+    /// ```
+    #[clippy::version = "1.46.0"]
+    pub VEC_RESIZE_TO_ZERO,
+    correctness,
+    "emptying a vector with `resize(0, an_int)` instead of `clear()` is probably an argument inversion mistake"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for use of File::read_to_end and File::read_to_string.
+    ///
+    /// ### Why is this bad?
+    /// `fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values.
+    /// See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html)
+    ///
+    /// ### Example
+    /// ```rust,no_run
+    /// # use std::io::Read;
+    /// # use std::fs::File;
+    /// let mut f = File::open("foo.txt").unwrap();
+    /// let mut bytes = Vec::new();
+    /// f.read_to_end(&mut bytes).unwrap();
+    /// ```
+    /// Can be written more concisely as
+    /// ```rust,no_run
+    /// # use std::fs;
+    /// let mut bytes = fs::read("foo.txt").unwrap();
+    /// ```
+    #[clippy::version = "1.44.0"]
+    pub VERBOSE_FILE_READS,
+    restriction,
+    "use of `File::read_to_end` or `File::read_to_string`"
+}
+
 pub struct Methods {
     avoid_breaking_exported_api: bool,
     msrv: Option<RustcVersion>,
@@ -2347,6 +3078,7 @@ impl_lint_pass!(Methods => [
     CLONE_ON_COPY,
     CLONE_ON_REF_PTR,
     CLONE_DOUBLE_REF,
+    COLLAPSIBLE_STR_REPLACE,
     ITER_OVEREAGER_CLONED,
     CLONED_INSTEAD_OF_COPIED,
     FLAT_MAP_OPTION,
@@ -2393,6 +3125,7 @@ impl_lint_pass!(Methods => [
     FROM_ITER_INSTEAD_OF_COLLECT,
     INSPECT_FOR_EACH,
     IMPLICIT_CLONE,
+    SUSPICIOUS_TO_OWNED,
     SUSPICIOUS_SPLITN,
     MANUAL_STR_REPEAT,
     EXTEND_WITH_DRAIN,
@@ -2406,6 +3139,25 @@ impl_lint_pass!(Methods => [
     NEEDLESS_OPTION_TAKE,
     NO_EFFECT_REPLACE,
     OBFUSCATED_IF_ELSE,
+    ITER_ON_SINGLE_ITEMS,
+    ITER_ON_EMPTY_COLLECTIONS,
+    NAIVE_BYTECOUNT,
+    BYTES_COUNT_TO_LEN,
+    CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
+    GET_FIRST,
+    MANUAL_OK_OR,
+    MAP_CLONE,
+    MAP_ERR_IGNORE,
+    MUT_MUTEX_LOCK,
+    NONSENSICAL_OPEN_OPTIONS,
+    PATH_BUF_PUSH_OVERWRITE,
+    RANGE_ZIP_WITH_LEN,
+    REPEAT_ONCE,
+    STABLE_SORT_PRIMITIVE,
+    UNIT_HASH,
+    UNNECESSARY_SORT_BY,
+    VEC_RESIZE_TO_ZERO,
+    VERBOSE_FILE_READS,
 ]);
 
 /// Extracts a method call name, args, and `Span` of the method name.
@@ -2541,7 +3293,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
                 if contains_adt_constructor(ret_ty, self_adt) {
                     return;
                 }
-            } else if contains_ty(ret_ty, self_ty) {
+            } else if ret_ty.contains(self_ty) {
                 return;
             }
 
@@ -2559,7 +3311,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
                             if contains_adt_constructor(assoc_ty, self_adt) {
                                 return;
                             }
-                        } else if contains_ty(assoc_ty, self_ty) {
+                        } else if assoc_ty.contains(self_ty) {
                             return;
                         }
                     }
@@ -2608,7 +3360,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
             if let TraitItemKind::Fn(_, _) = item.kind;
             let ret_ty = return_ty(cx, item.hir_id());
             let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder();
-            if !contains_ty(ret_ty, self_ty);
+            if !ret_ty.contains(self_ty);
 
             then {
                 span_lint(
@@ -2660,22 +3412,30 @@ impl Methods {
                     },
                     _ => {},
                 },
-                ("count", []) => match method_call(recv) {
+                ("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((name2 @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => {
                         iter_count::check(cx, expr, recv2, name2);
                     },
                     Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg),
+                    Some(("filter", [recv2, arg], _)) => bytecount::check(cx, expr, recv2, arg),
+                    Some(("bytes", [recv2], _)) => bytes_count_to_len::check(cx, expr, recv, recv2),
                     _ => {},
                 },
                 ("drain", [arg]) => {
                     iter_with_drain::check(cx, expr, recv, span, arg);
                 },
+                ("ends_with", [arg]) => {
+                    if let ExprKind::MethodCall(_, _, span) = expr.kind {
+                        case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg);
+                    }
+                },
                 ("expect", [_]) => match method_call(recv) {
                     Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
                     Some(("err", [recv], err_span)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span),
-                    _ => expect_used::check(cx, expr, recv, self.allow_expect_in_tests),
+                    _ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests),
                 },
+                ("expect_err", [_]) => expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests),
                 ("extend", [arg]) => {
                     string_extend_chars::check(cx, expr, recv, arg);
                     extend_with_drain::check(cx, expr, recv, arg);
@@ -2702,12 +3462,21 @@ impl Methods {
                         inspect_for_each::check(cx, expr, span2);
                     }
                 },
-                ("get", [arg]) => get_last_with_len::check(cx, expr, recv, arg),
+                ("get", [arg]) => {
+                    get_first::check(cx, expr, recv, arg);
+                    get_last_with_len::check(cx, expr, recv, arg);
+                },
                 ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"),
+                ("hash", [arg]) => {
+                    unit_hash::check(cx, expr, recv, arg);
+                },
                 ("is_file", []) => filetype_is_file::check(cx, expr, recv),
                 ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv),
                 ("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
                 ("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
+                ("iter" | "iter_mut" | "into_iter", []) => {
+                    iter_on_single_or_empty_collections::check(cx, expr, name, recv);
+                },
                 ("join", [join_arg]) => {
                     if let Some(("collect", _, span)) = method_call(recv) {
                         unnecessary_join::check(cx, expr, recv, join_arg, span);
@@ -2720,7 +3489,15 @@ impl Methods {
                         }
                     }
                 },
+                ("lock", []) => {
+                    mut_mutex_lock::check(cx, expr, recv, span);
+                },
                 (name @ ("map" | "map_err"), [m_arg]) => {
+                    if name == "map" {
+                        map_clone::check(cx, expr, recv, m_arg, self.msrv);
+                    } else {
+                        map_err_ignore::check(cx, expr, m_arg);
+                    }
                     if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) {
                         match (name, args) {
                             ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv),
@@ -2736,7 +3513,10 @@ impl Methods {
                     }
                     map_identity::check(cx, expr, recv, m_arg, name, span);
                 },
-                ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
+                ("map_or", [def, map]) => {
+                    option_map_or_none::check(cx, expr, recv, def, map);
+                    manual_ok_or::check(cx, expr, recv, def, map);
+                },
                 ("next", []) => {
                     if let Some((name2, [recv2, args2 @ ..], _)) = method_call(recv) {
                         match (name2, args2) {
@@ -2758,11 +3538,46 @@ impl Methods {
                     _ => iter_nth_zero::check(cx, expr, recv, n_arg),
                 },
                 ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"),
+                ("open", [_]) => {
+                    open_options::check(cx, expr, recv);
+                },
                 ("or_else", [arg]) => {
                     if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) {
                         unnecessary_lazy_eval::check(cx, expr, recv, arg, "or");
                     }
                 },
+                ("push", [arg]) => {
+                    path_buf_push_overwrite::check(cx, expr, arg);
+                },
+                ("read_to_end", [_]) => {
+                    verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_END_MSG);
+                },
+                ("read_to_string", [_]) => {
+                    verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_STRING_MSG);
+                },
+                ("repeat", [arg]) => {
+                    repeat_once::check(cx, expr, recv, arg);
+                },
+                (name @ ("replace" | "replacen"), [arg1, arg2] | [arg1, arg2, _]) => {
+                    no_effect_replace::check(cx, expr, arg1, arg2);
+
+                    // Check for repeated `str::replace` calls to perform `collapsible_str_replace` lint
+                    if name == "replace" && let Some(("replace", ..)) = method_call(recv) {
+                        collapsible_str_replace::check(cx, expr, arg1, arg2);
+                    }
+                },
+                ("resize", [count_arg, default_arg]) => {
+                    vec_resize_to_zero::check(cx, expr, count_arg, default_arg, span);
+                },
+                ("sort", []) => {
+                    stable_sort_primitive::check(cx, expr, recv);
+                },
+                ("sort_by", [arg]) => {
+                    unnecessary_sort_by::check(cx, expr, recv, arg, false);
+                },
+                ("sort_unstable_by", [arg]) => {
+                    unnecessary_sort_by::check(cx, expr, recv, arg, true);
+                },
                 ("splitn" | "rsplitn", [count_arg, pat_arg]) => {
                     if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
                         suspicious_splitn::check(cx, name, expr, recv, count);
@@ -2789,7 +3604,12 @@ impl Methods {
                     }
                     unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some");
                 },
-                ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
+                ("to_owned", []) => {
+                    if !suspicious_to_owned::check(cx, expr, recv) {
+                        implicit_clone::check(cx, name, expr, recv);
+                    }
+                },
+                ("to_os_string" | "to_path_buf" | "to_vec", []) => {
                     implicit_clone::check(cx, name, expr, recv);
                 },
                 ("unwrap", []) => {
@@ -2805,8 +3625,9 @@ impl Methods {
                         },
                         _ => {},
                     }
-                    unwrap_used::check(cx, expr, recv, self.allow_unwrap_in_tests);
+                    unwrap_used::check(cx, expr, recv, false, self.allow_unwrap_in_tests);
                 },
+                ("unwrap_err", []) => unwrap_used::check(cx, expr, recv, true, self.allow_unwrap_in_tests),
                 ("unwrap_or", [u_arg]) => match method_call(recv) {
                     Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => {
                         manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]);
@@ -2827,8 +3648,12 @@ impl Methods {
                         unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
                     },
                 },
-                ("replace" | "replacen", [arg1, arg2] | [arg1, arg2, _]) => {
-                    no_effect_replace::check(cx, expr, arg1, arg2);
+                ("zip", [arg]) => {
+                    if let ExprKind::MethodCall(name, [iter_recv], _) = recv.kind
+                        && name.ident.name == sym::iter
+                    {
+                        range_zip_with_len::check(cx, expr, iter_recv, arg);
+                    }
                 },
                 _ => {},
             }
diff --git a/clippy_lints/src/methods/mut_mutex_lock.rs b/clippy_lints/src/methods/mut_mutex_lock.rs
new file mode 100644
index 00000000000..bd8458a222e
--- /dev/null
+++ b/clippy_lints/src/methods/mut_mutex_lock.rs
@@ -0,0 +1,30 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, Mutability};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_span::{sym, Span};
+
+use super::MUT_MUTEX_LOCK;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &'tcx Expr<'tcx>, name_span: Span) {
+    if_chain! {
+        if let ty::Ref(_, _, Mutability::Mut) = cx.typeck_results().expr_ty(recv).kind();
+        if let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id);
+        if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
+        if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Mutex);
+        then {
+            span_lint_and_sugg(
+                cx,
+                MUT_MUTEX_LOCK,
+                name_span,
+                "calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference",
+                "change this to",
+                "get_mut".to_owned(),
+                Applicability::MaybeIncorrect,
+            );
+        }
+    }
+}
diff --git a/clippy_lints/src/open_options.rs b/clippy_lints/src/methods/open_options.rs
index 5a0b5042018..c3112823e34 100644
--- a/clippy_lints/src/open_options.rs
+++ b/clippy_lints/src/methods/open_options.rs
@@ -3,43 +3,19 @@ use clippy_utils::paths;
 use clippy_utils::ty::match_type;
 use rustc_ast::ast::LitKind;
 use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_lint::LateContext;
 use rustc_span::source_map::{Span, Spanned};
 
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for duplicate open options as well as combinations
-    /// that make no sense.
-    ///
-    /// ### Why is this bad?
-    /// In the best case, the code will be harder to read than
-    /// necessary. I don't know the worst case.
-    ///
-    /// ### Example
-    /// ```rust
-    /// use std::fs::OpenOptions;
-    ///
-    /// OpenOptions::new().read(true).truncate(true);
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub NONSENSICAL_OPEN_OPTIONS,
-    correctness,
-    "nonsensical combination of options for opening a file"
-}
-
-declare_lint_pass!(OpenOptions => [NONSENSICAL_OPEN_OPTIONS]);
+use super::NONSENSICAL_OPEN_OPTIONS;
 
-impl<'tcx> LateLintPass<'tcx> for OpenOptions {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
-        if let ExprKind::MethodCall(path, [self_arg, ..], _) = &e.kind {
-            let obj_ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
-            if path.ident.name == sym!(open) && match_type(cx, obj_ty, &paths::OPEN_OPTIONS) {
-                let mut options = Vec::new();
-                get_open_options(cx, self_arg, &mut options);
-                check_open_options(cx, &options, e.span);
-            }
-        }
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
+    if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
+        && let Some(impl_id) = cx.tcx.impl_of_method(method_id)
+        && match_type(cx, cx.tcx.type_of(impl_id), &paths::OPEN_OPTIONS)
+    {
+        let mut options = Vec::new();
+        get_open_options(cx, recv, &mut options);
+        check_open_options(cx, &options, e.span);
     }
 }
 
diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs
index 6c641af59f9..3c4002a3aef 100644
--- a/clippy_lints/src/methods/option_map_unwrap_or.rs
+++ b/clippy_lints/src/methods/option_map_unwrap_or.rs
@@ -78,7 +78,7 @@ pub(super) fn check<'tcx>(
                     map_span,
                     String::from(if unwrap_snippet_none { "and_then" } else { "map_or" }),
                 ),
-                (expr.span.with_lo(unwrap_recv.span.hi()), String::from("")),
+                (expr.span.with_lo(unwrap_recv.span.hi()), String::new()),
             ];
 
             if !unwrap_snippet_none {
diff --git a/clippy_lints/src/methods/path_buf_push_overwrite.rs b/clippy_lints/src/methods/path_buf_push_overwrite.rs
new file mode 100644
index 00000000000..0cc28c0dcb3
--- /dev/null
+++ b/clippy_lints/src/methods/path_buf_push_overwrite.rs
@@ -0,0 +1,37 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
+use if_chain::if_chain;
+use rustc_ast::ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_span::symbol::sym;
+use std::path::{Component, Path};
+
+use super::PATH_BUF_PUSH_OVERWRITE;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) {
+    if_chain! {
+        if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+        if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
+        if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::PathBuf);
+        if let ExprKind::Lit(ref lit) = arg.kind;
+        if let LitKind::Str(ref path_lit, _) = lit.node;
+        if let pushed_path = Path::new(path_lit.as_str());
+        if let Some(pushed_path_lit) = pushed_path.to_str();
+        if pushed_path.has_root();
+        if let Some(root) = pushed_path.components().next();
+        if root == Component::RootDir;
+        then {
+            span_lint_and_sugg(
+                cx,
+                PATH_BUF_PUSH_OVERWRITE,
+                lit.span,
+                "calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition",
+                "try",
+                format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+}
diff --git a/clippy_lints/src/methods/range_zip_with_len.rs b/clippy_lints/src/methods/range_zip_with_len.rs
new file mode 100644
index 00000000000..00a2a0d14d1
--- /dev/null
+++ b/clippy_lints/src/methods/range_zip_with_len.rs
@@ -0,0 +1,34 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::source::snippet;
+use clippy_utils::{higher, SpanlessEq};
+use clippy_utils::{is_integer_const, is_trait_method};
+use if_chain::if_chain;
+use rustc_hir::{Expr, ExprKind, QPath};
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::RANGE_ZIP_WITH_LEN;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, zip_arg: &'tcx Expr<'_>) {
+    if_chain! {
+        if is_trait_method(cx, expr, sym::Iterator);
+        // range expression in `.zip()` call: `0..x.len()`
+        if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg);
+        if is_integer_const(cx, start, 0);
+        // `.len()` call
+        if let ExprKind::MethodCall(len_path, [len_recv], _) = end.kind;
+        if len_path.ident.name == sym::len;
+        // `.iter()` and `.len()` called on same `Path`
+        if let ExprKind::Path(QPath::Resolved(_, iter_path)) = recv.kind;
+        if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_recv.kind;
+        if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments);
+        then {
+            span_lint(cx,
+                RANGE_ZIP_WITH_LEN,
+                expr.span,
+                &format!("it is more idiomatic to use `{}.iter().enumerate()`",
+                    snippet(cx, recv.span, "_"))
+            );
+        }
+    }
+}
diff --git a/clippy_lints/src/methods/repeat_once.rs b/clippy_lints/src/methods/repeat_once.rs
new file mode 100644
index 00000000000..0a14f9216ab
--- /dev/null
+++ b/clippy_lints/src/methods/repeat_once.rs
@@ -0,0 +1,52 @@
+use clippy_utils::consts::{constant_context, Constant};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
+use rustc_errors::Applicability;
+use rustc_hir::Expr;
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::REPEAT_ONCE;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    recv: &'tcx Expr<'_>,
+    repeat_arg: &'tcx Expr<'_>,
+) {
+    if constant_context(cx, cx.typeck_results()).expr(repeat_arg) == Some(Constant::Int(1)) {
+        let ty = cx.typeck_results().expr_ty(recv).peel_refs();
+        if ty.is_str() {
+            span_lint_and_sugg(
+                cx,
+                REPEAT_ONCE,
+                expr.span,
+                "calling `repeat(1)` on str",
+                "consider using `.to_string()` instead",
+                format!("{}.to_string()", snippet(cx, recv.span, r#""...""#)),
+                Applicability::MachineApplicable,
+            );
+        } else if ty.builtin_index().is_some() {
+            span_lint_and_sugg(
+                cx,
+                REPEAT_ONCE,
+                expr.span,
+                "calling `repeat(1)` on slice",
+                "consider using `.to_vec()` instead",
+                format!("{}.to_vec()", snippet(cx, recv.span, r#""...""#)),
+                Applicability::MachineApplicable,
+            );
+        } else if is_type_diagnostic_item(cx, ty, sym::String) {
+            span_lint_and_sugg(
+                cx,
+                REPEAT_ONCE,
+                expr.span,
+                "calling `repeat(1)` on a string literal",
+                "consider using `.clone()` instead",
+                format!("{}.clone()", snippet(cx, recv.span, r#""...""#)),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+}
diff --git a/clippy_lints/src/methods/stable_sort_primitive.rs b/clippy_lints/src/methods/stable_sort_primitive.rs
new file mode 100644
index 00000000000..91951c65bb3
--- /dev/null
+++ b/clippy_lints/src/methods/stable_sort_primitive.rs
@@ -0,0 +1,31 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::is_slice_of_primitives;
+use clippy_utils::source::snippet_with_context;
+use rustc_errors::Applicability;
+use rustc_hir::Expr;
+use rustc_lint::LateContext;
+
+use super::STABLE_SORT_PRIMITIVE;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
+    if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
+        && let Some(impl_id) = cx.tcx.impl_of_method(method_id)
+        && cx.tcx.type_of(impl_id).is_slice()
+        && let Some(slice_type) = is_slice_of_primitives(cx, recv)
+    {
+        span_lint_and_then(
+            cx,
+            STABLE_SORT_PRIMITIVE,
+            e.span,
+            &format!("used `sort` on primitive type `{}`", slice_type),
+            |diag| {
+                let mut app = Applicability::MachineApplicable;
+                let recv_snip = snippet_with_context(cx, recv.span, e.span.ctxt(), "..", &mut app).0;
+                diag.span_suggestion(e.span, "try", format!("{}.sort_unstable()", recv_snip), app);
+                diag.note(
+                    "an unstable sort typically performs faster without any observable difference for this data type",
+                );
+            },
+        );
+    }
+}
diff --git a/clippy_lints/src/methods/suspicious_to_owned.rs b/clippy_lints/src/methods/suspicious_to_owned.rs
new file mode 100644
index 00000000000..6b306fbf008
--- /dev/null
+++ b/clippy_lints/src/methods/suspicious_to_owned.rs
@@ -0,0 +1,36 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_diag_trait_item;
+use clippy_utils::source::snippet_with_context;
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_span::sym;
+
+use super::SUSPICIOUS_TO_OWNED;
+
+pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) -> bool {
+    if_chain! {
+        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+        if is_diag_trait_item(cx, method_def_id, sym::ToOwned);
+        let input_type = cx.typeck_results().expr_ty(expr);
+        if let ty::Adt(adt, _) = cx.typeck_results().expr_ty(expr).kind();
+        if cx.tcx.is_diagnostic_item(sym::Cow, adt.did());
+        then {
+            let mut app = Applicability::MaybeIncorrect;
+            let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0;
+            span_lint_and_sugg(
+                cx,
+                SUSPICIOUS_TO_OWNED,
+                expr.span,
+                &format!("this `to_owned` call clones the {0} itself and does not cause the {0} contents to become owned", input_type),
+                "consider using, depending on intent",
+                format!("{0}.clone()` or `{0}.into_owned()", recv_snip),
+                app,
+            );
+            return true;
+        }
+    }
+    false
+}
diff --git a/clippy_lints/src/methods/uninit_assumed_init.rs b/clippy_lints/src/methods/uninit_assumed_init.rs
index 77d21f1d373..a1c6294737c 100644
--- a/clippy_lints/src/methods/uninit_assumed_init.rs
+++ b/clippy_lints/src/methods/uninit_assumed_init.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::{is_expr_diagnostic_item, ty::is_uninit_value_valid_for_ty};
+use clippy_utils::{is_path_diagnostic_item, ty::is_uninit_value_valid_for_ty};
 use if_chain::if_chain;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
@@ -12,7 +12,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
     if_chain! {
         if let hir::ExprKind::Call(callee, args) = recv.kind;
         if args.is_empty();
-        if is_expr_diagnostic_item(cx, callee, sym::maybe_uninit_uninit);
+        if is_path_diagnostic_item(cx, callee, sym::maybe_uninit_uninit);
         if !is_uninit_value_valid_for_ty(cx, cx.typeck_results().expr_ty_adjusted(expr));
         then {
             span_lint(
diff --git a/clippy_lints/src/methods/unit_hash.rs b/clippy_lints/src/methods/unit_hash.rs
new file mode 100644
index 00000000000..3c7955bc469
--- /dev/null
+++ b/clippy_lints/src/methods/unit_hash.rs
@@ -0,0 +1,29 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::is_trait_method;
+use clippy_utils::source::snippet;
+use rustc_errors::Applicability;
+use rustc_hir::Expr;
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::UNIT_HASH;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) {
+    if is_trait_method(cx, expr, sym::Hash) && cx.typeck_results().expr_ty(recv).is_unit() {
+        span_lint_and_then(
+            cx,
+            UNIT_HASH,
+            expr.span,
+            "this call to `hash` on the unit type will do nothing",
+            |diag| {
+                diag.span_suggestion(
+                    expr.span,
+                    "remove the call to `hash` or consider using",
+                    format!("0_u8.hash({})", snippet(cx, arg.span, ".."),),
+                    Applicability::MaybeIncorrect,
+                );
+                diag.note("the implementation of `Hash` for `()` is a no-op");
+            },
+        );
+    }
+}
diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/methods/unnecessary_sort_by.rs
index ea5aadbbca1..1966990bd77 100644
--- a/clippy_lints/src/unnecessary_sort_by.rs
+++ b/clippy_lints/src/methods/unnecessary_sort_by.rs
@@ -1,51 +1,17 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_trait_method;
 use clippy_utils::sugg::Sugg;
-use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
+use clippy_utils::ty::implements_trait;
 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, LateLintPass};
+use rustc_lint::LateContext;
 use rustc_middle::ty::{self, subst::GenericArgKind};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::sym;
 use rustc_span::symbol::Ident;
 use std::iter;
 
-declare_clippy_lint! {
-    /// ### What it does
-    /// Detects uses of `Vec::sort_by` passing in a closure
-    /// which compares the two arguments, either directly or indirectly.
-    ///
-    /// ### Why is this bad?
-    /// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if
-    /// possible) than to use `Vec::sort_by` and a more complicated
-    /// closure.
-    ///
-    /// ### Known problems
-    /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't already
-    /// imported by a use statement, then it will need to be added manually.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # struct A;
-    /// # impl A { fn foo(&self) {} }
-    /// # let mut vec: Vec<A> = Vec::new();
-    /// vec.sort_by(|a, b| a.foo().cmp(&b.foo()));
-    /// ```
-    /// Use instead:
-    /// ```rust
-    /// # struct A;
-    /// # impl A { fn foo(&self) {} }
-    /// # let mut vec: Vec<A> = Vec::new();
-    /// vec.sort_by_key(|a| a.foo());
-    /// ```
-    #[clippy::version = "1.46.0"]
-    pub UNNECESSARY_SORT_BY,
-    complexity,
-    "Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer"
-}
-
-declare_lint_pass!(UnnecessarySortBy => [UNNECESSARY_SORT_BY]);
+use super::UNNECESSARY_SORT_BY;
 
 enum LintTrigger {
     Sort(SortDetection),
@@ -54,7 +20,6 @@ enum LintTrigger {
 
 struct SortDetection {
     vec_name: String,
-    unstable: bool,
 }
 
 struct SortByKeyDetection {
@@ -62,7 +27,6 @@ struct SortByKeyDetection {
     closure_arg: String,
     closure_body: String,
     reverse: bool,
-    unstable: bool,
 }
 
 /// Detect if the two expressions are mirrored (identical, except one
@@ -150,20 +114,20 @@ fn mirrored_exprs(a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident
     }
 }
 
-fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
+fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) -> Option<LintTrigger> {
     if_chain! {
-        if let ExprKind::MethodCall(name_ident, args, _) = &expr.kind;
-        if let name = name_ident.ident.name.to_ident_string();
-        if name == "sort_by" || name == "sort_unstable_by";
-        if let [vec, Expr { kind: ExprKind::Closure(Closure { body: closure_body_id, .. }), .. }] = args;
-        if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(vec), sym::Vec);
-        if let closure_body = cx.tcx.hir().body(*closure_body_id);
+        if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+        if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
+        if cx.tcx.type_of(impl_id).is_slice();
+        if let ExprKind::Closure(&Closure { body, .. }) = arg.kind;
+        if let closure_body = cx.tcx.hir().body(body);
         if let &[
             Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..},
             Param { pat: Pat { kind: PatKind::Binding(_, _, right_ident, _), .. }, .. }
         ] = &closure_body.params;
-        if let ExprKind::MethodCall(method_path, [ref left_expr, ref right_expr], _) = &closure_body.value.kind;
+        if let ExprKind::MethodCall(method_path, [left_expr, right_expr], _) = closure_body.value.kind;
         if method_path.ident.name == sym::cmp;
+        if is_trait_method(cx, &closure_body.value, sym::Ord);
         then {
             let (closure_body, closure_arg, reverse) = if mirrored_exprs(
                 left_expr,
@@ -177,19 +141,18 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
             } else {
                 return None;
             };
-            let vec_name = Sugg::hir(cx, &args[0], "..").to_string();
-            let unstable = name == "sort_unstable_by";
+            let vec_name = Sugg::hir(cx, recv, "..").to_string();
 
             if_chain! {
-            if let ExprKind::Path(QPath::Resolved(_, Path {
-                segments: [PathSegment { ident: left_name, .. }], ..
-            })) = &left_expr.kind;
-            if left_name == left_ident;
-            if cx.tcx.get_diagnostic_item(sym::Ord).map_or(false, |id| {
-                implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[])
-            });
+                if let ExprKind::Path(QPath::Resolved(_, Path {
+                    segments: [PathSegment { ident: left_name, .. }], ..
+                })) = &left_expr.kind;
+                if left_name == left_ident;
+                if cx.tcx.get_diagnostic_item(sym::Ord).map_or(false, |id| {
+                    implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[])
+                });
                 then {
-                    return Some(LintTrigger::Sort(SortDetection { vec_name, unstable }));
+                    return Some(LintTrigger::Sort(SortDetection { vec_name }));
                 }
             }
 
@@ -199,7 +162,6 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
                     closure_arg,
                     closure_body,
                     reverse,
-                    unstable,
                 }));
             }
         }
@@ -213,46 +175,50 @@ fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     matches!(ty.kind(), ty::Ref(..)) || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)))
 }
 
-impl LateLintPass<'_> for UnnecessarySortBy {
-    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
-        match detect_lint(cx, expr) {
-            Some(LintTrigger::SortByKey(trigger)) => span_lint_and_sugg(
-                cx,
-                UNNECESSARY_SORT_BY,
-                expr.span,
-                "use Vec::sort_by_key here instead",
-                "try",
-                format!(
-                    "{}.sort{}_by_key(|{}| {})",
-                    trigger.vec_name,
-                    if trigger.unstable { "_unstable" } else { "" },
-                    trigger.closure_arg,
-                    if trigger.reverse {
-                        format!("std::cmp::Reverse({})", trigger.closure_body)
-                    } else {
-                        trigger.closure_body.to_string()
-                    },
-                ),
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    recv: &'tcx Expr<'_>,
+    arg: &'tcx Expr<'_>,
+    is_unstable: bool,
+) {
+    match detect_lint(cx, expr, recv, arg) {
+        Some(LintTrigger::SortByKey(trigger)) => span_lint_and_sugg(
+            cx,
+            UNNECESSARY_SORT_BY,
+            expr.span,
+            "use Vec::sort_by_key here instead",
+            "try",
+            format!(
+                "{}.sort{}_by_key(|{}| {})",
+                trigger.vec_name,
+                if is_unstable { "_unstable" } else { "" },
+                trigger.closure_arg,
                 if trigger.reverse {
-                    Applicability::MaybeIncorrect
+                    format!("std::cmp::Reverse({})", trigger.closure_body)
                 } else {
-                    Applicability::MachineApplicable
+                    trigger.closure_body.to_string()
                 },
             ),
-            Some(LintTrigger::Sort(trigger)) => span_lint_and_sugg(
-                cx,
-                UNNECESSARY_SORT_BY,
-                expr.span,
-                "use Vec::sort here instead",
-                "try",
-                format!(
-                    "{}.sort{}()",
-                    trigger.vec_name,
-                    if trigger.unstable { "_unstable" } else { "" },
-                ),
-                Applicability::MachineApplicable,
+            if trigger.reverse {
+                Applicability::MaybeIncorrect
+            } else {
+                Applicability::MachineApplicable
+            },
+        ),
+        Some(LintTrigger::Sort(trigger)) => span_lint_and_sugg(
+            cx,
+            UNNECESSARY_SORT_BY,
+            expr.span,
+            "use Vec::sort here instead",
+            "try",
+            format!(
+                "{}.sort{}()",
+                trigger.vec_name,
+                if is_unstable { "_unstable" } else { "" },
             ),
-            None => {},
-        }
+            Applicability::MachineApplicable,
+        ),
+        None => {},
     }
 }
diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs
index b3276f1394e..44bf8435294 100644
--- a/clippy_lints/src/methods/unnecessary_to_owned.rs
+++ b/clippy_lints/src/methods/unnecessary_to_owned.rs
@@ -3,11 +3,10 @@ use super::unnecessary_iter_cloned::{self, is_into_iter};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::{
-    contains_ty, get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs,
+    get_associated_type, get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, peel_mid_ty_refs,
 };
-use clippy_utils::{meets_msrv, msrvs};
-
 use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item};
+use clippy_utils::{meets_msrv, msrvs};
 use rustc_errors::Applicability;
 use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind};
 use rustc_lint::LateContext;
@@ -279,7 +278,19 @@ fn check_other_call_arg<'tcx>(
                 &trait_predicate.trait_ref.substs.iter().skip(1).collect::<Vec<_>>()[..],
                 call_substs,
             );
-            implements_trait(cx, receiver_ty, as_ref_trait_id, &composed_substs)
+            // if `expr` is a `String` and generic target is [u8], skip
+            // (https://github.com/rust-lang/rust-clippy/issues/9317).
+            if let [subst] = composed_substs[..]
+                && let GenericArgKind::Type(arg_ty) = subst.unpack()
+                && arg_ty.is_slice()
+                && let inner_ty = arg_ty.builtin_index().unwrap()
+                && let ty::Uint(ty::UintTy::U8) = inner_ty.kind()
+                && let self_ty = cx.typeck_results().expr_ty(expr).peel_refs()
+                && is_type_diagnostic_item(cx, self_ty, sym::String) {
+                false
+            } else {
+                implements_trait(cx, receiver_ty, as_ref_trait_id, &composed_substs)
+            }
         } else {
             false
         };
@@ -292,7 +303,7 @@ fn check_other_call_arg<'tcx>(
         // (https://github.com/rust-lang/rust-clippy/issues/8507).
         if (n_refs == 0 && !receiver_ty.is_ref())
             || trait_predicate.def_id() != as_ref_trait_id
-            || !contains_ty(fn_sig.output(), input);
+            || !fn_sig.output().contains(input);
         if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
         then {
             span_lint_and_sugg(
@@ -360,25 +371,15 @@ fn get_input_traits_and_projections<'tcx>(
 ) -> (Vec<TraitPredicate<'tcx>>, Vec<ProjectionPredicate<'tcx>>) {
     let mut trait_predicates = Vec::new();
     let mut projection_predicates = Vec::new();
-    for (predicate, _) in cx.tcx.predicates_of(callee_def_id).predicates.iter() {
-        // `substs` should have 1 + n elements. The first is the type on the left hand side of an
-        // `as`. The remaining n are trait parameters.
-        let is_input_substs = |substs: SubstsRef<'tcx>| {
-            if_chain! {
-                if let Some(arg) = substs.iter().next();
-                if let GenericArgKind::Type(arg_ty) = arg.unpack();
-                if arg_ty == input;
-                then { true } else { false }
-            }
-        };
+    for predicate in cx.tcx.param_env(callee_def_id).caller_bounds() {
         match predicate.kind().skip_binder() {
             PredicateKind::Trait(trait_predicate) => {
-                if is_input_substs(trait_predicate.trait_ref.substs) {
+                if trait_predicate.trait_ref.self_ty() == input {
                     trait_predicates.push(trait_predicate);
                 }
             },
             PredicateKind::Projection(projection_predicate) => {
-                if is_input_substs(projection_predicate.projection_ty.substs) {
+                if projection_predicate.projection_ty.self_ty() == input {
                     projection_predicates.push(projection_predicate);
                 }
             },
diff --git a/clippy_lints/src/methods/unwrap_used.rs b/clippy_lints/src/methods/unwrap_used.rs
index ce1a52e5480..ee17f2d7889 100644
--- a/clippy_lints/src/methods/unwrap_used.rs
+++ b/clippy_lints/src/methods/unwrap_used.rs
@@ -7,18 +7,26 @@ use rustc_span::sym;
 
 use super::{EXPECT_USED, UNWRAP_USED};
 
-/// lint use of `unwrap()` for `Option`s and `Result`s
-pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, allow_unwrap_in_tests: bool) {
+/// 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) {
+    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", "Err", "an "))
+        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) {
         return;
     }
@@ -27,7 +35,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
         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()` to provide a better panic message"
+                using `expect{method_suffix}()` to provide a better panic message"
             )
         } else {
             format!("if this value is {none_prefix}`{none_value}`, it will panic")
@@ -37,7 +45,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
             cx,
             lint,
             expr.span,
-            &format!("used `unwrap()` on `{kind}` value"),
+            &format!("used `unwrap{method_suffix}()` on `{kind}` value"),
             None,
             &help,
         );
diff --git a/clippy_lints/src/methods/vec_resize_to_zero.rs b/clippy_lints/src/methods/vec_resize_to_zero.rs
new file mode 100644
index 00000000000..02d8364cb29
--- /dev/null
+++ b/clippy_lints/src/methods/vec_resize_to_zero.rs
@@ -0,0 +1,45 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::ty::is_type_diagnostic_item;
+use if_chain::if_chain;
+use rustc_ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_span::source_map::Spanned;
+use rustc_span::{sym, Span};
+
+use super::VEC_RESIZE_TO_ZERO;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    count_arg: &'tcx Expr<'_>,
+    default_arg: &'tcx Expr<'_>,
+    name_span: Span,
+) {
+    if_chain! {
+        if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+        if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
+        if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Vec);
+        if let ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = count_arg.kind;
+        if let ExprKind::Lit(Spanned { node: LitKind::Int(..), .. }) = default_arg.kind;
+        then {
+            let method_call_span = expr.span.with_lo(name_span.lo());
+            span_lint_and_then(
+                cx,
+                VEC_RESIZE_TO_ZERO,
+                expr.span,
+                "emptying a vector with `resize`",
+                |db| {
+                    db.help("the arguments may be inverted...");
+                    db.span_suggestion(
+                        method_call_span,
+                        "...or you can empty the vector with",
+                        "clear()".to_string(),
+                        Applicability::MaybeIncorrect,
+                    );
+                },
+            );
+        }
+    }
+}
diff --git a/clippy_lints/src/methods/verbose_file_reads.rs b/clippy_lints/src/methods/verbose_file_reads.rs
new file mode 100644
index 00000000000..2fe5ae9a9ad
--- /dev/null
+++ b/clippy_lints/src/methods/verbose_file_reads.rs
@@ -0,0 +1,28 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::is_trait_method;
+use clippy_utils::ty::is_type_diagnostic_item;
+use rustc_hir::{Expr, ExprKind, QPath};
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::VERBOSE_FILE_READS;
+
+pub(super) const READ_TO_END_MSG: (&str, &str) = ("use of `File::read_to_end`", "consider using `fs::read` instead");
+pub(super) const READ_TO_STRING_MSG: (&str, &str) = (
+    "use of `File::read_to_string`",
+    "consider using `fs::read_to_string` instead",
+);
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    recv: &'tcx Expr<'_>,
+    (msg, help): (&str, &str),
+) {
+    if is_trait_method(cx, expr, sym::IoRead)
+        && matches!(recv.kind, ExprKind::Path(QPath::Resolved(None, _)))
+        && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(recv).peel_refs(), sym::File)
+    {
+        span_lint_and_help(cx, VERBOSE_FILE_READS, expr.span, msg, None, help);
+    }
+}
diff --git a/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs b/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs
index df044538fe1..7c4ae746e90 100644
--- a/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs
+++ b/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs
@@ -46,7 +46,7 @@ fn span_lint(cx: &EarlyContext<'_>, span: Span, only_one: bool) {
             "these patterns are unneeded as the `..` pattern can match those elements"
         },
         if only_one { "remove it" } else { "remove them" },
-        "".to_string(),
+        String::new(),
         Applicability::MachineApplicable,
     );
 }
diff --git a/clippy_lints/src/mismatching_type_param_order.rs b/clippy_lints/src/mismatching_type_param_order.rs
index f763e0d24c9..020efeaebf0 100644
--- a/clippy_lints/src/mismatching_type_param_order.rs
+++ b/clippy_lints/src/mismatching_type_param_order.rs
@@ -40,7 +40,7 @@ declare_clippy_lint! {
     /// }
     /// impl<A, B> Foo<A, B> {}
     /// ```
-    #[clippy::version = "1.62.0"]
+    #[clippy::version = "1.63.0"]
     pub MISMATCHING_TYPE_PARAM_ORDER,
     pedantic,
     "type parameter positioned inconsistently between type def and impl block"
diff --git a/clippy_lints/src/multi_assignments.rs b/clippy_lints/src/multi_assignments.rs
new file mode 100644
index 00000000000..81eb1a085ae
--- /dev/null
+++ b/clippy_lints/src/multi_assignments.rs
@@ -0,0 +1,65 @@
+use clippy_utils::diagnostics::span_lint;
+use rustc_ast::ast::{Expr, ExprKind, Stmt, StmtKind};
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for nested assignments.
+    ///
+    /// ### Why is this bad?
+    /// While this is in most cases already a type mismatch,
+    /// the result of an assignment being `()` can throw off people coming from languages like python or C,
+    /// where such assignments return a copy of the assigned value.
+    ///
+    /// ### Example
+    /// ```rust
+    ///# let (a, b);
+    /// a = b = 42;
+    /// ```
+    /// Use instead:
+    /// ```rust
+    ///# let (a, b);
+    /// b = 42;
+    /// a = b;
+    /// ```
+    #[clippy::version = "1.65.0"]
+    pub MULTI_ASSIGNMENTS,
+    suspicious,
+    "instead of using `a = b = c;` use `a = c; b = c;`"
+}
+
+declare_lint_pass!(MultiAssignments => [MULTI_ASSIGNMENTS]);
+
+fn strip_paren_blocks(expr: &Expr) -> &Expr {
+    match &expr.kind {
+        ExprKind::Paren(e) => strip_paren_blocks(e),
+        ExprKind::Block(b, _) => {
+            if let [
+                Stmt {
+                    kind: StmtKind::Expr(e),
+                    ..
+                },
+            ] = &b.stmts[..]
+            {
+                strip_paren_blocks(e)
+            } else {
+                expr
+            }
+        },
+        _ => expr,
+    }
+}
+
+impl EarlyLintPass for MultiAssignments {
+    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
+        if let ExprKind::Assign(target, source, _) = &expr.kind {
+            if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(target).kind {
+                span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively");
+            };
+            if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(source).kind {
+                span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively");
+            }
+        };
+    }
+}
diff --git a/clippy_lints/src/mut_mutex_lock.rs b/clippy_lints/src/mut_mutex_lock.rs
deleted file mode 100644
index b7f981faa2d..00000000000
--- a/clippy_lints/src/mut_mutex_lock.rs
+++ /dev/null
@@ -1,70 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::ty::is_type_diagnostic_item;
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, Mutability};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for `&mut Mutex::lock` calls
-    ///
-    /// ### Why is this bad?
-    /// `Mutex::lock` is less efficient than
-    /// calling `Mutex::get_mut`. In addition you also have a statically
-    /// guarantee that the mutex isn't locked, instead of just a runtime
-    /// guarantee.
-    ///
-    /// ### Example
-    /// ```rust
-    /// use std::sync::{Arc, Mutex};
-    ///
-    /// let mut value_rc = Arc::new(Mutex::new(42_u8));
-    /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
-    ///
-    /// let mut value = value_mutex.lock().unwrap();
-    /// *value += 1;
-    /// ```
-    /// Use instead:
-    /// ```rust
-    /// use std::sync::{Arc, Mutex};
-    ///
-    /// let mut value_rc = Arc::new(Mutex::new(42_u8));
-    /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
-    ///
-    /// let value = value_mutex.get_mut().unwrap();
-    /// *value += 1;
-    /// ```
-    #[clippy::version = "1.49.0"]
-    pub MUT_MUTEX_LOCK,
-    style,
-    "`&mut Mutex::lock` does unnecessary locking"
-}
-
-declare_lint_pass!(MutMutexLock => [MUT_MUTEX_LOCK]);
-
-impl<'tcx> LateLintPass<'tcx> for MutMutexLock {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>) {
-        if_chain! {
-            if let ExprKind::MethodCall(path, [self_arg, ..], _) = &ex.kind;
-            if path.ident.name == sym!(lock);
-            let ty = cx.typeck_results().expr_ty(self_arg);
-            if let ty::Ref(_, inner_ty, Mutability::Mut) = ty.kind();
-            if is_type_diagnostic_item(cx, *inner_ty, sym::Mutex);
-            then {
-                span_lint_and_sugg(
-                    cx,
-                    MUT_MUTEX_LOCK,
-                    path.ident.span,
-                    "calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference",
-                    "change this to",
-                    "get_mut".to_owned(),
-                    Applicability::MaybeIncorrect,
-                );
-            }
-        }
-    }
-}
diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs
index 413a740be25..774a3540d1e 100644
--- a/clippy_lints/src/only_used_in_recursion.rs
+++ b/clippy_lints/src/only_used_in_recursion.rs
@@ -1,25 +1,16 @@
-use std::collections::VecDeque;
-
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::is_lint_allowed;
-use itertools::{izip, Itertools};
-use rustc_ast::{walk_list, Label, Mutability};
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::{get_expr_use_or_unification_node, get_parent_node, path_def_id, path_to_local, path_to_local_id};
+use core::cell::Cell;
+use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::Applicability;
-use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
-use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
-use rustc_hir::intravisit::{walk_expr, walk_stmt, FnKind, Visitor};
-use rustc_hir::{
-    Arm, Block, Body, Closure, Expr, ExprKind, Guard, HirId, ImplicitSelfKind, Let, Local, Pat, PatKind, Path,
-    PathSegment, QPath, Stmt, StmtKind, TyKind, UnOp,
-};
+use rustc_hir::hir_id::HirIdMap;
+use rustc_hir::{Body, Expr, ExprKind, HirId, ImplItem, ImplItemKind, Node, PatKind, TraitItem, TraitItemKind};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
-use rustc_middle::ty::{Ty, TyCtxt, TypeckResults};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::kw;
-use rustc_span::symbol::Ident;
+use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
+use rustc_middle::ty::{self, ConstKind};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::symbol::{kw, Ident};
 use rustc_span::Span;
 
 declare_clippy_lint! {
@@ -89,572 +80,323 @@ declare_clippy_lint! {
     /// ```
     #[clippy::version = "1.61.0"]
     pub ONLY_USED_IN_RECURSION,
-    nursery,
+    complexity,
     "arguments that is only used in recursion can be removed"
 }
-declare_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]);
-
-impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
-    fn check_fn(
-        &mut self,
-        cx: &LateContext<'tcx>,
-        kind: FnKind<'tcx>,
-        decl: &'tcx rustc_hir::FnDecl<'tcx>,
-        body: &'tcx Body<'tcx>,
-        _: Span,
-        id: HirId,
-    ) {
-        if is_lint_allowed(cx, ONLY_USED_IN_RECURSION, id) {
-            return;
-        }
-        if let FnKind::ItemFn(ident, ..) | FnKind::Method(ident, ..) = kind {
-            let def_id = id.owner.to_def_id();
-            let data = cx.tcx.def_path(def_id).data;
-
-            if data.len() > 1 {
-                match data.get(data.len() - 2) {
-                    Some(DisambiguatedDefPathData {
-                        data: DefPathData::Impl,
-                        disambiguator,
-                    }) if *disambiguator != 0 => return,
-                    _ => {},
-                }
-            }
-
-            let has_self = !matches!(decl.implicit_self, ImplicitSelfKind::None);
-
-            let ty_res = cx.typeck_results();
-            let param_span = body
-                .params
-                .iter()
-                .flat_map(|param| {
-                    let mut v = Vec::new();
-                    param.pat.each_binding(|_, hir_id, span, ident| {
-                        v.push((hir_id, span, ident));
-                    });
-                    v
-                })
-                .skip(if has_self { 1 } else { 0 })
-                .filter(|(_, _, ident)| !ident.name.as_str().starts_with('_'))
-                .collect_vec();
-
-            let params = body.params.iter().map(|param| param.pat).collect();
-
-            let mut visitor = SideEffectVisit {
-                graph: FxHashMap::default(),
-                has_side_effect: FxHashSet::default(),
-                ret_vars: Vec::new(),
-                contains_side_effect: false,
-                break_vars: FxHashMap::default(),
-                params,
-                fn_ident: ident,
-                fn_def_id: def_id,
-                is_method: matches!(kind, FnKind::Method(..)),
-                has_self,
-                ty_res,
-                tcx: cx.tcx,
-                visited_exprs: FxHashSet::default(),
-            };
-
-            visitor.visit_expr(&body.value);
-            let vars = std::mem::take(&mut visitor.ret_vars);
-            // this would set the return variables to side effect
-            visitor.add_side_effect(vars);
-
-            let mut queue = visitor.has_side_effect.iter().copied().collect::<VecDeque<_>>();
-
-            // a simple BFS to check all the variables that have side effect
-            while let Some(id) = queue.pop_front() {
-                if let Some(next) = visitor.graph.get(&id) {
-                    for i in next {
-                        if !visitor.has_side_effect.contains(i) {
-                            visitor.has_side_effect.insert(*i);
-                            queue.push_back(*i);
-                        }
-                    }
-                }
-            }
-
-            for (id, span, ident) in param_span {
-                // if the variable is not used in recursion, it would be marked as unused
-                if !visitor.has_side_effect.contains(&id) {
-                    let mut queue = VecDeque::new();
-                    let mut visited = FxHashSet::default();
-
-                    queue.push_back(id);
-
-                    // a simple BFS to check the graph can reach to itself
-                    // if it can't, it means the variable is never used in recursion
-                    while let Some(id) = queue.pop_front() {
-                        if let Some(next) = visitor.graph.get(&id) {
-                            for i in next {
-                                if !visited.contains(i) {
-                                    visited.insert(id);
-                                    queue.push_back(*i);
-                                }
-                            }
-                        }
-                    }
+impl_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]);
+
+#[derive(Clone, Copy)]
+enum FnKind {
+    Fn,
+    TraitFn,
+    // This is a hack. Ideally we would store a `SubstsRef<'tcx>` type here, but a lint pass must be `'static`.
+    // Substitutions are, however, interned. This allows us to store the pointer as a `usize` when comparing for
+    // equality.
+    ImplTraitFn(usize),
+}
 
-                    if visited.contains(&id) {
-                        span_lint_and_sugg(
-                            cx,
-                            ONLY_USED_IN_RECURSION,
-                            span,
-                            "parameter is only used in recursion",
-                            "if this is intentional, prefix with an underscore",
-                            format!("_{}", ident.name.as_str()),
-                            Applicability::MaybeIncorrect,
-                        );
-                    }
-                }
-            }
+struct Param {
+    /// The function this is a parameter for.
+    fn_id: DefId,
+    fn_kind: FnKind,
+    /// The index of this parameter.
+    idx: usize,
+    ident: Ident,
+    /// Whether this parameter should be linted. Set by `Params::flag_for_linting`.
+    apply_lint: Cell<bool>,
+    /// All the uses of this parameter.
+    uses: Vec<Usage>,
+}
+impl Param {
+    fn new(fn_id: DefId, fn_kind: FnKind, idx: usize, ident: Ident) -> Self {
+        Self {
+            fn_id,
+            fn_kind,
+            idx,
+            ident,
+            apply_lint: Cell::new(true),
+            uses: Vec::new(),
         }
     }
 }
 
-pub fn is_primitive(ty: Ty<'_>) -> bool {
-    let ty = ty.peel_refs();
-    ty.is_primitive() || ty.is_str()
+#[derive(Debug)]
+struct Usage {
+    span: Span,
+    idx: usize,
 }
-
-pub fn is_array(ty: Ty<'_>) -> bool {
-    let ty = ty.peel_refs();
-    ty.is_array() || ty.is_array_slice()
+impl Usage {
+    fn new(span: Span, idx: usize) -> Self {
+        Self { span, idx }
+    }
 }
 
-/// This builds the graph of side effect.
-/// The edge `a -> b` means if `a` has side effect, `b` will have side effect.
-///
-/// There are some example in following code:
-/// ```rust, ignore
-/// let b = 1;
-/// let a = b; // a -> b
-/// let (c, d) = (a, b); // c -> b, d -> b
-///
-/// let e = if a == 0 { // e -> a
-///     c // e -> c
-/// } else {
-///     d // e -> d
-/// };
-/// ```
-pub struct SideEffectVisit<'tcx> {
-    graph: FxHashMap<HirId, FxHashSet<HirId>>,
-    has_side_effect: FxHashSet<HirId>,
-    // bool for if the variable was dereferenced from mutable reference
-    ret_vars: Vec<(HirId, bool)>,
-    contains_side_effect: bool,
-    // break label
-    break_vars: FxHashMap<Ident, Vec<(HirId, bool)>>,
-    params: Vec<&'tcx Pat<'tcx>>,
-    fn_ident: Ident,
-    fn_def_id: DefId,
-    is_method: bool,
-    has_self: bool,
-    ty_res: &'tcx TypeckResults<'tcx>,
-    tcx: TyCtxt<'tcx>,
-    visited_exprs: FxHashSet<HirId>,
+/// The parameters being checked by the lint, indexed by both the parameter's `HirId` and the
+/// `DefId` of the function paired with the parameter's index.
+#[derive(Default)]
+struct Params {
+    params: Vec<Param>,
+    by_id: HirIdMap<usize>,
+    by_fn: FxHashMap<(DefId, usize), usize>,
 }
-
-impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> {
-    fn visit_stmt(&mut self, s: &'tcx Stmt<'tcx>) {
-        match s.kind {
-            StmtKind::Local(Local {
-                pat, init: Some(init), ..
-            }) => {
-                self.visit_pat_expr(pat, init, false);
-            },
-            StmtKind::Item(_) | StmtKind::Expr(_) | StmtKind::Semi(_) => {
-                walk_stmt(self, s);
-            },
-            StmtKind::Local(_) => {},
-        }
-        self.ret_vars.clear();
+impl Params {
+    fn insert(&mut self, param: Param, id: HirId) {
+        let idx = self.params.len();
+        self.by_id.insert(id, idx);
+        self.by_fn.insert((param.fn_id, param.idx), idx);
+        self.params.push(param);
     }
 
-    fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
-        if !self.visited_exprs.insert(ex.hir_id) {
-            return;
-        }
-        match ex.kind {
-            ExprKind::Array(exprs) | ExprKind::Tup(exprs) => {
-                self.ret_vars = exprs
-                    .iter()
-                    .flat_map(|expr| {
-                        self.visit_expr(expr);
-                        std::mem::take(&mut self.ret_vars)
-                    })
-                    .collect();
-            },
-            ExprKind::Call(callee, args) => self.visit_fn(callee, args),
-            ExprKind::MethodCall(path, args, _) => self.visit_method_call(path, args),
-            ExprKind::Binary(_, lhs, rhs) => {
-                self.visit_bin_op(lhs, rhs);
-            },
-            ExprKind::Unary(op, expr) => self.visit_un_op(op, expr),
-            ExprKind::Let(Let { pat, init, .. }) => self.visit_pat_expr(pat, init, false),
-            ExprKind::If(bind, then_expr, else_expr) => {
-                self.visit_if(bind, then_expr, else_expr);
-            },
-            ExprKind::Match(expr, arms, _) => self.visit_match(expr, arms),
-            // since analysing the closure is not easy, just set all variables in it to side-effect
-            ExprKind::Closure(&Closure { body, .. }) => {
-                let body = self.tcx.hir().body(body);
-                self.visit_body(body);
-                let vars = std::mem::take(&mut self.ret_vars);
-                self.add_side_effect(vars);
-            },
-            ExprKind::Loop(block, label, _, _) | ExprKind::Block(block, label) => {
-                self.visit_block_label(block, label);
-            },
-            ExprKind::Assign(bind, expr, _) => {
-                self.visit_assign(bind, expr);
-            },
-            ExprKind::AssignOp(_, bind, expr) => {
-                self.visit_assign(bind, expr);
-                self.visit_bin_op(bind, expr);
-            },
-            ExprKind::Field(expr, _) => {
-                self.visit_expr(expr);
-                if matches!(self.ty_res.expr_ty(expr).kind(), ty::Ref(_, _, Mutability::Mut)) {
-                    self.ret_vars.iter_mut().for_each(|(_, b)| *b = true);
-                }
-            },
-            ExprKind::Index(expr, index) => {
-                self.visit_expr(expr);
-                let mut vars = std::mem::take(&mut self.ret_vars);
-                self.visit_expr(index);
-                self.ret_vars.append(&mut vars);
-
-                if !is_array(self.ty_res.expr_ty(expr)) {
-                    self.add_side_effect(self.ret_vars.clone());
-                } else if matches!(self.ty_res.expr_ty(expr).kind(), ty::Ref(_, _, Mutability::Mut)) {
-                    self.ret_vars.iter_mut().for_each(|(_, b)| *b = true);
-                }
-            },
-            ExprKind::Break(dest, Some(expr)) => {
-                self.visit_expr(expr);
-                if let Some(label) = dest.label {
-                    self.break_vars
-                        .entry(label.ident)
-                        .or_insert(Vec::new())
-                        .append(&mut self.ret_vars);
-                }
-                self.contains_side_effect = true;
-            },
-            ExprKind::Ret(Some(expr)) => {
-                self.visit_expr(expr);
-                let vars = std::mem::take(&mut self.ret_vars);
-                self.add_side_effect(vars);
-                self.contains_side_effect = true;
-            },
-            ExprKind::Break(_, None) | ExprKind::Continue(_) | ExprKind::Ret(None) => {
-                self.contains_side_effect = true;
-            },
-            ExprKind::Struct(_, exprs, expr) => {
-                let mut ret_vars = exprs
-                    .iter()
-                    .flat_map(|field| {
-                        self.visit_expr(field.expr);
-                        std::mem::take(&mut self.ret_vars)
-                    })
-                    .collect();
-
-                walk_list!(self, visit_expr, expr);
-                self.ret_vars.append(&mut ret_vars);
-            },
-            _ => walk_expr(self, ex),
+    fn remove_by_id(&mut self, id: HirId) {
+        if let Some(param) = self.get_by_id_mut(id) {
+            param.uses = Vec::new();
+            let key = (param.fn_id, param.idx);
+            self.by_fn.remove(&key);
+            self.by_id.remove(&id);
         }
     }
 
-    fn visit_path(&mut self, path: &'tcx Path<'tcx>, _id: HirId) {
-        if let Res::Local(id) = path.res {
-            self.ret_vars.push((id, false));
-        }
+    fn get_by_id_mut(&mut self, id: HirId) -> Option<&mut Param> {
+        self.params.get_mut(*self.by_id.get(&id)?)
     }
-}
 
-impl<'tcx> SideEffectVisit<'tcx> {
-    fn visit_assign(&mut self, lhs: &'tcx Expr<'tcx>, rhs: &'tcx Expr<'tcx>) {
-        // Just support array and tuple unwrapping for now.
-        //
-        // ex) `(a, b) = (c, d);`
-        // The graph would look like this:
-        //   a -> c
-        //   b -> d
-        //
-        // This would minimize the connection of the side-effect graph.
-        match (&lhs.kind, &rhs.kind) {
-            (ExprKind::Array(lhs), ExprKind::Array(rhs)) | (ExprKind::Tup(lhs), ExprKind::Tup(rhs)) => {
-                // if not, it is a compile error
-                debug_assert!(lhs.len() == rhs.len());
-                izip!(*lhs, *rhs).for_each(|(lhs, rhs)| self.visit_assign(lhs, rhs));
-            },
-            // in other assigns, we have to connect all each other
-            // because they can be connected somehow
-            _ => {
-                self.visit_expr(lhs);
-                let lhs_vars = std::mem::take(&mut self.ret_vars);
-                self.visit_expr(rhs);
-                let rhs_vars = std::mem::take(&mut self.ret_vars);
-                self.connect_assign(&lhs_vars, &rhs_vars, false);
-            },
-        }
+    fn get_by_fn(&self, id: DefId, idx: usize) -> Option<&Param> {
+        self.params.get(*self.by_fn.get(&(id, idx))?)
     }
 
-    fn visit_block_label(&mut self, block: &'tcx Block<'tcx>, label: Option<Label>) {
-        self.visit_block(block);
-        let _ = label.and_then(|label| {
-            self.break_vars
-                .remove(&label.ident)
-                .map(|mut break_vars| self.ret_vars.append(&mut break_vars))
-        });
-    }
-
-    fn visit_bin_op(&mut self, lhs: &'tcx Expr<'tcx>, rhs: &'tcx Expr<'tcx>) {
-        self.visit_expr(lhs);
-        let mut ret_vars = std::mem::take(&mut self.ret_vars);
-        self.visit_expr(rhs);
-        self.ret_vars.append(&mut ret_vars);
-
-        // the binary operation between non primitive values are overloaded operators
-        // so they can have side-effects
-        if !is_primitive(self.ty_res.expr_ty(lhs)) || !is_primitive(self.ty_res.expr_ty(rhs)) {
-            self.ret_vars.iter().for_each(|id| {
-                self.has_side_effect.insert(id.0);
-            });
-            self.contains_side_effect = true;
-        }
+    fn clear(&mut self) {
+        self.params.clear();
+        self.by_id.clear();
+        self.by_fn.clear();
     }
 
-    fn visit_un_op(&mut self, op: UnOp, expr: &'tcx Expr<'tcx>) {
-        self.visit_expr(expr);
-        let ty = self.ty_res.expr_ty(expr);
-        // dereferencing a reference has no side-effect
-        if !is_primitive(ty) && !matches!((op, ty.kind()), (UnOp::Deref, ty::Ref(..))) {
-            self.add_side_effect(self.ret_vars.clone());
-        }
-
-        if matches!((op, ty.kind()), (UnOp::Deref, ty::Ref(_, _, Mutability::Mut))) {
-            self.ret_vars.iter_mut().for_each(|(_, b)| *b = true);
+    /// Sets the `apply_lint` flag on each parameter.
+    fn flag_for_linting(&mut self) {
+        // Stores the list of parameters currently being resolved. Needed to avoid cycles.
+        let mut eval_stack = Vec::new();
+        for param in &self.params {
+            self.try_disable_lint_for_param(param, &mut eval_stack);
         }
     }
 
-    fn visit_pat_expr(&mut self, pat: &'tcx Pat<'tcx>, expr: &'tcx Expr<'tcx>, connect_self: bool) {
-        match (&pat.kind, &expr.kind) {
-            (PatKind::Tuple(pats, _), ExprKind::Tup(exprs)) => {
-                self.ret_vars = izip!(*pats, *exprs)
-                    .flat_map(|(pat, expr)| {
-                        self.visit_pat_expr(pat, expr, connect_self);
-                        std::mem::take(&mut self.ret_vars)
-                    })
-                    .collect();
-            },
-            (PatKind::Slice(front_exprs, _, back_exprs), ExprKind::Array(exprs)) => {
-                let mut vars = izip!(*front_exprs, *exprs)
-                    .flat_map(|(pat, expr)| {
-                        self.visit_pat_expr(pat, expr, connect_self);
-                        std::mem::take(&mut self.ret_vars)
-                    })
-                    .collect();
-                self.ret_vars = izip!(back_exprs.iter().rev(), exprs.iter().rev())
-                    .flat_map(|(pat, expr)| {
-                        self.visit_pat_expr(pat, expr, connect_self);
-                        std::mem::take(&mut self.ret_vars)
-                    })
-                    .collect();
-                self.ret_vars.append(&mut vars);
-            },
-            _ => {
-                let mut lhs_vars = Vec::new();
-                pat.each_binding(|_, id, _, _| lhs_vars.push((id, false)));
-                self.visit_expr(expr);
-                let rhs_vars = std::mem::take(&mut self.ret_vars);
-                self.connect_assign(&lhs_vars, &rhs_vars, connect_self);
-                self.ret_vars = rhs_vars;
-            },
+    // Use by calling `flag_for_linting`.
+    fn try_disable_lint_for_param(&self, param: &Param, eval_stack: &mut Vec<usize>) -> bool {
+        if !param.apply_lint.get() {
+            true
+        } else if param.uses.is_empty() {
+            // Don't lint on unused parameters.
+            param.apply_lint.set(false);
+            true
+        } else if eval_stack.contains(&param.idx) {
+            // Already on the evaluation stack. Returning false will continue to evaluate other dependencies.
+            false
+        } else {
+            eval_stack.push(param.idx);
+            // Check all cases when used at a different parameter index.
+            // Needed to catch cases like: `fn f(x: u32, y: u32) { f(y, x) }`
+            for usage in param.uses.iter().filter(|u| u.idx != param.idx) {
+                if self
+                    .get_by_fn(param.fn_id, usage.idx)
+                    // If the parameter can't be found, then it's used for more than just recursion.
+                    .map_or(true, |p| self.try_disable_lint_for_param(p, eval_stack))
+                {
+                    param.apply_lint.set(false);
+                    eval_stack.pop();
+                    return true;
+                }
+            }
+            eval_stack.pop();
+            false
         }
     }
+}
 
-    fn visit_fn(&mut self, callee: &'tcx Expr<'tcx>, args: &'tcx [Expr<'tcx>]) {
-        self.visit_expr(callee);
-        let mut ret_vars = std::mem::take(&mut self.ret_vars);
-        self.add_side_effect(ret_vars.clone());
-
-        let mut is_recursive = false;
-
-        if_chain! {
-            if !self.has_self;
-            if let ExprKind::Path(QPath::Resolved(_, path)) = callee.kind;
-            if let Res::Def(DefKind::Fn, def_id) = path.res;
-            if self.fn_def_id == def_id;
-            then {
-                is_recursive = true;
-            }
-        }
+#[derive(Default)]
+pub struct OnlyUsedInRecursion {
+    /// Track the top-level body entered. Needed to delay reporting when entering nested bodies.
+    entered_body: Option<HirId>,
+    params: Params,
+}
 
-        if_chain! {
-            if !self.has_self && self.is_method;
-            if let ExprKind::Path(QPath::TypeRelative(ty, segment)) = callee.kind;
-            if segment.ident == self.fn_ident;
-            if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
-            if let Res::SelfTy{ .. } = path.res;
-            then {
-                is_recursive = true;
-            }
+impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
+    fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'tcx>) {
+        if body.value.span.from_expansion() {
+            return;
         }
-
-        if is_recursive {
-            izip!(self.params.clone(), args).for_each(|(pat, expr)| {
-                self.visit_pat_expr(pat, expr, true);
-                self.ret_vars.clear();
-            });
-        } else {
-            // This would set arguments used in closure that does not have side-effect.
-            // Closure itself can be detected whether there is a side-effect, but the
-            // value of variable that is holding closure can change.
-            // So, we just check the variables.
-            self.ret_vars = args
-                .iter()
-                .flat_map(|expr| {
-                    self.visit_expr(expr);
-                    std::mem::take(&mut self.ret_vars)
-                })
-                .collect_vec()
-                .into_iter()
-                .map(|id| {
-                    self.has_side_effect.insert(id.0);
-                    id
-                })
-                .collect();
-            self.contains_side_effect = true;
+        // `skip_params` is either `0` or `1` to skip the `self` parameter in trait functions.
+        // It can't be renamed, and it can't be removed without removing it from multiple functions.
+        let (fn_id, fn_kind, skip_params) = match get_parent_node(cx.tcx, body.value.hir_id) {
+            Some(Node::Item(i)) => (i.def_id.to_def_id(), FnKind::Fn, 0),
+            Some(Node::TraitItem(&TraitItem {
+                kind: TraitItemKind::Fn(ref sig, _),
+                def_id,
+                ..
+            })) => (
+                def_id.to_def_id(),
+                FnKind::TraitFn,
+                if sig.decl.implicit_self.has_implicit_self() {
+                    1
+                } else {
+                    0
+                },
+            ),
+            Some(Node::ImplItem(&ImplItem {
+                kind: ImplItemKind::Fn(ref sig, _),
+                def_id,
+                ..
+            })) => {
+                #[allow(trivial_casts)]
+                if let Some(Node::Item(item)) = get_parent_node(cx.tcx, cx.tcx.hir().local_def_id_to_hir_id(def_id))
+                    && let Some(trait_ref) = cx.tcx.impl_trait_ref(item.def_id)
+                    && let Some(trait_item_id) = cx.tcx.associated_item(def_id).trait_item_def_id
+                {
+                    (
+                        trait_item_id,
+                        FnKind::ImplTraitFn(cx.tcx.erase_regions(trait_ref.substs) as *const _ as usize),
+                        if sig.decl.implicit_self.has_implicit_self() {
+                            1
+                        } else {
+                            0
+                        },
+                    )
+                } else {
+                    (def_id.to_def_id(), FnKind::Fn, 0)
+                }
+            },
+            _ => return,
+        };
+        body.params
+            .iter()
+            .enumerate()
+            .skip(skip_params)
+            .filter_map(|(idx, p)| match p.pat.kind {
+                PatKind::Binding(_, id, ident, None) if !ident.as_str().starts_with('_') => {
+                    Some((id, Param::new(fn_id, fn_kind, idx, ident)))
+                },
+                _ => None,
+            })
+            .for_each(|(id, param)| self.params.insert(param, id));
+        if self.entered_body.is_none() {
+            self.entered_body = Some(body.value.hir_id);
         }
-
-        self.ret_vars.append(&mut ret_vars);
     }
 
-    fn visit_method_call(&mut self, path: &'tcx PathSegment<'tcx>, args: &'tcx [Expr<'tcx>]) {
-        if_chain! {
-            if self.is_method;
-            if path.ident == self.fn_ident;
-            if let ExprKind::Path(QPath::Resolved(_, path)) = args.first().unwrap().kind;
-            if let Res::Local(..) = path.res;
-            let ident = path.segments.last().unwrap().ident;
-            if ident.name == kw::SelfLower;
-            then {
-                izip!(self.params.clone(), args.iter())
-                    .for_each(|(pat, expr)| {
-                        self.visit_pat_expr(pat, expr, true);
-                        self.ret_vars.clear();
-                    });
-            } else {
-                self.ret_vars = args
-                    .iter()
-                    .flat_map(|expr| {
-                        self.visit_expr(expr);
-                        std::mem::take(&mut self.ret_vars)
-                    })
-                    .collect_vec()
-                    .into_iter()
-                    .map(|a| {
-                        self.has_side_effect.insert(a.0);
-                        a
-                    })
-                    .collect();
-                self.contains_side_effect = true;
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) {
+        if let Some(id) = path_to_local(e)
+            && let Some(param) = self.params.get_by_id_mut(id)
+        {
+            let typeck = cx.typeck_results();
+            let span = e.span;
+            let mut e = e;
+            loop {
+                match get_expr_use_or_unification_node(cx.tcx, e) {
+                    None | Some((Node::Stmt(_), _)) => return,
+                    Some((Node::Expr(parent), child_id)) => match parent.kind {
+                        // Recursive call. Track which index the parameter is used in.
+                        ExprKind::Call(callee, args)
+                            if path_def_id(cx, callee).map_or(false, |id| {
+                                id == param.fn_id
+                                    && has_matching_substs(param.fn_kind, typeck.node_substs(callee.hir_id))
+                            }) =>
+                        {
+                            if let Some(idx) = args.iter().position(|arg| arg.hir_id == child_id) {
+                                param.uses.push(Usage::new(span, idx));
+                            }
+                            return;
+                        },
+                        ExprKind::MethodCall(_, args, _)
+                            if typeck.type_dependent_def_id(parent.hir_id).map_or(false, |id| {
+                                id == param.fn_id
+                                    && has_matching_substs(param.fn_kind, typeck.node_substs(parent.hir_id))
+                            }) =>
+                        {
+                            if let Some(idx) = args.iter().position(|arg| arg.hir_id == child_id) {
+                                param.uses.push(Usage::new(span, idx));
+                            }
+                            return;
+                        },
+                        // Assignment to a parameter is fine.
+                        ExprKind::Assign(lhs, _, _) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
+                            return;
+                        },
+                        // Parameter update e.g. `x = x + 1`
+                        ExprKind::Assign(lhs, rhs, _) | ExprKind::AssignOp(_, lhs, rhs)
+                            if rhs.hir_id == child_id && path_to_local_id(lhs, id) =>
+                        {
+                            return;
+                        },
+                        // Side-effect free expressions. Walk to the parent expression.
+                        ExprKind::Binary(_, lhs, rhs)
+                            if typeck.expr_ty(lhs).is_primitive() && typeck.expr_ty(rhs).is_primitive() =>
+                        {
+                            e = parent;
+                            continue;
+                        },
+                        ExprKind::Unary(_, arg) if typeck.expr_ty(arg).is_primitive() => {
+                            e = parent;
+                            continue;
+                        },
+                        ExprKind::AddrOf(..) | ExprKind::Cast(..) => {
+                            e = parent;
+                            continue;
+                        },
+                        // Only allow field accesses without auto-deref
+                        ExprKind::Field(..) if typeck.adjustments().get(child_id).is_none() => {
+                            e = parent;
+                            continue
+                        }
+                        _ => (),
+                    },
+                    _ => (),
+                }
+                self.params.remove_by_id(id);
+                return;
             }
         }
     }
 
-    fn visit_if(&mut self, bind: &'tcx Expr<'tcx>, then_expr: &'tcx Expr<'tcx>, else_expr: Option<&'tcx Expr<'tcx>>) {
-        let contains_side_effect = self.contains_side_effect;
-        self.contains_side_effect = false;
-        self.visit_expr(bind);
-        let mut vars = std::mem::take(&mut self.ret_vars);
-        self.visit_expr(then_expr);
-        let mut then_vars = std::mem::take(&mut self.ret_vars);
-        walk_list!(self, visit_expr, else_expr);
-        if self.contains_side_effect {
-            self.add_side_effect(vars.clone());
-        }
-        self.contains_side_effect |= contains_side_effect;
-        self.ret_vars.append(&mut vars);
-        self.ret_vars.append(&mut then_vars);
-    }
-
-    fn visit_match(&mut self, expr: &'tcx Expr<'tcx>, arms: &'tcx [Arm<'tcx>]) {
-        self.visit_expr(expr);
-        let mut expr_vars = std::mem::take(&mut self.ret_vars);
-        self.ret_vars = arms
-            .iter()
-            .flat_map(|arm| {
-                let contains_side_effect = self.contains_side_effect;
-                self.contains_side_effect = false;
-                // this would visit `expr` multiple times
-                // but couldn't think of a better way
-                self.visit_pat_expr(arm.pat, expr, false);
-                let mut vars = std::mem::take(&mut self.ret_vars);
-                let _ = arm.guard.as_ref().map(|guard| {
-                    self.visit_expr(match guard {
-                        Guard::If(expr) | Guard::IfLet(Let { init: expr, .. }) => expr,
-                    });
-                    vars.append(&mut self.ret_vars);
-                });
-                self.visit_expr(arm.body);
-                if self.contains_side_effect {
-                    self.add_side_effect(vars.clone());
-                    self.add_side_effect(expr_vars.clone());
+    fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'tcx>) {
+        if self.entered_body == Some(body.value.hir_id) {
+            self.entered_body = None;
+            self.params.flag_for_linting();
+            for param in &self.params.params {
+                if param.apply_lint.get() {
+                    span_lint_and_then(
+                        cx,
+                        ONLY_USED_IN_RECURSION,
+                        param.ident.span,
+                        "parameter is only used in recursion",
+                        |diag| {
+                            if param.ident.name != kw::SelfLower {
+                                diag.span_suggestion(
+                                    param.ident.span,
+                                    "if this is intentional, prefix it with an underscore",
+                                    format!("_{}", param.ident.name),
+                                    Applicability::MaybeIncorrect,
+                                );
+                            }
+                            diag.span_note(
+                                param.uses.iter().map(|x| x.span).collect::<Vec<_>>(),
+                                "parameter used here",
+                            );
+                        },
+                    );
                 }
-                self.contains_side_effect |= contains_side_effect;
-                vars.append(&mut self.ret_vars);
-                vars
-            })
-            .collect();
-        self.ret_vars.append(&mut expr_vars);
-    }
-
-    fn connect_assign(&mut self, lhs: &[(HirId, bool)], rhs: &[(HirId, bool)], connect_self: bool) {
-        // if mutable dereference is on assignment it can have side-effect
-        // (this can lead to parameter mutable dereference and change the original value)
-        // too hard to detect whether this value is from parameter, so this would all
-        // check mutable dereference assignment to side effect
-        lhs.iter().filter(|(_, b)| *b).for_each(|(id, _)| {
-            self.has_side_effect.insert(*id);
-            self.contains_side_effect = true;
-        });
-
-        // there is no connection
-        if lhs.is_empty() || rhs.is_empty() {
-            return;
-        }
-
-        // by connected rhs in cycle, the connections would decrease
-        // from `n * m` to `n + m`
-        // where `n` and `m` are length of `lhs` and `rhs`.
-
-        // unwrap is possible since rhs is not empty
-        let rhs_first = rhs.first().unwrap();
-        for (id, _) in lhs.iter() {
-            if connect_self || *id != rhs_first.0 {
-                self.graph
-                    .entry(*id)
-                    .or_insert_with(FxHashSet::default)
-                    .insert(rhs_first.0);
             }
+            self.params.clear();
         }
-
-        let rhs = rhs.iter();
-        izip!(rhs.clone().cycle().skip(1), rhs).for_each(|(from, to)| {
-            if connect_self || from.0 != to.0 {
-                self.graph.entry(from.0).or_insert_with(FxHashSet::default).insert(to.0);
-            }
-        });
     }
+}
 
-    fn add_side_effect(&mut self, v: Vec<(HirId, bool)>) {
-        for (id, _) in v {
-            self.has_side_effect.insert(id);
-            self.contains_side_effect = true;
-        }
+fn has_matching_substs(kind: FnKind, substs: SubstsRef<'_>) -> bool {
+    match kind {
+        FnKind::Fn => true,
+        FnKind::TraitFn => substs.iter().enumerate().all(|(idx, subst)| match subst.unpack() {
+            GenericArgKind::Lifetime(_) => true,
+            GenericArgKind::Type(ty) => matches!(*ty.kind(), ty::Param(ty) if ty.index as usize == idx),
+            GenericArgKind::Const(c) => matches!(c.kind(), ConstKind::Param(c) if c.index as usize == idx),
+        }),
+        #[allow(trivial_casts)]
+        FnKind::ImplTraitFn(expected_substs) => substs as *const _ as usize == expected_substs,
     }
 }
diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs
index 44f153cffac..9602d0d1d2e 100644
--- a/clippy_lints/src/option_if_let_else.rs
+++ b/clippy_lints/src/option_if_let_else.rs
@@ -1,21 +1,22 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::sugg::Sugg;
-use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{
     can_move_expr_to_closure, eager_or_lazy, higher, in_constant, is_else_clause, is_lang_ctor, peel_blocks,
     peel_hir_expr_while, CaptureKind,
 };
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir::LangItem::OptionSome;
-use rustc_hir::{def::Res, BindingAnnotation, Expr, ExprKind, Mutability, PatKind, Path, QPath, UnOp};
+use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
+use rustc_hir::{
+    def::Res, Arm, BindingAnnotation, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath, UnOp,
+};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Lints usage of `if let Some(v) = ... { y } else { x }` which is more
+    /// Lints usage of `if let Some(v) = ... { y } else { x }` and
+    /// `match .. { Some(v) => y, None/_ => x }` which are more
     /// idiomatically done with `Option::map_or` (if the else bit is a pure
     /// expression) or `Option::map_or_else` (if the else bit is an impure
     /// expression).
@@ -39,6 +40,10 @@ declare_clippy_lint! {
     /// } else {
     ///     5
     /// };
+    /// let _ = match optional {
+    ///     Some(val) => val + 1,
+    ///     None => 5
+    /// };
     /// let _ = if let Some(foo) = optional {
     ///     foo
     /// } else {
@@ -53,11 +58,14 @@ declare_clippy_lint! {
     /// # let optional: Option<u32> = Some(0);
     /// # fn do_complicated_function() -> u32 { 5 };
     /// let _ = optional.map_or(5, |foo| foo);
+    /// let _ = optional.map_or(5, |val| val + 1);
     /// let _ = optional.map_or_else(||{
     ///     let y = do_complicated_function();
     ///     y*y
     /// }, |foo| foo);
     /// ```
+    // FIXME: Before moving this lint out of nursery, the lint name needs to be updated. It now also
+    // covers matches and `Result`.
     #[clippy::version = "1.47.0"]
     pub OPTION_IF_LET_ELSE,
     nursery,
@@ -66,19 +74,21 @@ declare_clippy_lint! {
 
 declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]);
 
-/// Returns true iff the given expression is the result of calling `Result::ok`
-fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool {
-    if let ExprKind::MethodCall(path, &[ref receiver], _) = &expr.kind {
-        path.ident.name.as_str() == "ok"
-            && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::Result)
-    } else {
-        false
-    }
-}
-
-/// A struct containing information about occurrences of the
-/// `if let Some(..) = .. else` construct that this lint detects.
-struct OptionIfLetElseOccurrence {
+/// A struct containing information about occurrences of construct that this lint detects
+///
+/// Such as:
+///
+/// ```ignore
+/// if let Some(..) = {..} else {..}
+/// ```
+/// or
+/// ```ignore
+/// match x {
+///     Some(..) => {..},
+///     None/_ => {..}
+/// }
+/// ```
+struct OptionOccurence {
     option: String,
     method_sugg: String,
     some_expr: String,
@@ -99,43 +109,38 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo
     )
 }
 
-/// If this expression is the option if let/else construct we're detecting, then
-/// this function returns an `OptionIfLetElseOccurrence` struct with details if
-/// this construct is found, or None if this construct is not found.
-fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionIfLetElseOccurrence> {
+fn try_get_option_occurence<'tcx>(
+    cx: &LateContext<'tcx>,
+    pat: &Pat<'tcx>,
+    expr: &Expr<'_>,
+    if_then: &'tcx Expr<'_>,
+    if_else: &'tcx Expr<'_>,
+) -> Option<OptionOccurence> {
+    let cond_expr = match expr.kind {
+        ExprKind::Unary(UnOp::Deref, inner_expr) | ExprKind::AddrOf(_, _, inner_expr) => inner_expr,
+        _ => expr,
+    };
+    let inner_pat = try_get_inner_pat(cx, pat)?;
     if_chain! {
-        if !expr.span.from_expansion(); // Don't lint macros, because it behaves weirdly
-        if !in_constant(cx, expr.hir_id);
-        if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) })
-            = higher::IfLet::hir(cx, expr);
-        if !is_else_clause(cx.tcx, expr);
-        if !is_result_ok(cx, let_expr); // Don't lint on Result::ok because a different lint does it already
-        if let PatKind::TupleStruct(struct_qpath, [inner_pat], _) = &let_pat.kind;
-        if is_lang_ctor(cx, struct_qpath, OptionSome);
-        if let PatKind::Binding(bind_annotation, _, id, None) = &inner_pat.kind;
+        if let PatKind::Binding(bind_annotation, _, id, None) = inner_pat.kind;
         if let Some(some_captures) = can_move_expr_to_closure(cx, if_then);
         if let Some(none_captures) = can_move_expr_to_closure(cx, if_else);
         if some_captures
             .iter()
             .filter_map(|(id, &c)| none_captures.get(id).map(|&c2| (c, c2)))
             .all(|(x, y)| x.is_imm_ref() && y.is_imm_ref());
-
         then {
-            let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" };
+            let capture_mut = if bind_annotation == BindingAnnotation::Mutable { "mut " } else { "" };
             let some_body = peel_blocks(if_then);
             let none_body = peel_blocks(if_else);
             let method_sugg = if eager_or_lazy::switch_to_eager_eval(cx, none_body) { "map_or" } else { "map_or_else" };
             let capture_name = id.name.to_ident_string();
-            let (as_ref, as_mut) = match &let_expr.kind {
+            let (as_ref, as_mut) = match &expr.kind {
                 ExprKind::AddrOf(_, Mutability::Not, _) => (true, false),
                 ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true),
-                _ => (bind_annotation == &BindingAnnotation::Ref, bind_annotation == &BindingAnnotation::RefMut),
-            };
-            let cond_expr = match let_expr.kind {
-                // Pointer dereferencing happens automatically, so we can omit it in the suggestion
-                ExprKind::Unary(UnOp::Deref, expr) | ExprKind::AddrOf(_, _, expr) => expr,
-                _ => let_expr,
+                _ => (bind_annotation == BindingAnnotation::Ref, bind_annotation == BindingAnnotation::RefMut),
             };
+
             // Check if captures the closure will need conflict with borrows made in the scrutinee.
             // TODO: check all the references made in the scrutinee expression. This will require interacting
             // with the borrow checker. Currently only `<local>[.<field>]*` is checked for.
@@ -154,30 +159,100 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) ->
                     }
                 }
             }
-            Some(OptionIfLetElseOccurrence {
+
+            return Some(OptionOccurence {
                 option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut),
                 method_sugg: method_sugg.to_string(),
                 some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir_with_macro_callsite(cx, some_body, "..")),
                 none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir_with_macro_callsite(cx, none_body, "..")),
-            })
+            });
+        }
+    }
+
+    None
+}
+
+fn try_get_inner_pat<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<&'tcx Pat<'tcx>> {
+    if let PatKind::TupleStruct(ref qpath, [inner_pat], ..) = pat.kind {
+        if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk) {
+            return Some(inner_pat);
+        }
+    }
+    None
+}
+
+/// If this expression is the option if let/else construct we're detecting, then
+/// this function returns an `OptionOccurence` struct with details if
+/// this construct is found, or None if this construct is not found.
+fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionOccurence> {
+    if let Some(higher::IfLet {
+        let_pat,
+        let_expr,
+        if_then,
+        if_else: Some(if_else),
+    }) = higher::IfLet::hir(cx, expr)
+    {
+        if !is_else_clause(cx.tcx, expr) {
+            return try_get_option_occurence(cx, let_pat, let_expr, if_then, if_else);
+        }
+    }
+    None
+}
+
+fn detect_option_match<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionOccurence> {
+    if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind {
+        if let Some((let_pat, if_then, if_else)) = try_convert_match(cx, arms) {
+            return try_get_option_occurence(cx, let_pat, ex, if_then, if_else);
+        }
+    }
+    None
+}
+
+fn try_convert_match<'tcx>(
+    cx: &LateContext<'tcx>,
+    arms: &[Arm<'tcx>],
+) -> Option<(&'tcx Pat<'tcx>, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
+    if arms.len() == 2 {
+        return if is_none_or_err_arm(cx, &arms[1]) {
+            Some((arms[0].pat, arms[0].body, arms[1].body))
+        } else if is_none_or_err_arm(cx, &arms[0]) {
+            Some((arms[1].pat, arms[1].body, arms[0].body))
         } else {
             None
-        }
+        };
+    }
+    None
+}
+
+fn is_none_or_err_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
+    match arm.pat.kind {
+        PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
+        PatKind::TupleStruct(ref qpath, [first_pat], _) => {
+            is_lang_ctor(cx, qpath, ResultErr) && matches!(first_pat.kind, PatKind::Wild)
+        },
+        PatKind::Wild => true,
+        _ => false,
     }
 }
 
 impl<'tcx> LateLintPass<'tcx> for OptionIfLetElse {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
-        if let Some(detection) = detect_option_if_let_else(cx, expr) {
+        // Don't lint macros and constants
+        if expr.span.from_expansion() || in_constant(cx, expr.hir_id) {
+            return;
+        }
+
+        let detection = detect_option_if_let_else(cx, expr).or_else(|| detect_option_match(cx, expr));
+        if let Some(det) = detection {
             span_lint_and_sugg(
                 cx,
                 OPTION_IF_LET_ELSE,
                 expr.span,
-                format!("use Option::{} instead of an if let/else", detection.method_sugg).as_str(),
+                format!("use Option::{} instead of an if let/else", det.method_sugg).as_str(),
                 "try",
                 format!(
                     "{}.{}({}, {})",
-                    detection.option, detection.method_sugg, detection.none_expr, detection.some_expr,
+                    det.option, det.method_sugg, det.none_expr, det.some_expr
                 ),
                 Applicability::MaybeIncorrect,
             );
diff --git a/clippy_lints/src/partialeq_to_none.rs b/clippy_lints/src/partialeq_to_none.rs
index eee7642068d..000b0ba7a14 100644
--- a/clippy_lints/src/partialeq_to_none.rs
+++ b/clippy_lints/src/partialeq_to_none.rs
@@ -53,7 +53,8 @@ impl<'tcx> LateLintPass<'tcx> for PartialeqToNone {
 
         // If the expression is a literal `Option::None`
         let is_none_ctor = |expr: &Expr<'_>| {
-            matches!(&peel_hir_expr_refs(expr).0.kind,
+            !expr.span.from_expansion()
+                && matches!(&peel_hir_expr_refs(expr).0.kind,
             ExprKind::Path(p) if is_lang_ctor(cx, p, LangItem::OptionNone))
         };
 
diff --git a/clippy_lints/src/path_buf_push_overwrite.rs b/clippy_lints/src/path_buf_push_overwrite.rs
deleted file mode 100644
index bc6a918f703..00000000000
--- a/clippy_lints/src/path_buf_push_overwrite.rs
+++ /dev/null
@@ -1,72 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::ty::is_type_diagnostic_item;
-use if_chain::if_chain;
-use rustc_ast::ast::LitKind;
-use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::sym;
-use std::path::{Component, Path};
-
-declare_clippy_lint! {
-    /// ### What it does
-    ///* Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push)
-    /// calls on `PathBuf` that can cause overwrites.
-    ///
-    /// ### Why is this bad?
-    /// Calling `push` with a root path at the start can overwrite the
-    /// previous defined path.
-    ///
-    /// ### Example
-    /// ```rust
-    /// use std::path::PathBuf;
-    ///
-    /// let mut x = PathBuf::from("/foo");
-    /// x.push("/bar");
-    /// assert_eq!(x, PathBuf::from("/bar"));
-    /// ```
-    /// Could be written:
-    ///
-    /// ```rust
-    /// use std::path::PathBuf;
-    ///
-    /// let mut x = PathBuf::from("/foo");
-    /// x.push("bar");
-    /// assert_eq!(x, PathBuf::from("/foo/bar"));
-    /// ```
-    #[clippy::version = "1.36.0"]
-    pub PATH_BUF_PUSH_OVERWRITE,
-    nursery,
-    "calling `push` with file system root on `PathBuf` can overwrite it"
-}
-
-declare_lint_pass!(PathBufPushOverwrite => [PATH_BUF_PUSH_OVERWRITE]);
-
-impl<'tcx> LateLintPass<'tcx> for PathBufPushOverwrite {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if_chain! {
-            if let ExprKind::MethodCall(path, [recv, get_index_arg], _) = expr.kind;
-            if path.ident.name == sym!(push);
-            if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv).peel_refs(), sym::PathBuf);
-            if let ExprKind::Lit(ref lit) = get_index_arg.kind;
-            if let LitKind::Str(ref path_lit, _) = lit.node;
-            if let pushed_path = Path::new(path_lit.as_str());
-            if let Some(pushed_path_lit) = pushed_path.to_str();
-            if pushed_path.has_root();
-            if let Some(root) = pushed_path.components().next();
-            if root == Component::RootDir;
-            then {
-                span_lint_and_sugg(
-                    cx,
-                    PATH_BUF_PUSH_OVERWRITE,
-                    lit.span,
-                    "calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition",
-                    "try",
-                    format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')),
-                    Applicability::MachineApplicable,
-                );
-            }
-        }
-    }
-}
diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs
index 964a057f00d..b432ccb1ee3 100644
--- a/clippy_lints/src/question_mark.rs
+++ b/clippy_lints/src/question_mark.rs
@@ -123,7 +123,7 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr:
         if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else }) = higher::IfLet::hir(cx, expr);
         if !is_else_clause(cx.tcx, expr);
         if let PatKind::TupleStruct(ref path1, [field], None) = let_pat.kind;
-        if let PatKind::Binding(annot, bind_id, ident, _) = field.kind;
+        if let PatKind::Binding(annot, bind_id, ident, None) = field.kind;
         let caller_ty = cx.typeck_results().expr_ty(let_expr);
         let if_block = IfBlockType::IfLet(path1, caller_ty, ident.name, let_expr, if_then, if_else);
         if (is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id))
diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs
index fbf842c339e..490f345d297 100644
--- a/clippy_lints/src/ranges.rs
+++ b/clippy_lints/src/ranges.rs
@@ -1,48 +1,22 @@
 use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::higher;
 use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
 use clippy_utils::sugg::Sugg;
 use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, path_to_local};
-use clippy_utils::{higher, SpanlessEq};
 use if_chain::if_chain;
 use rustc_ast::ast::RangeLimits;
 use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Expr, ExprKind, HirId, PathSegment, QPath};
+use rustc_hir::{BinOpKind, Expr, ExprKind, HirId};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::{Span, Spanned};
-use rustc_span::sym;
 use std::cmp::Ordering;
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for zipping a collection with the range of
-    /// `0.._.len()`.
-    ///
-    /// ### Why is this bad?
-    /// The code is better expressed with `.enumerate()`.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let x = vec![1];
-    /// let _ = x.iter().zip(0..x.len());
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust
-    /// # let x = vec![1];
-    /// let _ = x.iter().enumerate();
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub RANGE_ZIP_WITH_LEN,
-    complexity,
-    "zipping iterator with a range when `enumerate()` would do"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
     /// Checks for exclusive ranges where 1 is added to the
     /// upper bound, e.g., `x..(y+1)`.
     ///
@@ -198,7 +172,6 @@ impl Ranges {
 }
 
 impl_lint_pass!(Ranges => [
-    RANGE_ZIP_WITH_LEN,
     RANGE_PLUS_ONE,
     RANGE_MINUS_ONE,
     REVERSED_EMPTY_RANGES,
@@ -207,16 +180,10 @@ impl_lint_pass!(Ranges => [
 
 impl<'tcx> LateLintPass<'tcx> for Ranges {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        match expr.kind {
-            ExprKind::MethodCall(path, args, _) => {
-                check_range_zip_with_len(cx, path, args, expr.span);
-            },
-            ExprKind::Binary(ref op, l, r) => {
-                if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) {
-                    check_possible_range_contains(cx, op.node, l, r, expr, expr.span);
-                }
-            },
-            _ => {},
+        if let ExprKind::Binary(ref op, l, r) = expr.kind {
+            if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) {
+                check_possible_range_contains(cx, op.node, l, r, expr, expr.span);
+            }
         }
 
         check_exclusive_range_plus_one(cx, expr);
@@ -380,34 +347,6 @@ fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option<R
     None
 }
 
-fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: &[Expr<'_>], span: Span) {
-    if_chain! {
-        if path.ident.as_str() == "zip";
-        if let [iter, zip_arg] = args;
-        // `.iter()` call
-        if let ExprKind::MethodCall(iter_path, [iter_caller, ..], _) = iter.kind;
-        if iter_path.ident.name == sym::iter;
-        // range expression in `.zip()` call: `0..x.len()`
-        if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg);
-        if is_integer_const(cx, start, 0);
-        // `.len()` call
-        if let ExprKind::MethodCall(len_path, [len_caller], _) = end.kind;
-        if len_path.ident.name == sym::len;
-        // `.iter()` and `.len()` called on same `Path`
-        if let ExprKind::Path(QPath::Resolved(_, iter_path)) = iter_caller.kind;
-        if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_caller.kind;
-        if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments);
-        then {
-            span_lint(cx,
-                RANGE_ZIP_WITH_LEN,
-                span,
-                &format!("it is more idiomatic to use `{}.iter().enumerate()`",
-                    snippet(cx, iter_caller.span, "_"))
-            );
-        }
-    }
-}
-
 // exclusive range plus one: `x..(y+1)`
 fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
     if_chain! {
diff --git a/clippy_lints/src/rc_clone_in_vec_init.rs b/clippy_lints/src/rc_clone_in_vec_init.rs
index 8db8c4e9b78..e82aa3a7b98 100644
--- a/clippy_lints/src/rc_clone_in_vec_init.rs
+++ b/clippy_lints/src/rc_clone_in_vec_init.rs
@@ -41,7 +41,7 @@ declare_clippy_lint! {
     /// let data = std::rc::Rc::new("some data".to_string());
     /// let v = vec![data; 100];
     /// ```
-    #[clippy::version = "1.62.0"]
+    #[clippy::version = "1.63.0"]
     pub RC_CLONE_IN_VEC_INIT,
     suspicious,
     "initializing reference-counted pointer in `vec![elem; len]`"
diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs
index db6c97f3739..8693ca9af83 100644
--- a/clippy_lints/src/redundant_slicing.rs
+++ b/clippy_lints/src/redundant_slicing.rs
@@ -11,6 +11,8 @@ use rustc_middle::ty::adjustment::{Adjust, AutoBorrow, AutoBorrowMutability};
 use rustc_middle::ty::subst::GenericArg;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
+use std::iter;
+
 declare_clippy_lint! {
     /// ### What it does
     /// Checks for redundant slicing expressions which use the full range, and
@@ -134,7 +136,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing {
                 } else if let Some(target_id) = cx.tcx.lang_items().deref_target() {
                     if let Ok(deref_ty) = cx.tcx.try_normalize_erasing_regions(
                         cx.param_env,
-                        cx.tcx.mk_projection(target_id, cx.tcx.mk_substs([GenericArg::from(indexed_ty)].into_iter())),
+                        cx.tcx.mk_projection(target_id, cx.tcx.mk_substs(iter::once(GenericArg::from(indexed_ty)))),
                     ) {
                         if deref_ty == expr_ty {
                             let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0;
diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs
index f8801f769e8..2d751c27467 100644
--- a/clippy_lints/src/redundant_static_lifetimes.rs
+++ b/clippy_lints/src/redundant_static_lifetimes.rs
@@ -48,15 +48,15 @@ impl_lint_pass!(RedundantStaticLifetimes => [REDUNDANT_STATIC_LIFETIMES]);
 
 impl RedundantStaticLifetimes {
     // Recursively visit types
-    fn visit_type(&mut self, ty: &Ty, cx: &EarlyContext<'_>, reason: &str) {
+    fn visit_type(ty: &Ty, cx: &EarlyContext<'_>, reason: &str) {
         match ty.kind {
             // Be careful of nested structures (arrays and tuples)
             TyKind::Array(ref ty, _) | TyKind::Slice(ref ty) => {
-                self.visit_type(ty, cx, reason);
+                Self::visit_type(ty, cx, reason);
             },
             TyKind::Tup(ref tup) => {
                 for tup_ty in tup {
-                    self.visit_type(tup_ty, cx, reason);
+                    Self::visit_type(tup_ty, cx, reason);
                 }
             },
             // This is what we are looking for !
@@ -87,7 +87,7 @@ impl RedundantStaticLifetimes {
                         _ => {},
                     }
                 }
-                self.visit_type(&borrow_type.ty, cx, reason);
+                Self::visit_type(&borrow_type.ty, cx, reason);
             },
             _ => {},
         }
@@ -102,13 +102,13 @@ impl EarlyLintPass for RedundantStaticLifetimes {
 
         if !item.span.from_expansion() {
             if let ItemKind::Const(_, ref var_type, _) = item.kind {
-                self.visit_type(var_type, cx, "constants have by default a `'static` lifetime");
+                Self::visit_type(var_type, cx, "constants have by default a `'static` lifetime");
                 // Don't check associated consts because `'static` cannot be elided on those (issue
                 // #2438)
             }
 
             if let ItemKind::Static(ref var_type, _, _) = item.kind {
-                self.visit_type(var_type, cx, "statics have by default a `'static` lifetime");
+                Self::visit_type(var_type, cx, "statics have by default a `'static` lifetime");
             }
         }
     }
diff --git a/clippy_lints/src/repeat_once.rs b/clippy_lints/src/repeat_once.rs
deleted file mode 100644
index 898c70ace66..00000000000
--- a/clippy_lints/src/repeat_once.rs
+++ /dev/null
@@ -1,89 +0,0 @@
-use clippy_utils::consts::{constant_context, Constant};
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet;
-use clippy_utils::ty::is_type_diagnostic_item;
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for usage of `.repeat(1)` and suggest the following method for each types.
-    /// - `.to_string()` for `str`
-    /// - `.clone()` for `String`
-    /// - `.to_vec()` for `slice`
-    ///
-    /// The lint will evaluate constant expressions and values as arguments of `.repeat(..)` and emit a message if
-    /// they are equivalent to `1`. (Related discussion in [rust-clippy#7306](https://github.com/rust-lang/rust-clippy/issues/7306))
-    ///
-    /// ### Why is this bad?
-    /// For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning
-    /// the string is the intention behind this, `clone()` should be used.
-    ///
-    /// ### Example
-    /// ```rust
-    /// fn main() {
-    ///     let x = String::from("hello world").repeat(1);
-    /// }
-    /// ```
-    /// Use instead:
-    /// ```rust
-    /// fn main() {
-    ///     let x = String::from("hello world").clone();
-    /// }
-    /// ```
-    #[clippy::version = "1.47.0"]
-    pub REPEAT_ONCE,
-    complexity,
-    "using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` "
-}
-
-declare_lint_pass!(RepeatOnce => [REPEAT_ONCE]);
-
-impl<'tcx> LateLintPass<'tcx> for RepeatOnce {
-    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
-        if_chain! {
-            if let ExprKind::MethodCall(path, [receiver, count], _) = &expr.kind;
-            if path.ident.name == sym!(repeat);
-            if constant_context(cx, cx.typeck_results()).expr(count) == Some(Constant::Int(1));
-            if !receiver.span.from_expansion();
-            then {
-                let ty = cx.typeck_results().expr_ty(receiver).peel_refs();
-                if ty.is_str() {
-                    span_lint_and_sugg(
-                        cx,
-                        REPEAT_ONCE,
-                        expr.span,
-                        "calling `repeat(1)` on str",
-                        "consider using `.to_string()` instead",
-                        format!("{}.to_string()", snippet(cx, receiver.span, r#""...""#)),
-                        Applicability::MachineApplicable,
-                    );
-                } else if ty.builtin_index().is_some() {
-                    span_lint_and_sugg(
-                        cx,
-                        REPEAT_ONCE,
-                        expr.span,
-                        "calling `repeat(1)` on slice",
-                        "consider using `.to_vec()` instead",
-                        format!("{}.to_vec()", snippet(cx, receiver.span, r#""...""#)),
-                        Applicability::MachineApplicable,
-                    );
-                } else if is_type_diagnostic_item(cx, ty, sym::String) {
-                    span_lint_and_sugg(
-                        cx,
-                        REPEAT_ONCE,
-                        expr.span,
-                        "calling `repeat(1)` on a string literal",
-                        "consider using `.clone()` instead",
-                        format!("{}.clone()", snippet(cx, receiver.span, r#""...""#)),
-                        Applicability::MachineApplicable,
-                    );
-                }
-            }
-        }
-    }
-}
diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs
index 1d9a2abf706..1926661c596 100644
--- a/clippy_lints/src/returns.rs
+++ b/clippy_lints/src/returns.rs
@@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::source::{snippet_opt, snippet_with_context};
 use clippy_utils::{fn_def_id, path_to_local_id};
 use if_chain::if_chain;
-use rustc_ast::ast::Attribute;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
 use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, PatKind, StmtKind};
@@ -11,7 +10,6 @@ use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::subst::GenericArgKind;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Span;
-use rustc_span::sym;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -152,10 +150,6 @@ impl<'tcx> LateLintPass<'tcx> for Return {
     }
 }
 
-fn attr_is_cfg(attr: &Attribute) -> bool {
-    attr.meta_item_list().is_some() && attr.has_name(sym::cfg)
-}
-
 fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) {
     if let Some(expr) = block.expr {
         check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty);
@@ -178,9 +172,7 @@ fn check_final_expr<'tcx>(
     match expr.kind {
         // simple return is always "bad"
         ExprKind::Ret(ref inner) => {
-            // allow `#[cfg(a)] return a; #[cfg(b)] return b;`
-            let attrs = cx.tcx.hir().attrs(expr.hir_id);
-            if !attrs.iter().any(attr_is_cfg) {
+            if cx.tcx.hir().attrs(expr.hir_id).is_empty() {
                 let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
                 if !borrows {
                     emit_return_lint(
diff --git a/clippy_lints/src/self_named_constructors.rs b/clippy_lints/src/self_named_constructors.rs
index d07c26d7c89..9cea4d88067 100644
--- a/clippy_lints/src/self_named_constructors.rs
+++ b/clippy_lints/src/self_named_constructors.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::return_ty;
-use clippy_utils::ty::{contains_adt_constructor, contains_ty};
+use clippy_utils::ty::contains_adt_constructor;
 use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind, Node};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for SelfNamedConstructors {
             if !contains_adt_constructor(ret_ty, self_adt) {
                 return;
             }
-        } else if !contains_ty(ret_ty, self_ty) {
+        } else if !ret_ty.contains(self_ty) {
             return;
         }
 
diff --git a/clippy_lints/src/stable_sort_primitive.rs b/clippy_lints/src/stable_sort_primitive.rs
deleted file mode 100644
index 6d54935f81a..00000000000
--- a/clippy_lints/src/stable_sort_primitive.rs
+++ /dev/null
@@ -1,144 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::{is_slice_of_primitives, sugg::Sugg};
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// When sorting primitive values (integers, bools, chars, as well
-    /// as arrays, slices, and tuples of such items), it is typically better to
-    /// use an unstable sort than a stable sort.
-    ///
-    /// ### Why is this bad?
-    /// Typically, using a stable sort consumes more memory and cpu cycles.
-    /// Because values which compare equal are identical, preserving their
-    /// relative order (the guarantee that a stable sort provides) means
-    /// nothing, while the extra costs still apply.
-    ///
-    /// ### Known problems
-    ///
-    /// As pointed out in
-    /// [issue #8241](https://github.com/rust-lang/rust-clippy/issues/8241),
-    /// a stable sort can instead be significantly faster for certain scenarios
-    /// (eg. when a sorted vector is extended with new data and resorted).
-    ///
-    /// For more information and benchmarking results, please refer to the
-    /// issue linked above.
-    ///
-    /// ### Example
-    /// ```rust
-    /// let mut vec = vec![2, 1, 3];
-    /// vec.sort();
-    /// ```
-    /// Use instead:
-    /// ```rust
-    /// let mut vec = vec![2, 1, 3];
-    /// vec.sort_unstable();
-    /// ```
-    #[clippy::version = "1.47.0"]
-    pub STABLE_SORT_PRIMITIVE,
-    pedantic,
-    "use of sort() when sort_unstable() is equivalent"
-}
-
-declare_lint_pass!(StableSortPrimitive => [STABLE_SORT_PRIMITIVE]);
-
-/// The three "kinds" of sorts
-enum SortingKind {
-    Vanilla,
-    /* The other kinds of lint are currently commented out because they
-     * can map distinct values to equal ones. If the key function is
-     * provably one-to-one, or if the Cmp function conserves equality,
-     * then they could be linted on, but I don't know if we can check
-     * for that. */
-
-    /* ByKey,
-     * ByCmp, */
-}
-impl SortingKind {
-    /// The name of the stable version of this kind of sort
-    fn stable_name(&self) -> &str {
-        match self {
-            SortingKind::Vanilla => "sort",
-            /* SortingKind::ByKey => "sort_by_key",
-             * SortingKind::ByCmp => "sort_by", */
-        }
-    }
-    /// The name of the unstable version of this kind of sort
-    fn unstable_name(&self) -> &str {
-        match self {
-            SortingKind::Vanilla => "sort_unstable",
-            /* SortingKind::ByKey => "sort_unstable_by_key",
-             * SortingKind::ByCmp => "sort_unstable_by", */
-        }
-    }
-    /// Takes the name of a function call and returns the kind of sort
-    /// that corresponds to that function name (or None if it isn't)
-    fn from_stable_name(name: &str) -> Option<SortingKind> {
-        match name {
-            "sort" => Some(SortingKind::Vanilla),
-            // "sort_by" => Some(SortingKind::ByCmp),
-            // "sort_by_key" => Some(SortingKind::ByKey),
-            _ => None,
-        }
-    }
-}
-
-/// A detected instance of this lint
-struct LintDetection {
-    slice_name: String,
-    method: SortingKind,
-    method_args: String,
-    slice_type: String,
-}
-
-fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintDetection> {
-    if_chain! {
-        if let ExprKind::MethodCall(method_name, [slice, args @ ..], _) = &expr.kind;
-        if let Some(method) = SortingKind::from_stable_name(method_name.ident.name.as_str());
-        if let Some(slice_type) = is_slice_of_primitives(cx, slice);
-        then {
-            let args_str = args.iter().map(|arg| Sugg::hir(cx, arg, "..").to_string()).collect::<Vec<String>>().join(", ");
-            Some(LintDetection { slice_name: Sugg::hir(cx, slice, "..").to_string(), method, method_args: args_str, slice_type })
-        } else {
-            None
-        }
-    }
-}
-
-impl LateLintPass<'_> for StableSortPrimitive {
-    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
-        if let Some(detection) = detect_stable_sort_primitive(cx, expr) {
-            span_lint_and_then(
-                cx,
-                STABLE_SORT_PRIMITIVE,
-                expr.span,
-                format!(
-                    "used `{}` on primitive type `{}`",
-                    detection.method.stable_name(),
-                    detection.slice_type,
-                )
-                .as_str(),
-                |diag| {
-                    diag.span_suggestion(
-                        expr.span,
-                        "try",
-                        format!(
-                            "{}.{}({})",
-                            detection.slice_name,
-                            detection.method.unstable_name(),
-                            detection.method_args,
-                        ),
-                        Applicability::MachineApplicable,
-                    );
-                    diag.note(
-                        "an unstable sort typically performs faster without any observable difference for this data type",
-                    );
-                },
-            );
-        }
-    }
-}
diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs
index 0a42a31fb8c..2ffa022b04f 100644
--- a/clippy_lints/src/trait_bounds.rs
+++ b/clippy_lints/src/trait_bounds.rs
@@ -4,7 +4,7 @@ use clippy_utils::{SpanlessEq, SpanlessHash};
 use core::hash::{Hash, Hasher};
 use if_chain::if_chain;
 use itertools::Itertools;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::unhash::UnhashMap;
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
@@ -15,6 +15,7 @@ use rustc_hir::{
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{BytePos, Span};
+use std::collections::hash_map::Entry;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -103,7 +104,6 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
     fn check_generics(&mut self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
         self.check_type_repetition(cx, gen);
         check_trait_bound_duplication(cx, gen);
-        check_bounds_or_where_duplication(cx, gen);
     }
 
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
@@ -234,35 +234,61 @@ impl TraitBounds {
 }
 
 fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
-    if gen.span.from_expansion() || gen.params.is_empty() || gen.predicates.is_empty() {
+    if gen.span.from_expansion() {
         return;
     }
 
-    let mut map = FxHashMap::<_, Vec<_>>::default();
-    for predicate in gen.predicates {
+    // Explanation:
+    // fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
+    // where T: Clone + Default, { unimplemented!(); }
+    //       ^^^^^^^^^^^^^^^^^^
+    //       |
+    // collects each of these where clauses into a set keyed by generic name and comparable trait
+    // eg. (T, Clone)
+    let where_predicates = gen
+        .predicates
+        .iter()
+        .filter_map(|pred| {
+            if_chain! {
+                if pred.in_where_clause();
+                if let WherePredicate::BoundPredicate(bound_predicate) = pred;
+                if let TyKind::Path(QPath::Resolved(_, path)) =  bound_predicate.bounded_ty.kind;
+                then {
+                    return Some(
+                        rollup_traits(cx, bound_predicate.bounds, "these where clauses contain repeated elements")
+                        .into_iter().map(|(trait_ref, _)| (path.res, trait_ref)))
+                }
+            }
+            None
+        })
+        .flatten()
+        .collect::<FxHashSet<_>>();
+
+    // Explanation:
+    // fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z) ...
+    //            ^^^^^^^^^^^^^^^^^^  ^^^^^^^
+    //            |
+    // compare trait bounds keyed by generic name and comparable trait to collected where
+    // predicates eg. (T, Clone)
+    for predicate in gen.predicates.iter().filter(|pred| !pred.in_where_clause()) {
         if_chain! {
-            if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
+            if let WherePredicate::BoundPredicate(bound_predicate) = predicate;
             if bound_predicate.origin != PredicateOrigin::ImplTrait;
             if !bound_predicate.span.from_expansion();
-            if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind;
-            if let Some(segment) = segments.first();
+            if let TyKind::Path(QPath::Resolved(_, path)) =  bound_predicate.bounded_ty.kind;
             then {
-                for (res_where, _, span_where) in bound_predicate.bounds.iter().filter_map(get_trait_info_from_bound) {
-                    let trait_resolutions_direct = map.entry(segment.ident).or_default();
-                    if let Some((_, span_direct)) = trait_resolutions_direct
-                                                .iter()
-                                                .find(|(res_direct, _)| *res_direct == res_where) {
+                let traits = rollup_traits(cx, bound_predicate.bounds, "these bounds contain repeated elements");
+                for (trait_ref, span) in traits {
+                    let key = (path.res, trait_ref);
+                    if where_predicates.contains(&key) {
                         span_lint_and_help(
                             cx,
                             TRAIT_DUPLICATION_IN_BOUNDS,
-                            *span_direct,
+                            span,
                             "this trait bound is already specified in the where clause",
                             None,
                             "consider removing this trait bound",
-                        );
-                    }
-                    else {
-                        trait_resolutions_direct.push((res_where, span_where));
+                            );
                     }
                 }
             }
@@ -270,23 +296,11 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
     }
 }
 
-#[derive(PartialEq, Eq, Hash, Debug)]
+#[derive(Clone, PartialEq, Eq, Hash, Debug)]
 struct ComparableTraitRef(Res, Vec<Res>);
-
-fn check_bounds_or_where_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
-    if gen.span.from_expansion() {
-        return;
-    }
-
-    for predicate in gen.predicates {
-        if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate {
-            let msg = if predicate.in_where_clause() {
-                "these where clauses contain repeated elements"
-            } else {
-                "these bounds contain repeated elements"
-            };
-            rollup_traits(cx, bound_predicate.bounds, msg);
-        }
+impl Default for ComparableTraitRef {
+    fn default() -> Self {
+        Self(Res::Err, Vec::new())
     }
 }
 
@@ -331,7 +345,7 @@ fn into_comparable_trait_ref(trait_ref: &TraitRef<'_>) -> ComparableTraitRef {
     )
 }
 
-fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) {
+fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) -> Vec<(ComparableTraitRef, Span)> {
     let mut map = FxHashMap::default();
     let mut repeated_res = false;
 
@@ -343,23 +357,33 @@ fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) {
         }
     };
 
+    let mut i = 0usize;
     for bound in bounds.iter().filter_map(only_comparable_trait_refs) {
         let (comparable_bound, span_direct) = bound;
-        if map.insert(comparable_bound, span_direct).is_some() {
-            repeated_res = true;
+        match map.entry(comparable_bound) {
+            Entry::Occupied(_) => repeated_res = true,
+            Entry::Vacant(e) => {
+                e.insert((span_direct, i));
+                i += 1;
+            },
         }
     }
 
+    // Put bounds in source order
+    let mut comparable_bounds = vec![Default::default(); map.len()];
+    for (k, (v, i)) in map {
+        comparable_bounds[i] = (k, v);
+    }
+
     if_chain! {
         if repeated_res;
         if let [first_trait, .., last_trait] = bounds;
         then {
             let all_trait_span = first_trait.span().to(last_trait.span());
 
-            let mut traits = map.values()
-                .filter_map(|span| snippet_opt(cx, *span))
+            let traits = comparable_bounds.iter()
+                .filter_map(|&(_, span)| snippet_opt(cx, span))
                 .collect::<Vec<_>>();
-            traits.sort_unstable();
             let traits = traits.join(" + ");
 
             span_lint_and_sugg(
@@ -373,4 +397,6 @@ fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) {
             );
         }
     }
+
+    comparable_bounds
 }
diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs
index 5f3e98144f4..424a6e9264e 100644
--- a/clippy_lints/src/transmute/mod.rs
+++ b/clippy_lints/src/transmute/mod.rs
@@ -9,6 +9,7 @@ mod transmute_ptr_to_ref;
 mod transmute_ref_to_ref;
 mod transmute_undefined_repr;
 mod transmutes_expressible_as_ptr_casts;
+mod transmuting_null;
 mod unsound_collection_transmute;
 mod useless_transmute;
 mod utils;
@@ -386,6 +387,28 @@ declare_clippy_lint! {
     "transmute to or from a type with an undefined representation"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for transmute calls which would receive a null pointer.
+    ///
+    /// ### Why is this bad?
+    /// Transmuting a null pointer is undefined behavior.
+    ///
+    /// ### Known problems
+    /// Not all cases can be detected at the moment of this writing.
+    /// For example, variables which hold a null pointer and are then fed to a `transmute`
+    /// call, aren't detectable yet.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };
+    /// ```
+    #[clippy::version = "1.35.0"]
+    pub TRANSMUTING_NULL,
+    correctness,
+    "transmutes from a null pointer to a reference, which is undefined behavior"
+}
+
 pub struct Transmute {
     msrv: Option<RustcVersion>,
 }
@@ -404,6 +427,7 @@ impl_lint_pass!(Transmute => [
     UNSOUND_COLLECTION_TRANSMUTE,
     TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
     TRANSMUTE_UNDEFINED_REPR,
+    TRANSMUTING_NULL,
 ]);
 impl Transmute {
     #[must_use]
@@ -436,6 +460,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
 
                 let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
                     | crosspointer_transmute::check(cx, e, from_ty, to_ty)
+                    | transmuting_null::check(cx, e, arg, to_ty)
                     | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv)
                     | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context)
                     | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
diff --git a/clippy_lints/src/transmute/transmute_undefined_repr.rs b/clippy_lints/src/transmute/transmute_undefined_repr.rs
index 20b348fc14f..b6d7d9f5b42 100644
--- a/clippy_lints/src/transmute/transmute_undefined_repr.rs
+++ b/clippy_lints/src/transmute/transmute_undefined_repr.rs
@@ -5,9 +5,9 @@ use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty::subst::{Subst, SubstsRef};
 use rustc_middle::ty::{self, IntTy, Ty, TypeAndMut, UintTy};
-use rustc_span::Span;
+use rustc_span::DUMMY_SP;
 
-#[allow(clippy::too_many_lines)]
+#[expect(clippy::too_many_lines)]
 pub(super) fn check<'tcx>(
     cx: &LateContext<'tcx>,
     e: &'tcx Expr<'_>,
@@ -18,116 +18,89 @@ pub(super) fn check<'tcx>(
     let mut to_ty = cx.tcx.erase_regions(to_ty_orig);
 
     while from_ty != to_ty {
-        match reduce_refs(cx, e.span, from_ty, to_ty) {
-            ReducedTys::FromFatPtr {
-                unsized_ty,
-                to_ty: to_sub_ty,
-            } => match reduce_ty(cx, to_sub_ty) {
-                ReducedTy::TypeErasure => break,
-                ReducedTy::UnorderedFields(ty) if is_size_pair(ty) => break,
-                ReducedTy::Ref(to_sub_ty) => {
-                    from_ty = unsized_ty;
-                    to_ty = to_sub_ty;
-                    continue;
-                },
-                _ => {
-                    span_lint_and_then(
-                        cx,
-                        TRANSMUTE_UNDEFINED_REPR,
-                        e.span,
-                        &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
-                        |diag| {
-                            if from_ty_orig.peel_refs() != unsized_ty {
-                                diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
-                            }
-                        },
-                    );
-                    return true;
-                },
+        let reduced_tys = reduce_refs(cx, from_ty, to_ty);
+        match (reduce_ty(cx, reduced_tys.from_ty), reduce_ty(cx, reduced_tys.to_ty)) {
+            // Various forms of type erasure.
+            (ReducedTy::TypeErasure { raw_ptr_only: false }, _)
+            | (_, ReducedTy::TypeErasure { raw_ptr_only: false }) => return false,
+            (ReducedTy::TypeErasure { .. }, _) if reduced_tys.from_raw_ptr => return false,
+            (_, ReducedTy::TypeErasure { .. }) if reduced_tys.to_raw_ptr => return false,
+
+            // `Repr(C)` <-> unordered type.
+            // If the first field of the `Repr(C)` type matches then the transmute is ok
+            (ReducedTy::OrderedFields(_, Some(from_sub_ty)), ReducedTy::UnorderedFields(to_sub_ty))
+            | (ReducedTy::UnorderedFields(from_sub_ty), ReducedTy::OrderedFields(_, Some(to_sub_ty))) => {
+                from_ty = from_sub_ty;
+                to_ty = to_sub_ty;
+                continue;
             },
-            ReducedTys::ToFatPtr {
-                unsized_ty,
-                from_ty: from_sub_ty,
-            } => match reduce_ty(cx, from_sub_ty) {
-                ReducedTy::TypeErasure => break,
-                ReducedTy::UnorderedFields(ty) if is_size_pair(ty) => break,
-                ReducedTy::Ref(from_sub_ty) => {
-                    from_ty = from_sub_ty;
-                    to_ty = unsized_ty;
-                    continue;
-                },
-                _ => {
-                    span_lint_and_then(
-                        cx,
-                        TRANSMUTE_UNDEFINED_REPR,
-                        e.span,
-                        &format!("transmute to `{}` which has an undefined layout", to_ty_orig),
-                        |diag| {
-                            if to_ty_orig.peel_refs() != unsized_ty {
-                                diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
-                            }
-                        },
-                    );
-                    return true;
-                },
+            (ReducedTy::OrderedFields(_, Some(from_sub_ty)), ReducedTy::Other(to_sub_ty)) if reduced_tys.to_fat_ptr => {
+                from_ty = from_sub_ty;
+                to_ty = to_sub_ty;
+                continue;
             },
-            ReducedTys::ToPtr {
-                from_ty: from_sub_ty,
-                to_ty: to_sub_ty,
-            } => match reduce_ty(cx, from_sub_ty) {
-                ReducedTy::UnorderedFields(from_ty) => {
-                    span_lint_and_then(
-                        cx,
-                        TRANSMUTE_UNDEFINED_REPR,
-                        e.span,
-                        &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
-                        |diag| {
-                            if from_ty_orig.peel_refs() != from_ty {
-                                diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
-                            }
-                        },
-                    );
-                    return true;
-                },
-                ReducedTy::Ref(from_sub_ty) => {
-                    from_ty = from_sub_ty;
-                    to_ty = to_sub_ty;
-                    continue;
-                },
-                _ => break,
+            (ReducedTy::Other(from_sub_ty), ReducedTy::OrderedFields(_, Some(to_sub_ty)))
+                if reduced_tys.from_fat_ptr =>
+            {
+                from_ty = from_sub_ty;
+                to_ty = to_sub_ty;
+                continue;
             },
-            ReducedTys::FromPtr {
-                from_ty: from_sub_ty,
-                to_ty: to_sub_ty,
-            } => match reduce_ty(cx, to_sub_ty) {
-                ReducedTy::UnorderedFields(to_ty) => {
-                    span_lint_and_then(
-                        cx,
-                        TRANSMUTE_UNDEFINED_REPR,
-                        e.span,
-                        &format!("transmute to `{}` which has an undefined layout", to_ty_orig),
-                        |diag| {
-                            if to_ty_orig.peel_refs() != to_ty {
-                                diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
-                            }
-                        },
-                    );
-                    return true;
-                },
-                ReducedTy::Ref(to_sub_ty) => {
-                    from_ty = from_sub_ty;
-                    to_ty = to_sub_ty;
-                    continue;
-                },
-                _ => break,
+
+            // ptr <-> ptr
+            (ReducedTy::Other(from_sub_ty), ReducedTy::Other(to_sub_ty))
+                if matches!(from_sub_ty.kind(), ty::Ref(..) | ty::RawPtr(_))
+                    && matches!(to_sub_ty.kind(), ty::Ref(..) | ty::RawPtr(_)) =>
+            {
+                from_ty = from_sub_ty;
+                to_ty = to_sub_ty;
+                continue;
             },
-            ReducedTys::Other {
-                from_ty: from_sub_ty,
-                to_ty: to_sub_ty,
-            } => match (reduce_ty(cx, from_sub_ty), reduce_ty(cx, to_sub_ty)) {
-                (ReducedTy::TypeErasure, _) | (_, ReducedTy::TypeErasure) => return false,
-                (ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => {
-                    let same_adt_did = if let (ty::Adt(from_def, from_subs), ty::Adt(to_def, to_subs))
+
+            // fat ptr <-> (*size, *size)
+            (ReducedTy::Other(_), ReducedTy::UnorderedFields(to_ty))
+                if reduced_tys.from_fat_ptr && is_size_pair(to_ty) =>
+            {
+                return false;
+            },
+            (ReducedTy::UnorderedFields(from_ty), ReducedTy::Other(_))
+                if reduced_tys.to_fat_ptr && is_size_pair(from_ty) =>
+            {
+                return false;
+            },
+
+            // fat ptr -> some struct | some struct -> fat ptr
+            (ReducedTy::Other(_), _) if reduced_tys.from_fat_ptr => {
+                span_lint_and_then(
+                    cx,
+                    TRANSMUTE_UNDEFINED_REPR,
+                    e.span,
+                    &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
+                    |diag| {
+                        if from_ty_orig.peel_refs() != from_ty.peel_refs() {
+                            diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
+                        }
+                    },
+                );
+                return true;
+            },
+            (_, ReducedTy::Other(_)) if reduced_tys.to_fat_ptr => {
+                span_lint_and_then(
+                    cx,
+                    TRANSMUTE_UNDEFINED_REPR,
+                    e.span,
+                    &format!("transmute to `{}` which has an undefined layout", to_ty_orig),
+                    |diag| {
+                        if to_ty_orig.peel_refs() != to_ty.peel_refs() {
+                            diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
+                        }
+                    },
+                );
+                return true;
+            },
+
+            (ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => {
+                let same_adt_did = if let (ty::Adt(from_def, from_subs), ty::Adt(to_def, to_subs))
                         = (from_ty.kind(), to_ty.kind())
                         && from_def == to_def
                     {
@@ -138,79 +111,72 @@ pub(super) fn check<'tcx>(
                     } else {
                         None
                     };
-                    span_lint_and_then(
-                        cx,
-                        TRANSMUTE_UNDEFINED_REPR,
-                        e.span,
-                        &format!(
-                            "transmute from `{}` to `{}`, both of which have an undefined layout",
-                            from_ty_orig, to_ty_orig
-                        ),
-                        |diag| {
-                            if let Some(same_adt_did) = same_adt_did {
-                                diag.note(&format!(
-                                    "two instances of the same generic type (`{}`) may have different layouts",
-                                    cx.tcx.item_name(same_adt_did)
-                                ));
-                            } else {
-                                if from_ty_orig.peel_refs() != from_ty {
-                                    diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
-                                }
-                                if to_ty_orig.peel_refs() != to_ty {
-                                    diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
-                                }
-                            }
-                        },
-                    );
-                    return true;
-                },
-                (
-                    ReducedTy::UnorderedFields(from_ty),
-                    ReducedTy::Other(_) | ReducedTy::OrderedFields(_) | ReducedTy::Ref(_),
-                ) => {
-                    span_lint_and_then(
-                        cx,
-                        TRANSMUTE_UNDEFINED_REPR,
-                        e.span,
-                        &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
-                        |diag| {
+                span_lint_and_then(
+                    cx,
+                    TRANSMUTE_UNDEFINED_REPR,
+                    e.span,
+                    &format!(
+                        "transmute from `{}` to `{}`, both of which have an undefined layout",
+                        from_ty_orig, to_ty_orig
+                    ),
+                    |diag| {
+                        if let Some(same_adt_did) = same_adt_did {
+                            diag.note(&format!(
+                                "two instances of the same generic type (`{}`) may have different layouts",
+                                cx.tcx.item_name(same_adt_did)
+                            ));
+                        } else {
                             if from_ty_orig.peel_refs() != from_ty {
                                 diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
                             }
-                        },
-                    );
-                    return true;
-                },
-                (
-                    ReducedTy::Other(_) | ReducedTy::OrderedFields(_) | ReducedTy::Ref(_),
-                    ReducedTy::UnorderedFields(to_ty),
-                ) => {
-                    span_lint_and_then(
-                        cx,
-                        TRANSMUTE_UNDEFINED_REPR,
-                        e.span,
-                        &format!("transmute into `{}` which has an undefined layout", to_ty_orig),
-                        |diag| {
                             if to_ty_orig.peel_refs() != to_ty {
                                 diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
                             }
-                        },
-                    );
-                    return true;
-                },
-                (ReducedTy::Ref(from_sub_ty), ReducedTy::Ref(to_sub_ty)) => {
-                    from_ty = from_sub_ty;
-                    to_ty = to_sub_ty;
-                    continue;
-                },
-                (
-                    ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_) | ReducedTy::Param,
-                    ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_) | ReducedTy::Param,
-                )
-                | (
-                    ReducedTy::UnorderedFields(_) | ReducedTy::Param,
-                    ReducedTy::UnorderedFields(_) | ReducedTy::Param,
-                ) => break,
+                        }
+                    },
+                );
+                return true;
+            },
+            (
+                ReducedTy::UnorderedFields(from_ty),
+                ReducedTy::Other(_) | ReducedTy::OrderedFields(..) | ReducedTy::TypeErasure { raw_ptr_only: true },
+            ) => {
+                span_lint_and_then(
+                    cx,
+                    TRANSMUTE_UNDEFINED_REPR,
+                    e.span,
+                    &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
+                    |diag| {
+                        if from_ty_orig.peel_refs() != from_ty {
+                            diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
+                        }
+                    },
+                );
+                return true;
+            },
+            (
+                ReducedTy::Other(_) | ReducedTy::OrderedFields(..) | ReducedTy::TypeErasure { raw_ptr_only: true },
+                ReducedTy::UnorderedFields(to_ty),
+            ) => {
+                span_lint_and_then(
+                    cx,
+                    TRANSMUTE_UNDEFINED_REPR,
+                    e.span,
+                    &format!("transmute into `{}` which has an undefined layout", to_ty_orig),
+                    |diag| {
+                        if to_ty_orig.peel_refs() != to_ty {
+                            diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
+                        }
+                    },
+                );
+                return true;
+            },
+            (
+                ReducedTy::OrderedFields(..) | ReducedTy::Other(_) | ReducedTy::TypeErasure { raw_ptr_only: true },
+                ReducedTy::OrderedFields(..) | ReducedTy::Other(_) | ReducedTy::TypeErasure { raw_ptr_only: true },
+            )
+            | (ReducedTy::UnorderedFields(_), ReducedTy::UnorderedFields(_)) => {
+                break;
             },
         }
     }
@@ -218,64 +184,64 @@ pub(super) fn check<'tcx>(
     false
 }
 
-enum ReducedTys<'tcx> {
-    FromFatPtr { unsized_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
-    ToFatPtr { unsized_ty: Ty<'tcx>, from_ty: Ty<'tcx> },
-    ToPtr { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
-    FromPtr { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
-    Other { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
+#[expect(clippy::struct_excessive_bools)]
+struct ReducedTys<'tcx> {
+    from_ty: Ty<'tcx>,
+    to_ty: Ty<'tcx>,
+    from_raw_ptr: bool,
+    to_raw_ptr: bool,
+    from_fat_ptr: bool,
+    to_fat_ptr: bool,
 }
 
 /// Remove references so long as both types are references.
-fn reduce_refs<'tcx>(
-    cx: &LateContext<'tcx>,
-    span: Span,
-    mut from_ty: Ty<'tcx>,
-    mut to_ty: Ty<'tcx>,
-) -> ReducedTys<'tcx> {
-    loop {
-        return match (from_ty.kind(), to_ty.kind()) {
+fn reduce_refs<'tcx>(cx: &LateContext<'tcx>, mut from_ty: Ty<'tcx>, mut to_ty: Ty<'tcx>) -> ReducedTys<'tcx> {
+    let mut from_raw_ptr = false;
+    let mut to_raw_ptr = false;
+    let (from_fat_ptr, to_fat_ptr) = loop {
+        break match (from_ty.kind(), to_ty.kind()) {
             (
                 &(ty::Ref(_, from_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: from_sub_ty, .. })),
                 &(ty::Ref(_, to_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: to_sub_ty, .. })),
             ) => {
+                from_raw_ptr = matches!(*from_ty.kind(), ty::RawPtr(_));
                 from_ty = from_sub_ty;
+                to_raw_ptr = matches!(*to_ty.kind(), ty::RawPtr(_));
                 to_ty = to_sub_ty;
                 continue;
             },
             (&(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. })), _)
-                if !unsized_ty.is_sized(cx.tcx.at(span), cx.param_env) =>
+                if !unsized_ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env) =>
             {
-                ReducedTys::FromFatPtr { unsized_ty, to_ty }
+                (true, false)
             },
             (_, &(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. })))
-                if !unsized_ty.is_sized(cx.tcx.at(span), cx.param_env) =>
+                if !unsized_ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env) =>
             {
-                ReducedTys::ToFatPtr { unsized_ty, from_ty }
+                (false, true)
             },
-            (&(ty::Ref(_, from_ty, _) | ty::RawPtr(TypeAndMut { ty: from_ty, .. })), _) => {
-                ReducedTys::FromPtr { from_ty, to_ty }
-            },
-            (_, &(ty::Ref(_, to_ty, _) | ty::RawPtr(TypeAndMut { ty: to_ty, .. }))) => {
-                ReducedTys::ToPtr { from_ty, to_ty }
-            },
-            _ => ReducedTys::Other { from_ty, to_ty },
+            _ => (false, false),
         };
+    };
+    ReducedTys {
+        from_ty,
+        to_ty,
+        from_raw_ptr,
+        to_raw_ptr,
+        from_fat_ptr,
+        to_fat_ptr,
     }
 }
 
 enum ReducedTy<'tcx> {
     /// The type can be used for type erasure.
-    TypeErasure,
+    TypeErasure { raw_ptr_only: bool },
     /// The type is a struct containing either zero non-zero sized fields, or multiple non-zero
     /// sized fields with a defined order.
-    OrderedFields(Ty<'tcx>),
+    /// The second value is the first non-zero sized type.
+    OrderedFields(Ty<'tcx>, Option<Ty<'tcx>>),
     /// The type is a struct containing multiple non-zero sized fields with no defined order.
     UnorderedFields(Ty<'tcx>),
-    /// The type is a reference to the contained type.
-    Ref(Ty<'tcx>),
-    /// The type is a generic parameter.
-    Param,
     /// Any other type.
     Other(Ty<'tcx>),
 }
@@ -285,16 +251,18 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx>
     loop {
         ty = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty).unwrap_or(ty);
         return match *ty.kind() {
-            ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => ReducedTy::TypeErasure,
+            ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => {
+                ReducedTy::TypeErasure { raw_ptr_only: false }
+            },
             ty::Array(sub_ty, _) | ty::Slice(sub_ty) => {
                 ty = sub_ty;
                 continue;
             },
-            ty::Tuple(args) if args.is_empty() => ReducedTy::TypeErasure,
+            ty::Tuple(args) if args.is_empty() => ReducedTy::TypeErasure { raw_ptr_only: false },
             ty::Tuple(args) => {
                 let mut iter = args.iter();
                 let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else {
-                    return ReducedTy::OrderedFields(ty);
+                    return ReducedTy::OrderedFields(ty, None);
                 };
                 if iter.all(|ty| is_zero_sized_ty(cx, ty)) {
                     ty = sized_ty;
@@ -309,27 +277,25 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx>
                     .iter()
                     .map(|f| cx.tcx.bound_type_of(f.did).subst(cx.tcx, substs));
                 let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else {
-                    return ReducedTy::TypeErasure;
+                    return ReducedTy::TypeErasure { raw_ptr_only: false };
                 };
                 if iter.all(|ty| is_zero_sized_ty(cx, ty)) {
                     ty = sized_ty;
                     continue;
                 }
                 if def.repr().inhibit_struct_field_reordering_opt() {
-                    ReducedTy::OrderedFields(ty)
+                    ReducedTy::OrderedFields(ty, Some(sized_ty))
                 } else {
                     ReducedTy::UnorderedFields(ty)
                 }
             },
             ty::Adt(def, _) if def.is_enum() && (def.variants().is_empty() || is_c_void(cx, ty)) => {
-                ReducedTy::TypeErasure
+                ReducedTy::TypeErasure { raw_ptr_only: false }
             },
             // TODO: Check if the conversion to or from at least one of a union's fields is valid.
-            ty::Adt(def, _) if def.is_union() => ReducedTy::TypeErasure,
-            ty::Foreign(_) => ReducedTy::TypeErasure,
-            ty::Ref(_, ty, _) => ReducedTy::Ref(ty),
-            ty::RawPtr(ty) => ReducedTy::Ref(ty.ty),
-            ty::Param(_) => ReducedTy::Param,
+            ty::Adt(def, _) if def.is_union() => ReducedTy::TypeErasure { raw_ptr_only: false },
+            ty::Foreign(_) | ty::Param(_) => ReducedTy::TypeErasure { raw_ptr_only: false },
+            ty::Int(_) | ty::Uint(_) => ReducedTy::TypeErasure { raw_ptr_only: true },
             _ => ReducedTy::Other(ty),
         };
     }
diff --git a/clippy_lints/src/transmute/transmuting_null.rs b/clippy_lints/src/transmute/transmuting_null.rs
new file mode 100644
index 00000000000..d8e349af7af
--- /dev/null
+++ b/clippy_lints/src/transmute/transmuting_null.rs
@@ -0,0 +1,61 @@
+use clippy_utils::consts::{constant_context, Constant};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::is_path_diagnostic_item;
+use if_chain::if_chain;
+use rustc_ast::LitKind;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty::Ty;
+use rustc_span::symbol::sym;
+
+use super::TRANSMUTING_NULL;
+
+const LINT_MSG: &str = "transmuting a known null pointer into a reference";
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>, to_ty: Ty<'tcx>) -> bool {
+    if !to_ty.is_ref() {
+        return false;
+    }
+
+    // Catching transmute over constants that resolve to `null`.
+    let mut const_eval_context = constant_context(cx, cx.typeck_results());
+    if_chain! {
+        if let ExprKind::Path(ref _qpath) = arg.kind;
+        if let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg);
+        if x == 0;
+        then {
+            span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
+            return true;
+        }
+    }
+
+    // Catching:
+    // `std::mem::transmute(0 as *const i32)`
+    if_chain! {
+        if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind;
+        if let ExprKind::Lit(ref lit) = inner_expr.kind;
+        if let LitKind::Int(0, _) = lit.node;
+        then {
+            span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
+            return true;
+        }
+    }
+
+    // Catching:
+    // `std::mem::transmute(std::ptr::null::<i32>())`
+    if_chain! {
+        if let ExprKind::Call(func1, []) = arg.kind;
+        if is_path_diagnostic_item(cx, func1, sym::ptr_null);
+        then {
+            span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
+            return true;
+        }
+    }
+
+    // FIXME:
+    // Also catch transmutations of variables which are known nulls.
+    // To do this, MIR const propagation seems to be the better tool.
+    // Whenever MIR const prop routines are more developed, this will
+    // become available. As of this writing (25/03/19) it is not yet.
+    false
+}
diff --git a/clippy_lints/src/transmuting_null.rs b/clippy_lints/src/transmuting_null.rs
deleted file mode 100644
index 7939dfedc3a..00000000000
--- a/clippy_lints/src/transmuting_null.rs
+++ /dev/null
@@ -1,89 +0,0 @@
-use clippy_utils::consts::{constant_context, Constant};
-use clippy_utils::diagnostics::span_lint;
-use clippy_utils::is_expr_diagnostic_item;
-use if_chain::if_chain;
-use rustc_ast::LitKind;
-use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::lint::in_external_macro;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::sym;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for transmute calls which would receive a null pointer.
-    ///
-    /// ### Why is this bad?
-    /// Transmuting a null pointer is undefined behavior.
-    ///
-    /// ### Known problems
-    /// Not all cases can be detected at the moment of this writing.
-    /// For example, variables which hold a null pointer and are then fed to a `transmute`
-    /// call, aren't detectable yet.
-    ///
-    /// ### Example
-    /// ```rust
-    /// let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };
-    /// ```
-    #[clippy::version = "1.35.0"]
-    pub TRANSMUTING_NULL,
-    correctness,
-    "transmutes from a null pointer to a reference, which is undefined behavior"
-}
-
-declare_lint_pass!(TransmutingNull => [TRANSMUTING_NULL]);
-
-const LINT_MSG: &str = "transmuting a known null pointer into a reference";
-
-impl<'tcx> LateLintPass<'tcx> for TransmutingNull {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if in_external_macro(cx.sess(), expr.span) {
-            return;
-        }
-
-        if_chain! {
-            if let ExprKind::Call(func, [arg]) = expr.kind;
-            if is_expr_diagnostic_item(cx, func, sym::transmute);
-
-            then {
-                // Catching transmute over constants that resolve to `null`.
-                let mut const_eval_context = constant_context(cx, cx.typeck_results());
-                if_chain! {
-                    if let ExprKind::Path(ref _qpath) = arg.kind;
-                    if let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg);
-                    if x == 0;
-                    then {
-                        span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
-                    }
-                }
-
-                // Catching:
-                // `std::mem::transmute(0 as *const i32)`
-                if_chain! {
-                    if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind;
-                    if let ExprKind::Lit(ref lit) = inner_expr.kind;
-                    if let LitKind::Int(0, _) = lit.node;
-                    then {
-                        span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
-                    }
-                }
-
-                // Catching:
-                // `std::mem::transmute(std::ptr::null::<i32>())`
-                if_chain! {
-                    if let ExprKind::Call(func1, []) = arg.kind;
-                    if is_expr_diagnostic_item(cx, func1, sym::ptr_null);
-                    then {
-                        span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
-                    }
-                }
-
-                // FIXME:
-                // Also catch transmutations of variables which are known nulls.
-                // To do this, MIR const propagation seems to be the better tool.
-                // Whenever MIR const prop routines are more developed, this will
-                // become available. As of this writing (25/03/19) it is not yet.
-            }
-        }
-    }
-}
diff --git a/clippy_lints/src/unicode.rs b/clippy_lints/src/unicode.rs
index cc64d17be05..8980283e5c8 100644
--- a/clippy_lints/src/unicode.rs
+++ b/clippy_lints/src/unicode.rs
@@ -1,5 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::is_lint_allowed;
+use clippy_utils::macros::span_is_local;
 use clippy_utils::source::snippet;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
@@ -98,6 +99,10 @@ fn escape<T: Iterator<Item = char>>(s: T) -> String {
 }
 
 fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) {
+    if !span_is_local(span) {
+        return;
+    }
+
     let string = snippet(cx, span, "");
     if string.chars().any(|c| ['\u{200B}', '\u{ad}', '\u{2060}'].contains(&c)) {
         span_lint_and_sugg(
@@ -113,6 +118,7 @@ fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) {
             Applicability::MachineApplicable,
         );
     }
+
     if string.chars().any(|c| c as u32 > 0x7F) {
         span_lint_and_sugg(
             cx,
@@ -128,6 +134,7 @@ fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) {
             Applicability::MachineApplicable,
         );
     }
+
     if is_lint_allowed(cx, NON_ASCII_LITERAL, id) && string.chars().zip(string.nfc()).any(|(a, b)| a != b) {
         span_lint_and_sugg(
             cx,
diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs
index 9f4c5555f11..9a41603f2f4 100644
--- a/clippy_lints/src/uninit_vec.rs
+++ b/clippy_lints/src/uninit_vec.rs
@@ -45,7 +45,7 @@ declare_clippy_lint! {
     ///    let mut vec: Vec<MaybeUninit<T>> = Vec::with_capacity(1000);
     ///    vec.set_len(1000);  // `MaybeUninit` can be uninitialized
     ///    ```
-    /// 3. If you are on nightly, `Vec::spare_capacity_mut()` is available:
+    /// 3. If you are on 1.60.0 or later, `Vec::spare_capacity_mut()` is available:
     ///    ```rust,ignore
     ///    let mut vec: Vec<u8> = Vec::with_capacity(1000);
     ///    let remaining = vec.spare_capacity_mut();  // `&mut [MaybeUninit<u8>]`
diff --git a/clippy_lints/src/unit_hash.rs b/clippy_lints/src/unit_hash.rs
deleted file mode 100644
index 88ca0cb20a1..00000000000
--- a/clippy_lints/src/unit_hash.rs
+++ /dev/null
@@ -1,78 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet;
-use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Detects `().hash(_)`.
-    ///
-    /// ### Why is this bad?
-    /// Hashing a unit value doesn't do anything as the implementation of `Hash` for `()` is a no-op.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # use std::hash::Hash;
-    /// # use std::collections::hash_map::DefaultHasher;
-    /// # enum Foo { Empty, WithValue(u8) }
-    /// # use Foo::*;
-    /// # let mut state = DefaultHasher::new();
-    /// # let my_enum = Foo::Empty;
-    /// match my_enum {
-    /// 	Empty => ().hash(&mut state),
-    /// 	WithValue(x) => x.hash(&mut state),
-    /// }
-    /// ```
-    /// Use instead:
-    /// ```rust
-    /// # use std::hash::Hash;
-    /// # use std::collections::hash_map::DefaultHasher;
-    /// # enum Foo { Empty, WithValue(u8) }
-    /// # use Foo::*;
-    /// # let mut state = DefaultHasher::new();
-    /// # let my_enum = Foo::Empty;
-    /// match my_enum {
-    /// 	Empty => 0_u8.hash(&mut state),
-    /// 	WithValue(x) => x.hash(&mut state),
-    /// }
-    /// ```
-    #[clippy::version = "1.58.0"]
-    pub UNIT_HASH,
-    correctness,
-    "hashing a unit value, which does nothing"
-}
-declare_lint_pass!(UnitHash => [UNIT_HASH]);
-
-impl<'tcx> LateLintPass<'tcx> for UnitHash {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-        if_chain! {
-            if let ExprKind::MethodCall(name_ident, args, _) = &expr.kind;
-            if name_ident.ident.name == sym::hash;
-            if let [recv, state_param] = args;
-            if cx.typeck_results().expr_ty(recv).is_unit();
-            then {
-                span_lint_and_then(
-                    cx,
-                    UNIT_HASH,
-                    expr.span,
-                    "this call to `hash` on the unit type will do nothing",
-                    |diag| {
-                        diag.span_suggestion(
-                            expr.span,
-                            "remove the call to `hash` or consider using",
-                            format!(
-                                "0_u8.hash({})",
-                                snippet(cx, state_param.span, ".."),
-                            ),
-                            Applicability::MaybeIncorrect,
-                        );
-                        diag.note("the implementation of `Hash` for `()` is a no-op");
-                    }
-                );
-            }
-        }
-    }
-}
diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs
index f4f5a4336a3..a5afbb8ff9d 100644
--- a/clippy_lints/src/unnecessary_wraps.rs
+++ b/clippy_lints/src/unnecessary_wraps.rs
@@ -130,7 +130,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
                         (
                             ret_expr.span,
                             if inner_type.is_unit() {
-                                "".to_string()
+                                String::new()
                             } else {
                                 snippet(cx, arg.span.source_callsite(), "..").to_string()
                             }
diff --git a/clippy_lints/src/unused_peekable.rs b/clippy_lints/src/unused_peekable.rs
new file mode 100644
index 00000000000..ac73173697e
--- /dev/null
+++ b/clippy_lints/src/unused_peekable.rs
@@ -0,0 +1,225 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::{match_type, peel_mid_ty_refs_is_mutable};
+use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, paths, peel_ref_operators};
+use rustc_ast::Mutability;
+use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_hir::lang_items::LangItem;
+use rustc_hir::{Block, Expr, ExprKind, HirId, Local, Node, PatKind, PathSegment, StmtKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for the creation of a `peekable` iterator that is never `.peek()`ed
+    ///
+    /// ### Why is this bad?
+    /// Creating a peekable iterator without using any of its methods is likely a mistake,
+    /// or just a leftover after a refactor.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let collection = vec![1, 2, 3];
+    /// let iter = collection.iter().peekable();
+    ///
+    /// for item in iter {
+    ///     // ...
+    /// }
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// let collection = vec![1, 2, 3];
+    /// let iter = collection.iter();
+    ///
+    /// for item in iter {
+    ///     // ...
+    /// }
+    /// ```
+    #[clippy::version = "1.64.0"]
+    pub UNUSED_PEEKABLE,
+    suspicious,
+    "creating a peekable iterator without using any of its methods"
+}
+
+declare_lint_pass!(UnusedPeekable => [UNUSED_PEEKABLE]);
+
+impl<'tcx> LateLintPass<'tcx> for UnusedPeekable {
+    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
+        // Don't lint `Peekable`s returned from a block
+        if let Some(expr) = block.expr
+            && let Some(ty) = cx.typeck_results().expr_ty_opt(peel_ref_operators(cx, expr))
+            && match_type(cx, ty, &paths::PEEKABLE)
+        {
+            return;
+        }
+
+        for (idx, stmt) in block.stmts.iter().enumerate() {
+            if !stmt.span.from_expansion()
+                && let StmtKind::Local(local) = stmt.kind
+                && let PatKind::Binding(_, binding, ident, _) = local.pat.kind
+                && let Some(init) = local.init
+                && !init.span.from_expansion()
+                && let Some(ty) = cx.typeck_results().expr_ty_opt(init)
+                && let (ty, _, Mutability::Mut) = peel_mid_ty_refs_is_mutable(ty)
+                && match_type(cx, ty, &paths::PEEKABLE)
+            {
+                let mut vis = PeekableVisitor::new(cx, binding);
+
+                if idx + 1 == block.stmts.len() && block.expr.is_none() {
+                    return;
+                }
+
+                for stmt in &block.stmts[idx..] {
+                    vis.visit_stmt(stmt);
+                }
+
+                if let Some(expr) = block.expr {
+                    vis.visit_expr(expr);
+                }
+
+                if !vis.found_peek_call {
+                    span_lint_and_help(
+                        cx,
+                        UNUSED_PEEKABLE,
+                        ident.span,
+                        "`peek` never called on `Peekable` iterator",
+                        None,
+                        "consider removing the call to `peekable`"
+                   );
+                }
+            }
+        }
+    }
+}
+
+struct PeekableVisitor<'a, 'tcx> {
+    cx: &'a LateContext<'tcx>,
+    expected_hir_id: HirId,
+    found_peek_call: bool,
+}
+
+impl<'a, 'tcx> PeekableVisitor<'a, 'tcx> {
+    fn new(cx: &'a LateContext<'tcx>, expected_hir_id: HirId) -> Self {
+        Self {
+            cx,
+            expected_hir_id,
+            found_peek_call: false,
+        }
+    }
+}
+
+impl<'tcx> Visitor<'_> for PeekableVisitor<'_, 'tcx> {
+    fn visit_expr(&mut self, ex: &'_ Expr<'_>) {
+        if self.found_peek_call {
+            return;
+        }
+
+        if path_to_local_id(ex, self.expected_hir_id) {
+            for (_, node) in self.cx.tcx.hir().parent_iter(ex.hir_id) {
+                match node {
+                    Node::Expr(expr) => {
+                        match expr.kind {
+                            // some_function(peekable)
+                            //
+                            // If the Peekable is passed to a function, stop
+                            ExprKind::Call(_, args) => {
+                                if let Some(func_did) = fn_def_id(self.cx, expr)
+                                    && let Ok(into_iter_did) = self
+                                        .cx
+                                        .tcx
+                                        .lang_items()
+                                        .require(LangItem::IntoIterIntoIter)
+                                    && func_did == into_iter_did
+                                {
+                                    // Probably a for loop desugar, stop searching
+                                    return;
+                                }
+
+                                if args.iter().any(|arg| {
+                                    matches!(arg.kind, ExprKind::Path(_)) && arg_is_mut_peekable(self.cx, arg)
+                                }) {
+                                    self.found_peek_call = true;
+                                    return;
+                                }
+                            },
+                            // Catch anything taking a Peekable mutably
+                            ExprKind::MethodCall(
+                                PathSegment {
+                                    ident: method_name_ident,
+                                    ..
+                                },
+                                [self_arg, remaining_args @ ..],
+                                _,
+                            ) => {
+                                let method_name = method_name_ident.name.as_str();
+
+                                // `Peekable` methods
+                                if matches!(method_name, "peek" | "peek_mut" | "next_if" | "next_if_eq")
+                                    && arg_is_mut_peekable(self.cx, self_arg)
+                                {
+                                    self.found_peek_call = true;
+                                    return;
+                                }
+
+                                // foo.some_method() excluding Iterator methods
+                                if remaining_args.iter().any(|arg| arg_is_mut_peekable(self.cx, arg))
+                                    && !is_trait_method(self.cx, expr, sym::Iterator)
+                                {
+                                    self.found_peek_call = true;
+                                    return;
+                                }
+
+                                // foo.by_ref(), keep checking for `peek`
+                                if method_name == "by_ref" {
+                                    continue;
+                                }
+
+                                return;
+                            },
+                            ExprKind::AddrOf(_, Mutability::Mut, _) | ExprKind::Unary(..) | ExprKind::DropTemps(_) => {
+                            },
+                            ExprKind::AddrOf(_, Mutability::Not, _) => return,
+                            _ => {
+                                self.found_peek_call = true;
+                                return;
+                            },
+                        }
+                    },
+                    Node::Local(Local { init: Some(init), .. }) => {
+                        if arg_is_mut_peekable(self.cx, init) {
+                            self.found_peek_call = true;
+                            return;
+                        }
+
+                        break;
+                    },
+                    Node::Stmt(stmt) => match stmt.kind {
+                        StmtKind::Expr(_) | StmtKind::Semi(_) => {},
+                        _ => {
+                            self.found_peek_call = true;
+                            return;
+                        },
+                    },
+                    Node::Block(_) | Node::ExprField(_) => {},
+                    _ => {
+                        break;
+                    },
+                }
+            }
+        }
+
+        walk_expr(self, ex);
+    }
+}
+
+fn arg_is_mut_peekable(cx: &LateContext<'_>, arg: &Expr<'_>) -> bool {
+    if let Some(ty) = cx.typeck_results().expr_ty_opt(arg)
+        && let (ty, _, Mutability::Mut) = peel_mid_ty_refs_is_mutable(ty)
+        && match_type(cx, ty, &paths::PEEKABLE)
+    {
+        true
+    } else {
+        false
+    }
+}
diff --git a/clippy_lints/src/unused_rounding.rs b/clippy_lints/src/unused_rounding.rs
index e1ec357838d..b8a5d4ea8c9 100644
--- a/clippy_lints/src/unused_rounding.rs
+++ b/clippy_lints/src/unused_rounding.rs
@@ -22,7 +22,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let x = 1f32;
     /// ```
-    #[clippy::version = "1.62.0"]
+    #[clippy::version = "1.63.0"]
     pub UNUSED_ROUNDING,
     nursery,
     "Uselessly rounding a whole number floating-point literal"
diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs
index 3faae9ac0d2..84e65d5fa0b 100644
--- a/clippy_lints/src/utils/conf.rs
+++ b/clippy_lints/src/utils/conf.rs
@@ -350,7 +350,7 @@ define_Conf! {
     /// Lint: DISALLOWED_SCRIPT_IDENTS.
     ///
     /// The list of unicode scripts allowed to be used in the scope.
-    (allowed_scripts: Vec<String> = ["Latin"].iter().map(ToString::to_string).collect()),
+    (allowed_scripts: Vec<String> = vec!["Latin".to_string()]),
     /// Lint: NON_SEND_FIELDS_IN_SEND_TY.
     ///
     /// Whether to apply the raw pointer heuristic to determine if a type is `Send`.
@@ -379,6 +379,10 @@ define_Conf! {
     ///
     /// Whether `dbg!` should be allowed in test functions
     (allow_dbg_in_tests: bool = false),
+    /// Lint: RESULT_LARGE_ERR
+    ///
+    /// The maximum size of the `Err`-variant in a `Result` returned from a function
+    (large_error_threshold: u64 = 128),
 }
 
 /// Search for the configuration file.
diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs
index 5dcacd604be..eb34085a2ab 100644
--- a/clippy_lints/src/utils/internal_lints.rs
+++ b/clippy_lints/src/utils/internal_lints.rs
@@ -593,8 +593,8 @@ fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Opt
     attrs.iter().find_map(|attr| {
         if_chain! {
             // Identify attribute
-            if let ast::AttrKind::Normal(ref attr_kind, _) = &attr.kind;
-            if let [tool_name, attr_name] = &attr_kind.path.segments[..];
+            if let ast::AttrKind::Normal(ref attr_kind) = &attr.kind;
+            if let [tool_name, attr_name] = &attr_kind.item.path.segments[..];
             if tool_name.ident.name == sym::clippy;
             if attr_name.ident.name == sym::version;
             if let Some(version) = attr.value_str();
diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs
index 92cf42c7ad4..b1148bccc2a 100644
--- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs
+++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs
@@ -797,7 +797,7 @@ fn get_lint_group_and_level_or_lint(
     let result = cx.lint_store.check_lint_name(
         lint_name,
         Some(sym::clippy),
-        &[Ident::with_dummy_span(sym::clippy)].into_iter().collect(),
+        &std::iter::once(Ident::with_dummy_span(sym::clippy)).collect(),
     );
     if let CheckLintNameResult::Tool(Ok(lint_lst)) = result {
         if let Some(group) = get_lint_group(cx, lint_lst[0]) {
diff --git a/clippy_lints/src/vec_resize_to_zero.rs b/clippy_lints/src/vec_resize_to_zero.rs
deleted file mode 100644
index 0fee3e812d2..00000000000
--- a/clippy_lints/src/vec_resize_to_zero.rs
+++ /dev/null
@@ -1,64 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::{match_def_path, paths};
-use if_chain::if_chain;
-use rustc_ast::LitKind;
-use rustc_errors::Applicability;
-use rustc_hir as hir;
-use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Spanned;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Finds occurrences of `Vec::resize(0, an_int)`
-    ///
-    /// ### Why is this bad?
-    /// This is probably an argument inversion mistake.
-    ///
-    /// ### Example
-    /// ```rust
-    /// vec!(1, 2, 3, 4, 5).resize(0, 5)
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust
-    /// vec!(1, 2, 3, 4, 5).clear()
-    /// ```
-    #[clippy::version = "1.46.0"]
-    pub VEC_RESIZE_TO_ZERO,
-    correctness,
-    "emptying a vector with `resize(0, an_int)` instead of `clear()` is probably an argument inversion mistake"
-}
-
-declare_lint_pass!(VecResizeToZero => [VEC_RESIZE_TO_ZERO]);
-
-impl<'tcx> LateLintPass<'tcx> for VecResizeToZero {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if_chain! {
-            if let hir::ExprKind::MethodCall(path_segment, args, _) = expr.kind;
-            if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
-            if match_def_path(cx, method_def_id, &paths::VEC_RESIZE) && args.len() == 3;
-            if let ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = args[1].kind;
-            if let ExprKind::Lit(Spanned { node: LitKind::Int(..), .. }) = args[2].kind;
-            then {
-                let method_call_span = expr.span.with_lo(path_segment.ident.span.lo());
-                span_lint_and_then(
-                    cx,
-                    VEC_RESIZE_TO_ZERO,
-                    expr.span,
-                    "emptying a vector with `resize`",
-                    |db| {
-                        db.help("the arguments may be inverted...");
-                        db.span_suggestion(
-                            method_call_span,
-                            "...or you can empty the vector with",
-                            "clear()".to_string(),
-                            Applicability::MaybeIncorrect,
-                        );
-                    },
-                );
-            }
-        }
-    }
-}
diff --git a/clippy_lints/src/verbose_file_reads.rs b/clippy_lints/src/verbose_file_reads.rs
deleted file mode 100644
index afd0077a658..00000000000
--- a/clippy_lints/src/verbose_file_reads.rs
+++ /dev/null
@@ -1,88 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::paths;
-use clippy_utils::ty::match_type;
-use if_chain::if_chain;
-use rustc_hir::{Expr, ExprKind, QPath};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for use of File::read_to_end and File::read_to_string.
-    ///
-    /// ### Why is this bad?
-    /// `fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values.
-    /// See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html)
-    ///
-    /// ### Example
-    /// ```rust,no_run
-    /// # use std::io::Read;
-    /// # use std::fs::File;
-    /// let mut f = File::open("foo.txt").unwrap();
-    /// let mut bytes = Vec::new();
-    /// f.read_to_end(&mut bytes).unwrap();
-    /// ```
-    /// Can be written more concisely as
-    /// ```rust,no_run
-    /// # use std::fs;
-    /// let mut bytes = fs::read("foo.txt").unwrap();
-    /// ```
-    #[clippy::version = "1.44.0"]
-    pub VERBOSE_FILE_READS,
-    restriction,
-    "use of `File::read_to_end` or `File::read_to_string`"
-}
-
-declare_lint_pass!(VerboseFileReads => [VERBOSE_FILE_READS]);
-
-impl<'tcx> LateLintPass<'tcx> for VerboseFileReads {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-        if is_file_read_to_end(cx, expr) {
-            span_lint_and_help(
-                cx,
-                VERBOSE_FILE_READS,
-                expr.span,
-                "use of `File::read_to_end`",
-                None,
-                "consider using `fs::read` instead",
-            );
-        } else if is_file_read_to_string(cx, expr) {
-            span_lint_and_help(
-                cx,
-                VERBOSE_FILE_READS,
-                expr.span,
-                "use of `File::read_to_string`",
-                None,
-                "consider using `fs::read_to_string` instead",
-            );
-        }
-    }
-}
-
-fn is_file_read_to_end<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
-    if_chain! {
-        if let ExprKind::MethodCall(method_name, [recv, ..], _) = expr.kind;
-        if method_name.ident.as_str() == "read_to_end";
-        if let ExprKind::Path(QPath::Resolved(None, _)) = &recv.kind;
-        let ty = cx.typeck_results().expr_ty(recv);
-        if match_type(cx, ty, &paths::FILE);
-        then {
-            return true
-        }
-    }
-    false
-}
-
-fn is_file_read_to_string<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
-    if_chain! {
-        if let ExprKind::MethodCall(method_name, exprs, _) = expr.kind;
-        if method_name.ident.as_str() == "read_to_string";
-        if let ExprKind::Path(QPath::Resolved(None, _)) = &exprs[0].kind;
-        let ty = cx.typeck_results().expr_ty(&exprs[0]);
-        if match_type(cx, ty, &paths::FILE);
-        then {
-            return true
-        }
-    }
-    false
-}
diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs
index fa2383066f3..5533840b166 100644
--- a/clippy_lints/src/write.rs
+++ b/clippy_lints/src/write.rs
@@ -3,8 +3,9 @@ use std::iter;
 use std::ops::{Deref, Range};
 
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::source::{snippet_opt, snippet_with_applicability};
+use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
 use rustc_ast::ast::{Expr, ExprKind, Impl, Item, ItemKind, MacCall, Path, StrLit, StrStyle};
+use rustc_ast::ptr::P;
 use rustc_ast::token::{self, LitKind};
 use rustc_ast::tokenstream::TokenStream;
 use rustc_errors::{Applicability, DiagnosticBuilder};
@@ -256,6 +257,28 @@ declare_clippy_lint! {
     "writing a literal with a format string"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// This lint warns when a named parameter in a format string is used as a positional one.
+    ///
+    /// ### Why is this bad?
+    /// It may be confused for an assignment and obfuscates which parameter is being used.
+    ///
+    /// ### Example
+    /// ```rust
+    /// println!("{}", x = 10);
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// println!("{x}", x = 10);
+    /// ```
+    #[clippy::version = "1.63.0"]
+    pub POSITIONAL_NAMED_FORMAT_PARAMETERS,
+    suspicious,
+    "named parameter in a format string is used positionally"
+}
+
 #[derive(Default)]
 pub struct Write {
     in_debug_impl: bool,
@@ -270,7 +293,8 @@ impl_lint_pass!(Write => [
     PRINT_LITERAL,
     WRITE_WITH_NEWLINE,
     WRITELN_EMPTY_STRING,
-    WRITE_LITERAL
+    WRITE_LITERAL,
+    POSITIONAL_NAMED_FORMAT_PARAMETERS,
 ]);
 
 impl EarlyLintPass for Write {
@@ -408,6 +432,7 @@ fn newline_span(fmtstr: &StrLit) -> (Span, bool) {
 #[derive(Default)]
 struct SimpleFormatArgs {
     unnamed: Vec<Vec<Span>>,
+    complex_unnamed: Vec<Vec<Span>>,
     named: Vec<(Symbol, Vec<Span>)>,
 }
 impl SimpleFormatArgs {
@@ -419,6 +444,10 @@ impl SimpleFormatArgs {
         })
     }
 
+    fn get_complex_unnamed(&self) -> impl Iterator<Item = &[Span]> {
+        self.complex_unnamed.iter().map(Vec::as_slice)
+    }
+
     fn get_named(&self, n: &Path) -> &[Span] {
         self.named.iter().find(|x| *n == x.0).map_or(&[], |x| x.1.as_slice())
     }
@@ -479,6 +508,61 @@ impl SimpleFormatArgs {
             },
         };
     }
+
+    fn push_to_complex(&mut self, span: Span, position: usize) {
+        if self.complex_unnamed.len() <= position {
+            self.complex_unnamed.resize_with(position, Vec::new);
+            self.complex_unnamed.push(vec![span]);
+        } else {
+            let args: &mut Vec<Span> = &mut self.complex_unnamed[position];
+            args.push(span);
+        }
+    }
+
+    fn push_complex(
+        &mut self,
+        cx: &EarlyContext<'_>,
+        arg: rustc_parse_format::Argument<'_>,
+        str_lit_span: Span,
+        fmt_span: Span,
+    ) {
+        use rustc_parse_format::{ArgumentImplicitlyIs, ArgumentIs, CountIsParam};
+
+        let snippet = snippet_opt(cx, fmt_span);
+
+        let end = snippet
+            .as_ref()
+            .and_then(|s| s.find(':'))
+            .or_else(|| fmt_span.hi().0.checked_sub(fmt_span.lo().0 + 1).map(|u| u as usize));
+
+        if let (ArgumentIs(n) | ArgumentImplicitlyIs(n), Some(end)) = (arg.position, end) {
+            let span = fmt_span.from_inner(InnerSpan::new(1, end));
+            self.push_to_complex(span, n);
+        };
+
+        if let (CountIsParam(n), Some(span)) = (arg.format.precision, arg.format.precision_span) {
+            // We need to do this hack as precision spans should be converted from .* to .foo$
+            let hack = if snippet.as_ref().and_then(|s| s.find('*')).is_some() {
+                0
+            } else {
+                1
+            };
+
+            let span = str_lit_span.from_inner(InnerSpan {
+                start: span.start + 1,
+                end: span.end - hack,
+            });
+            self.push_to_complex(span, n);
+        };
+
+        if let (CountIsParam(n), Some(span)) = (arg.format.width, arg.format.width_span) {
+            let span = str_lit_span.from_inner(InnerSpan {
+                start: span.start,
+                end: span.end - 1,
+            });
+            self.push_to_complex(span, n);
+        };
+    }
 }
 
 impl Write {
@@ -511,8 +595,8 @@ impl Write {
                 // FIXME: modify rustc's fmt string parser to give us the current span
                 span_lint(cx, USE_DEBUG, span, "use of `Debug`-based formatting");
             }
-
             args.push(arg, span);
+            args.push_complex(cx, arg, str_lit.span, span);
         }
 
         parser.errors.is_empty().then_some(args)
@@ -566,6 +650,7 @@ impl Write {
 
         let lint = if is_write { WRITE_LITERAL } else { PRINT_LITERAL };
         let mut unnamed_args = args.get_unnamed();
+        let mut complex_unnamed_args = args.get_complex_unnamed();
         loop {
             if !parser.eat(&token::Comma) {
                 return (Some(fmtstr), expr);
@@ -577,11 +662,20 @@ impl Write {
             } else {
                 return (Some(fmtstr), None);
             };
+            let complex_unnamed_arg = complex_unnamed_args.next();
+
             let (fmt_spans, lit) = match &token_expr.kind {
                 ExprKind::Lit(lit) => (unnamed_args.next().unwrap_or(&[]), lit),
-                ExprKind::Assign(lhs, rhs, _) => match (&lhs.kind, &rhs.kind) {
-                    (ExprKind::Path(_, p), ExprKind::Lit(lit)) => (args.get_named(p), lit),
-                    _ => continue,
+                ExprKind::Assign(lhs, rhs, _) => {
+                    if let Some(span) = complex_unnamed_arg {
+                        for x in span {
+                            Self::report_positional_named_param(cx, *x, lhs, rhs);
+                        }
+                    }
+                    match (&lhs.kind, &rhs.kind) {
+                        (ExprKind::Path(_, p), ExprKind::Lit(lit)) => (args.get_named(p), lit),
+                        _ => continue,
+                    }
                 },
                 _ => {
                     unnamed_args.next();
@@ -637,6 +731,29 @@ impl Write {
         }
     }
 
+    fn report_positional_named_param(cx: &EarlyContext<'_>, span: Span, lhs: &P<Expr>, _rhs: &P<Expr>) {
+        if let ExprKind::Path(_, _p) = &lhs.kind {
+            let mut applicability = Applicability::MachineApplicable;
+            let name = snippet_with_applicability(cx, lhs.span, "name", &mut applicability);
+            // We need to do this hack as precision spans should be converted from .* to .foo$
+            let hack = snippet(cx, span, "").contains('*');
+
+            span_lint_and_sugg(
+                cx,
+                POSITIONAL_NAMED_FORMAT_PARAMETERS,
+                span,
+                &format!("named parameter {} is used as a positional parameter", name),
+                "replace it with",
+                if hack {
+                    format!("{}$", name)
+                } else {
+                    format!("{}", name)
+                },
+                applicability,
+            );
+        };
+    }
+
     fn lint_println_empty_string(&self, cx: &EarlyContext<'_>, mac: &MacCall) {
         if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
             if fmt_str.symbol == kw::Empty {