about summary refs log tree commit diff
path: root/clippy_lints
diff options
context:
space:
mode:
authorPhilipp Krones <hello@philkrones.com>2024-06-13 12:18:48 +0200
committerPhilipp Krones <hello@philkrones.com>2024-06-13 12:24:08 +0200
commitcc63143bbf5ae28daae127902090492560313ca1 (patch)
tree3cf8901da2767e0e330b32ef8a1f8a3bf75adc0c /clippy_lints
parent35f54fd439432f56c749fbb34f1326e34ed1fc42 (diff)
parentaaade2d12d3cf78fd9eb24bf7973e86433d6373d (diff)
Merge remote-tracking branch 'upstream/master' into rustup
Diffstat (limited to 'clippy_lints')
-rw-r--r--clippy_lints/src/attrs/maybe_misused_cfg.rs51
-rw-r--r--clippy_lints/src/attrs/mismatched_target_os.rs90
-rw-r--r--clippy_lints/src/attrs/mod.rs73
-rw-r--r--clippy_lints/src/blocks_in_conditions.rs32
-rw-r--r--clippy_lints/src/booleans.rs54
-rw-r--r--clippy_lints/src/cargo/lint_groups_priority.rs60
-rw-r--r--clippy_lints/src/casts/cast_sign_loss.rs6
-rw-r--r--clippy_lints/src/casts/unnecessary_cast.rs4
-rw-r--r--clippy_lints/src/cognitive_complexity.rs4
-rw-r--r--clippy_lints/src/collection_is_never_read.rs4
-rw-r--r--clippy_lints/src/copies.rs6
-rw-r--r--clippy_lints/src/declared_lints.rs7
-rw-r--r--clippy_lints/src/deprecated_lints.rs26
-rw-r--r--clippy_lints/src/doc/missing_headers.rs2
-rw-r--r--clippy_lints/src/eta_reduction.rs31
-rw-r--r--clippy_lints/src/float_literal.rs2
-rw-r--r--clippy_lints/src/floating_point_arithmetic.rs5
-rw-r--r--clippy_lints/src/format_args.rs4
-rw-r--r--clippy_lints/src/from_over_into.rs10
-rw-r--r--clippy_lints/src/functions/must_use.rs4
-rw-r--r--clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs4
-rw-r--r--clippy_lints/src/implicit_return.rs4
-rw-r--r--clippy_lints/src/implied_bounds_in_impls.rs8
-rw-r--r--clippy_lints/src/indexing_slicing.rs50
-rw-r--r--clippy_lints/src/integer_division_remainder_used.rs2
-rw-r--r--clippy_lints/src/iter_without_into_iter.rs34
-rw-r--r--clippy_lints/src/legacy_numeric_constants.rs2
-rw-r--r--clippy_lints/src/lib.deprecated.rs8
-rw-r--r--clippy_lints/src/lib.rs4
-rw-r--r--clippy_lints/src/manual_unwrap_or_default.rs13
-rw-r--r--clippy_lints/src/matches/redundant_guards.rs4
-rw-r--r--clippy_lints/src/matches/redundant_pattern_match.rs4
-rw-r--r--clippy_lints/src/methods/collapsible_str_replace.rs4
-rw-r--r--clippy_lints/src/methods/mod.rs62
-rw-r--r--clippy_lints/src/methods/needless_character_iteration.rs124
-rw-r--r--clippy_lints/src/methods/option_map_unwrap_or.rs2
-rw-r--r--clippy_lints/src/methods/path_buf_push_overwrite.rs2
-rw-r--r--clippy_lints/src/methods/search_is_some.rs12
-rw-r--r--clippy_lints/src/methods/single_char_insert_string.rs47
-rw-r--r--clippy_lints/src/methods/single_char_pattern.rs64
-rw-r--r--clippy_lints/src/methods/single_char_push_string.rs46
-rw-r--r--clippy_lints/src/methods/str_splitn.rs4
-rw-r--r--clippy_lints/src/methods/unnecessary_filter_map.rs4
-rw-r--r--clippy_lints/src/methods/unnecessary_iter_cloned.rs4
-rw-r--r--clippy_lints/src/methods/unnecessary_result_map_or_else.rs19
-rw-r--r--clippy_lints/src/methods/unnecessary_sort_by.rs3
-rw-r--r--clippy_lints/src/methods/utils.rs63
-rw-r--r--clippy_lints/src/misc_early/zero_prefixed_literal.rs4
-rw-r--r--clippy_lints/src/missing_asserts_for_indexing.rs4
-rw-r--r--clippy_lints/src/missing_const_for_fn.rs21
-rw-r--r--clippy_lints/src/missing_fields_in_debug.rs4
-rw-r--r--clippy_lints/src/multiple_unsafe_ops_per_block.rs4
-rw-r--r--clippy_lints/src/mutable_debug_assertion.rs4
-rw-r--r--clippy_lints/src/needless_bool.rs13
-rw-r--r--clippy_lints/src/needless_late_init.rs6
-rw-r--r--clippy_lints/src/needless_maybe_sized.rs164
-rw-r--r--clippy_lints/src/needless_pass_by_ref_mut.rs4
-rw-r--r--clippy_lints/src/non_canonical_impls.rs10
-rw-r--r--clippy_lints/src/non_copy_const.rs32
-rw-r--r--clippy_lints/src/operators/assign_op_pattern.rs4
-rw-r--r--clippy_lints/src/panic_in_result_fn.rs2
-rw-r--r--clippy_lints/src/redundant_async_block.rs4
-rw-r--r--clippy_lints/src/reference.rs3
-rw-r--r--clippy_lints/src/returns.rs4
-rw-r--r--clippy_lints/src/significant_drop_tightening.rs50
-rw-r--r--clippy_lints/src/string_patterns.rs227
-rw-r--r--clippy_lints/src/strings.rs10
-rw-r--r--clippy_lints/src/suspicious_trait_impl.rs4
-rw-r--r--clippy_lints/src/transmute/missing_transmute_annotations.rs1
-rw-r--r--clippy_lints/src/transmute/mod.rs2
-rw-r--r--clippy_lints/src/undocumented_unsafe_blocks.rs4
-rw-r--r--clippy_lints/src/unused_self.rs2
-rw-r--r--clippy_lints/src/unwrap_in_result.rs2
-rw-r--r--clippy_lints/src/utils/author.rs2
-rw-r--r--clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs4
-rw-r--r--clippy_lints/src/utils/internal_lints/metadata_collector.rs2
-rw-r--r--clippy_lints/src/zero_repeat_side_effects.rs6
77 files changed, 978 insertions, 697 deletions
diff --git a/clippy_lints/src/attrs/maybe_misused_cfg.rs b/clippy_lints/src/attrs/maybe_misused_cfg.rs
deleted file mode 100644
index e6b2e835be8..00000000000
--- a/clippy_lints/src/attrs/maybe_misused_cfg.rs
+++ /dev/null
@@ -1,51 +0,0 @@
-use super::{Attribute, MAYBE_MISUSED_CFG};
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use rustc_ast::{MetaItemKind, NestedMetaItem};
-use rustc_errors::Applicability;
-use rustc_lint::EarlyContext;
-use rustc_span::sym;
-
-pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) {
-    if attr.has_name(sym::cfg)
-        && let Some(items) = attr.meta_item_list()
-    {
-        check_nested_misused_cfg(cx, &items);
-    }
-}
-
-fn check_nested_misused_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
-    for item in items {
-        if let NestedMetaItem::MetaItem(meta) = item {
-            if let Some(ident) = meta.ident()
-                && ident.name.as_str() == "features"
-                && let Some(val) = meta.value_str()
-            {
-                span_lint_and_sugg(
-                    cx,
-                    MAYBE_MISUSED_CFG,
-                    meta.span,
-                    "'feature' may be misspelled as 'features'",
-                    "did you mean",
-                    format!("feature = \"{val}\""),
-                    Applicability::MaybeIncorrect,
-                );
-            }
-            if let MetaItemKind::List(list) = &meta.kind {
-                check_nested_misused_cfg(cx, list);
-            // If this is not a list, then we check for `cfg(test)`.
-            } else if let Some(ident) = meta.ident()
-                && matches!(ident.name.as_str(), "tests" | "Test")
-            {
-                span_lint_and_sugg(
-                    cx,
-                    MAYBE_MISUSED_CFG,
-                    meta.span,
-                    format!("'test' may be misspelled as '{}'", ident.name.as_str()),
-                    "did you mean",
-                    "test".to_string(),
-                    Applicability::MaybeIncorrect,
-                );
-            }
-        }
-    }
-}
diff --git a/clippy_lints/src/attrs/mismatched_target_os.rs b/clippy_lints/src/attrs/mismatched_target_os.rs
deleted file mode 100644
index b1cc0a763c5..00000000000
--- a/clippy_lints/src/attrs/mismatched_target_os.rs
+++ /dev/null
@@ -1,90 +0,0 @@
-use super::{Attribute, MISMATCHED_TARGET_OS};
-use clippy_utils::diagnostics::span_lint_and_then;
-use rustc_ast::{MetaItemKind, NestedMetaItem};
-use rustc_errors::Applicability;
-use rustc_lint::EarlyContext;
-use rustc_span::{sym, Span};
-
-static UNIX_SYSTEMS: &[&str] = &[
-    "android",
-    "dragonfly",
-    "emscripten",
-    "freebsd",
-    "fuchsia",
-    "haiku",
-    "illumos",
-    "ios",
-    "l4re",
-    "linux",
-    "macos",
-    "netbsd",
-    "openbsd",
-    "redox",
-    "solaris",
-    "vxworks",
-];
-
-// NOTE: windows is excluded from the list because it's also a valid target family.
-static NON_UNIX_SYSTEMS: &[&str] = &["hermit", "none", "wasi"];
-
-pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) {
-    fn find_os(name: &str) -> Option<&'static str> {
-        UNIX_SYSTEMS
-            .iter()
-            .chain(NON_UNIX_SYSTEMS.iter())
-            .find(|&&os| os == name)
-            .copied()
-    }
-
-    fn is_unix(name: &str) -> bool {
-        UNIX_SYSTEMS.iter().any(|&os| os == name)
-    }
-
-    fn find_mismatched_target_os(items: &[NestedMetaItem]) -> Vec<(&str, Span)> {
-        let mut mismatched = Vec::new();
-
-        for item in items {
-            if let NestedMetaItem::MetaItem(meta) = item {
-                match &meta.kind {
-                    MetaItemKind::List(list) => {
-                        mismatched.extend(find_mismatched_target_os(list));
-                    },
-                    MetaItemKind::Word => {
-                        if let Some(ident) = meta.ident()
-                            && let Some(os) = find_os(ident.name.as_str())
-                        {
-                            mismatched.push((os, ident.span));
-                        }
-                    },
-                    MetaItemKind::NameValue(..) => {},
-                }
-            }
-        }
-
-        mismatched
-    }
-
-    if attr.has_name(sym::cfg)
-        && let Some(list) = attr.meta_item_list()
-        && let mismatched = find_mismatched_target_os(&list)
-        && !mismatched.is_empty()
-    {
-        let mess = "operating system used in target family position";
-
-        span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, mess, |diag| {
-            // Avoid showing the unix suggestion multiple times in case
-            // we have more than one mismatch for unix-like systems
-            let mut unix_suggested = false;
-
-            for (os, span) in mismatched {
-                let sugg = format!("target_os = \"{os}\"");
-                diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect);
-
-                if !unix_suggested && is_unix(os) {
-                    diag.help("did you mean `unix`?");
-                    unix_suggested = true;
-                }
-            }
-        });
-    }
-}
diff --git a/clippy_lints/src/attrs/mod.rs b/clippy_lints/src/attrs/mod.rs
index a24bd5ed44b..e4c98a32fd6 100644
--- a/clippy_lints/src/attrs/mod.rs
+++ b/clippy_lints/src/attrs/mod.rs
@@ -7,8 +7,6 @@ mod deprecated_semver;
 mod duplicated_attributes;
 mod empty_line_after;
 mod inline_always;
-mod maybe_misused_cfg;
-mod mismatched_target_os;
 mod mixed_attributes_style;
 mod non_minimal_cfg;
 mod should_panic_without_expect;
@@ -272,39 +270,6 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for cfg attributes having operating systems used in target family position.
-    ///
-    /// ### Why is this bad?
-    /// The configuration option will not be recognised and the related item will not be included
-    /// by the conditional compilation engine.
-    ///
-    /// ### Example
-    /// ```no_run
-    /// #[cfg(linux)]
-    /// fn conditional() { }
-    /// ```
-    ///
-    /// Use instead:
-    /// ```no_run
-    /// # mod hidden {
-    /// #[cfg(target_os = "linux")]
-    /// fn conditional() { }
-    /// # }
-    ///
-    /// // or
-    ///
-    /// #[cfg(unix)]
-    /// fn conditional() { }
-    /// ```
-    /// Check the [Rust Reference](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) for more details.
-    #[clippy::version = "1.45.0"]
-    pub MISMATCHED_TARGET_OS,
-    correctness,
-    "usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
     /// Checks for attributes that allow lints without a reason.
     ///
     /// (This requires the `lint_reasons` feature)
@@ -393,38 +358,6 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for `#[cfg(features = "...")]` and suggests to replace it with
-    /// `#[cfg(feature = "...")]`.
-    ///
-    /// It also checks if `cfg(test)` was misspelled.
-    ///
-    /// ### Why is this bad?
-    /// Misspelling `feature` as `features` or `test` as `tests` can be sometimes hard to spot. It
-    /// may cause conditional compilation not work quietly.
-    ///
-    /// ### Example
-    /// ```no_run
-    /// #[cfg(features = "some-feature")]
-    /// fn conditional() { }
-    /// #[cfg(tests)]
-    /// mod tests { }
-    /// ```
-    ///
-    /// Use instead:
-    /// ```no_run
-    /// #[cfg(feature = "some-feature")]
-    /// fn conditional() { }
-    /// #[cfg(test)]
-    /// mod tests { }
-    /// ```
-    #[clippy::version = "1.69.0"]
-    pub MAYBE_MISUSED_CFG,
-    suspicious,
-    "prevent from misusing the wrong attr name"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
     /// Checks for `#[cfg_attr(feature = "cargo-clippy", ...)]` and for
     /// `#[cfg(feature = "cargo-clippy")]` and suggests to replace it with
     /// `#[cfg_attr(clippy, ...)]` or `#[cfg(clippy)]`.
@@ -530,7 +463,7 @@ declare_clippy_lint! {
     /// #[allow(dead_code)]
     /// fn foo() {}
     /// ```
-    #[clippy::version = "1.78.0"]
+    #[clippy::version = "1.79.0"]
     pub DUPLICATED_ATTRIBUTES,
     suspicious,
     "duplicated attribute"
@@ -612,11 +545,9 @@ pub struct EarlyAttributes {
 
 impl_lint_pass!(EarlyAttributes => [
     DEPRECATED_CFG_ATTR,
-    MISMATCHED_TARGET_OS,
     EMPTY_LINE_AFTER_OUTER_ATTR,
     EMPTY_LINE_AFTER_DOC_COMMENTS,
     NON_MINIMAL_CFG,
-    MAYBE_MISUSED_CFG,
     DEPRECATED_CLIPPY_CFG_ATTR,
     UNNECESSARY_CLIPPY_CFG,
 ]);
@@ -629,9 +560,7 @@ impl EarlyLintPass for EarlyAttributes {
     fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
         deprecated_cfg_attr::check(cx, attr, &self.msrv);
         deprecated_cfg_attr::check_clippy(cx, attr);
-        mismatched_target_os::check(cx, attr);
         non_minimal_cfg::check(cx, attr);
-        maybe_misused_cfg::check(cx, attr);
     }
 
     extract_msrv_attr!(EarlyContext);
diff --git a/clippy_lints/src/blocks_in_conditions.rs b/clippy_lints/src/blocks_in_conditions.rs
index 171f3031860..eb05dc96cde 100644
--- a/clippy_lints/src/blocks_in_conditions.rs
+++ b/clippy_lints/src/blocks_in_conditions.rs
@@ -1,15 +1,11 @@
-use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_block_with_applicability;
-use clippy_utils::ty::implements_trait;
-use clippy_utils::visitors::{for_each_expr, Descend};
-use clippy_utils::{get_parent_expr, higher, is_from_proc_macro};
-use core::ops::ControlFlow;
+use clippy_utils::{higher, is_from_proc_macro};
 use rustc_errors::Applicability;
 use rustc_hir::{BlockCheckMode, Expr, ExprKind, MatchSource};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::declare_lint_pass;
-use rustc_span::sym;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -124,30 +120,6 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInConditions {
                     );
                 }
             }
-        } else {
-            let _: Option<!> = for_each_expr(cond, |e| {
-                if let ExprKind::Closure(closure) = e.kind {
-                    // do not lint if the closure is called using an iterator (see #1141)
-                    if let Some(parent) = get_parent_expr(cx, e)
-                        && let ExprKind::MethodCall(_, self_arg, _, _) = &parent.kind
-                        && let caller = cx.typeck_results().expr_ty(self_arg)
-                        && let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator)
-                        && implements_trait(cx, caller, iter_id, &[])
-                    {
-                        return ControlFlow::Continue(Descend::No);
-                    }
-
-                    let body = cx.tcx.hir().body(closure.body);
-                    let ex = &body.value;
-                    if let ExprKind::Block(block, _) = ex.kind {
-                        if !body.value.span.from_expansion() && !block.stmts.is_empty() {
-                            span_lint(cx, BLOCKS_IN_CONDITIONS, ex.span, complex_block_message.clone());
-                            return ControlFlow::Continue(Descend::No);
-                        }
-                    }
-                }
-                ControlFlow::Continue(Descend::Yes)
-            });
         }
     }
 }
diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs
index b6341b3fe8e..a1c6c0b608f 100644
--- a/clippy_lints/src/booleans.rs
+++ b/clippy_lints/src/booleans.rs
@@ -174,6 +174,25 @@ fn check_inverted_bool_in_condition(
     );
 }
 
+fn check_simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) {
+    if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind
+        && !expr.span.from_expansion()
+        && !inner.span.from_expansion()
+        && let Some(suggestion) = simplify_not(cx, inner)
+        && cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow
+    {
+        span_lint_and_sugg(
+            cx,
+            NONMINIMAL_BOOL,
+            expr.span,
+            "this boolean expression can be simplified",
+            "try",
+            suggestion,
+            Applicability::MachineApplicable,
+        );
+    }
+}
+
 struct NonminimalBoolVisitor<'a, 'tcx> {
     cx: &'a LateContext<'tcx>,
 }
@@ -232,6 +251,11 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
                 _ => (),
             }
         }
+
+        if self.cx.typeck_results().expr_ty(e).is_never() {
+            return Err("contains never type".to_owned());
+        }
+
         for (n, expr) in self.terminals.iter().enumerate() {
             if eq_expr_value(self.cx, e, expr) {
                 #[expect(clippy::cast_possible_truncation)]
@@ -542,8 +566,7 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
                 }
             };
             if improvements.is_empty() {
-                let mut visitor = NotSimplificationVisitor { cx: self.cx };
-                visitor.visit_expr(e);
+                check_simplify_not(self.cx, e);
             } else {
                 nonminimal_bool_lint(
                     improvements
@@ -586,30 +609,3 @@ fn implements_ord(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
         .get_diagnostic_item(sym::Ord)
         .map_or(false, |id| implements_trait(cx, ty, id, &[]))
 }
-
-struct NotSimplificationVisitor<'a, 'tcx> {
-    cx: &'a LateContext<'tcx>,
-}
-
-impl<'a, 'tcx> Visitor<'tcx> for NotSimplificationVisitor<'a, 'tcx> {
-    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
-        if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind
-            && !expr.span.from_expansion()
-            && !inner.span.from_expansion()
-            && let Some(suggestion) = simplify_not(self.cx, inner)
-            && self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow
-        {
-            span_lint_and_sugg(
-                self.cx,
-                NONMINIMAL_BOOL,
-                expr.span,
-                "this boolean expression can be simplified",
-                "try",
-                suggestion,
-                Applicability::MachineApplicable,
-            );
-        }
-
-        walk_expr(self, expr);
-    }
-}
diff --git a/clippy_lints/src/cargo/lint_groups_priority.rs b/clippy_lints/src/cargo/lint_groups_priority.rs
index 0d9eaac882f..e924542fea2 100644
--- a/clippy_lints/src/cargo/lint_groups_priority.rs
+++ b/clippy_lints/src/cargo/lint_groups_priority.rs
@@ -71,12 +71,6 @@ struct CargoToml {
     workspace: Workspace,
 }
 
-#[derive(Default, Debug)]
-struct LintsAndGroups {
-    lints: Vec<Spanned<String>>,
-    groups: Vec<(Spanned<String>, Spanned<LintConfig>)>,
-}
-
 fn toml_span(range: Range<usize>, file: &SourceFile) -> Span {
     Span::new(
         file.start_pos + BytePos::from_usize(range.start),
@@ -86,27 +80,28 @@ fn toml_span(range: Range<usize>, file: &SourceFile) -> Span {
     )
 }
 
-fn check_table(cx: &LateContext<'_>, table: LintTable, groups: &FxHashSet<&str>, file: &SourceFile) {
-    let mut by_priority = BTreeMap::<_, LintsAndGroups>::new();
+fn check_table(cx: &LateContext<'_>, table: LintTable, known_groups: &FxHashSet<&str>, file: &SourceFile) {
+    let mut lints = Vec::new();
+    let mut groups = Vec::new();
     for (name, config) in table {
-        let lints_and_groups = by_priority.entry(config.as_ref().priority()).or_default();
-        if groups.contains(name.get_ref().as_str()) {
-            lints_and_groups.groups.push((name, config));
+        if name.get_ref() == "warnings" {
+            continue;
+        }
+
+        if known_groups.contains(name.get_ref().as_str()) {
+            groups.push((name, config));
         } else {
-            lints_and_groups.lints.push(name);
+            lints.push((name, config.into_inner()));
         }
     }
-    let low_priority = by_priority
-        .iter()
-        .find(|(_, lints_and_groups)| !lints_and_groups.lints.is_empty())
-        .map_or(-1, |(&lowest_lint_priority, _)| lowest_lint_priority - 1);
 
-    for (priority, LintsAndGroups { lints, groups }) in by_priority {
-        let Some(last_lint_alphabetically) = lints.last() else {
-            continue;
-        };
-
-        for (group, config) in groups {
+    for (group, group_config) in groups {
+        let priority = group_config.get_ref().priority();
+        let level = group_config.get_ref().level();
+        if let Some((conflict, _)) = lints
+            .iter()
+            .rfind(|(_, lint_config)| lint_config.priority() == priority && lint_config.level() != level)
+        {
             span_lint_and_then(
                 cx,
                 LINT_GROUPS_PRIORITY,
@@ -116,22 +111,23 @@ fn check_table(cx: &LateContext<'_>, table: LintTable, groups: &FxHashSet<&str>,
                     group.as_ref()
                 ),
                 |diag| {
-                    let config_span = toml_span(config.span(), file);
-                    if config.as_ref().is_implicit() {
+                    let config_span = toml_span(group_config.span(), file);
+
+                    if group_config.as_ref().is_implicit() {
                         diag.span_label(config_span, "has an implicit priority of 0");
                     }
-                    // add the label to next lint after this group that has the same priority
-                    let lint = lints
-                        .iter()
-                        .filter(|lint| lint.span().start > group.span().start)
-                        .min_by_key(|lint| lint.span().start)
-                        .unwrap_or(last_lint_alphabetically);
-                    diag.span_label(toml_span(lint.span(), file), "has the same priority as this lint");
+                    diag.span_label(toml_span(conflict.span(), file), "has the same priority as this lint");
                     diag.note("the order of the lints in the table is ignored by Cargo");
+
                     let mut suggestion = String::new();
+                    let low_priority = lints
+                        .iter()
+                        .map(|(_, config)| config.priority().saturating_sub(1))
+                        .min()
+                        .unwrap_or(-1);
                     Serialize::serialize(
                         &LintConfigTable {
-                            level: config.as_ref().level().into(),
+                            level: level.into(),
                             priority: Some(low_priority),
                         },
                         toml::ser::ValueSerializer::new(&mut suggestion),
diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs
index 864489ee3fc..8bbd41b0db1 100644
--- a/clippy_lints/src/casts/cast_sign_loss.rs
+++ b/clippy_lints/src/casts/cast_sign_loss.rs
@@ -3,7 +3,7 @@ use std::ops::ControlFlow;
 
 use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::visitors::{for_each_expr, Descend};
+use clippy_utils::visitors::{for_each_expr_without_closures, Descend};
 use clippy_utils::{method_chain_args, sext};
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::LateContext;
@@ -266,7 +266,7 @@ fn expr_add_sign(cx: &LateContext<'_>, expr: &Expr<'_>) -> Sign {
 fn exprs_with_muldiv_binop_peeled<'e>(expr: &'e Expr<'_>) -> Vec<&'e Expr<'e>> {
     let mut res = vec![];
 
-    for_each_expr(expr, |sub_expr| -> ControlFlow<Infallible, Descend> {
+    for_each_expr_without_closures(expr, |sub_expr| -> ControlFlow<Infallible, Descend> {
         // We don't check for mul/div/rem methods here, but we could.
         if let ExprKind::Binary(op, lhs, _rhs) = sub_expr.kind {
             if matches!(op.node, BinOpKind::Mul | BinOpKind::Div) {
@@ -315,7 +315,7 @@ fn exprs_with_muldiv_binop_peeled<'e>(expr: &'e Expr<'_>) -> Vec<&'e Expr<'e>> {
 fn exprs_with_add_binop_peeled<'e>(expr: &'e Expr<'_>) -> Vec<&'e Expr<'e>> {
     let mut res = vec![];
 
-    for_each_expr(expr, |sub_expr| -> ControlFlow<Infallible, Descend> {
+    for_each_expr_without_closures(expr, |sub_expr| -> ControlFlow<Infallible, Descend> {
         // We don't check for add methods here, but we could.
         if let ExprKind::Binary(op, _lhs, _rhs) = sub_expr.kind {
             if matches!(op.node, BinOpKind::Add) {
diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs
index a7f7bf7854e..fb0b0cba6a6 100644
--- a/clippy_lints/src/casts/unnecessary_cast.rs
+++ b/clippy_lints/src/casts/unnecessary_cast.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::numeric_literal::NumericLiteral;
 use clippy_utils::source::snippet_opt;
-use clippy_utils::visitors::{for_each_expr, Visitable};
+use clippy_utils::visitors::{for_each_expr_without_closures, Visitable};
 use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local};
 use rustc_ast::{LitFloatType, LitIntType, LitKind};
 use rustc_errors::Applicability;
@@ -245,7 +245,7 @@ fn fp_ty_mantissa_nbits(typ: Ty<'_>) -> u32 {
 /// TODO: Maybe we should move this to `clippy_utils` so others won't need to go down this dark,
 /// dark path reimplementing this (or something similar).
 fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx>, cast_from: Ty<'tcx>) -> bool {
-    for_each_expr(expr, |expr| {
+    for_each_expr_without_closures(expr, |expr| {
         // Calls are a `Path`, and usage of locals are a `Path`. So, this checks
         // - call() as i32
         // - local as i32
diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs
index ee1bb63b50d..e41abf42234 100644
--- a/clippy_lints/src/cognitive_complexity.rs
+++ b/clippy_lints/src/cognitive_complexity.rs
@@ -3,7 +3,7 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::visitors::for_each_expr;
+use clippy_utils::visitors::for_each_expr_without_closures;
 use clippy_utils::{get_async_fn_body, is_async_fn, LimitStack};
 use core::ops::ControlFlow;
 use rustc_ast::ast::Attribute;
@@ -65,7 +65,7 @@ impl CognitiveComplexity {
 
         let mut cc = 1u64;
         let mut returns = 0u64;
-        let _: Option<!> = for_each_expr(expr, |e| {
+        let _: Option<!> = for_each_expr_without_closures(expr, |e| {
             match e.kind {
                 ExprKind::If(_, _, _) => {
                     cc += 1;
diff --git a/clippy_lints/src/collection_is_never_read.rs b/clippy_lints/src/collection_is_never_read.rs
index 70856b80881..28d9f68d504 100644
--- a/clippy_lints/src/collection_is_never_read.rs
+++ b/clippy_lints/src/collection_is_never_read.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
-use clippy_utils::visitors::{for_each_expr_with_closures, Visitable};
+use clippy_utils::visitors::{for_each_expr, Visitable};
 use clippy_utils::{get_enclosing_block, path_to_local_id};
 use core::ops::ControlFlow;
 use rustc_hir::{Body, ExprKind, HirId, LangItem, LetStmt, Node, PatKind};
@@ -82,7 +82,7 @@ fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirI
     let mut has_read_access = false;
 
     // Inspect all expressions and sub-expressions in the block.
-    for_each_expr_with_closures(cx, block, |expr| {
+    for_each_expr(cx, block, |expr| {
         // Ignore expressions that are not simply `id`.
         if !path_to_local_id(expr, id) {
             return ControlFlow::Continue(());
diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs
index ccf1d9d6f8c..480df675d75 100644
--- a/clippy_lints/src/copies.rs
+++ b/clippy_lints/src/copies.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
 use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt};
 use clippy_utils::ty::{needs_ordered_drop, InteriorMut};
-use clippy_utils::visitors::for_each_expr;
+use clippy_utils::visitors::for_each_expr_without_closures;
 use clippy_utils::{
     capture_local_usage, eq_expr_value, find_binding_init, get_enclosing_block, hash_expr, hash_stmt, if_sequence,
     is_else_clause, is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq,
@@ -362,7 +362,7 @@ fn eq_binding_names(s: &Stmt<'_>, names: &[(HirId, Symbol)]) -> bool {
 
 /// Checks if the statement modifies or moves any of the given locals.
 fn modifies_any_local<'tcx>(cx: &LateContext<'tcx>, s: &'tcx Stmt<'_>, locals: &HirIdSet) -> bool {
-    for_each_expr(s, |e| {
+    for_each_expr_without_closures(s, |e| {
         if let Some(id) = path_to_local(e)
             && locals.contains(&id)
             && !capture_local_usage(cx, e).is_imm_ref()
@@ -413,7 +413,7 @@ fn scan_block_for_eq<'tcx>(
 
     let mut cond_locals = HirIdSet::default();
     for &cond in conds {
-        let _: Option<!> = for_each_expr(cond, |e| {
+        let _: Option<!> = for_each_expr_without_closures(cond, |e| {
             if let Some(id) = path_to_local(e) {
                 cond_locals.insert(id);
             }
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index df2ef465700..7e43a99e9f2 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -58,8 +58,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::attrs::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO,
     crate::attrs::EMPTY_LINE_AFTER_OUTER_ATTR_INFO,
     crate::attrs::INLINE_ALWAYS_INFO,
-    crate::attrs::MAYBE_MISUSED_CFG_INFO,
-    crate::attrs::MISMATCHED_TARGET_OS_INFO,
     crate::attrs::MIXED_ATTRIBUTES_STYLE_INFO,
     crate::attrs::NON_MINIMAL_CFG_INFO,
     crate::attrs::SHOULD_PANIC_WITHOUT_EXPECT_INFO,
@@ -419,6 +417,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::methods::MAP_UNWRAP_OR_INFO,
     crate::methods::MUT_MUTEX_LOCK_INFO,
     crate::methods::NAIVE_BYTECOUNT_INFO,
+    crate::methods::NEEDLESS_CHARACTER_ITERATION_INFO,
     crate::methods::NEEDLESS_COLLECT_INFO,
     crate::methods::NEEDLESS_OPTION_AS_DEREF_INFO,
     crate::methods::NEEDLESS_OPTION_TAKE_INFO,
@@ -449,7 +448,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::methods::SEEK_TO_START_INSTEAD_OF_REWIND_INFO,
     crate::methods::SHOULD_IMPLEMENT_TRAIT_INFO,
     crate::methods::SINGLE_CHAR_ADD_STR_INFO,
-    crate::methods::SINGLE_CHAR_PATTERN_INFO,
     crate::methods::SKIP_WHILE_NEXT_INFO,
     crate::methods::STABLE_SORT_PRIMITIVE_INFO,
     crate::methods::STRING_EXTEND_CHARS_INFO,
@@ -531,6 +529,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::needless_for_each::NEEDLESS_FOR_EACH_INFO,
     crate::needless_if::NEEDLESS_IF_INFO,
     crate::needless_late_init::NEEDLESS_LATE_INIT_INFO,
+    crate::needless_maybe_sized::NEEDLESS_MAYBE_SIZED_INFO,
     crate::needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS_INFO,
     crate::needless_pass_by_ref_mut::NEEDLESS_PASS_BY_REF_MUT_INFO,
     crate::needless_pass_by_value::NEEDLESS_PASS_BY_VALUE_INFO,
@@ -656,6 +655,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::std_instead_of_core::ALLOC_INSTEAD_OF_CORE_INFO,
     crate::std_instead_of_core::STD_INSTEAD_OF_ALLOC_INFO,
     crate::std_instead_of_core::STD_INSTEAD_OF_CORE_INFO,
+    crate::string_patterns::MANUAL_PATTERN_CHAR_COMPARISON_INFO,
+    crate::string_patterns::SINGLE_CHAR_PATTERN_INFO,
     crate::strings::STRING_ADD_INFO,
     crate::strings::STRING_ADD_ASSIGN_INFO,
     crate::strings::STRING_FROM_UTF8_AS_BYTES_INFO,
diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs
index 9aa5af3190f..a0900f46f6a 100644
--- a/clippy_lints/src/deprecated_lints.rs
+++ b/clippy_lints/src/deprecated_lints.rs
@@ -215,3 +215,29 @@ declare_deprecated_lint! {
     pub WRONG_PUB_SELF_CONVENTION,
     "set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items"
 }
+
+declare_deprecated_lint! {
+    /// ### What it does
+    /// Nothing. This lint has been deprecated.
+    ///
+    /// ### Deprecation reason
+    /// This lint has been superseded by rustc's own [`unexpected_cfgs`] lint that is able to detect the `#[cfg(features)]` and `#[cfg(tests)]` typos.
+    ///
+    /// [`unexpected_cfgs`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#unexpected-cfgs
+    #[clippy::version = "1.80.0"]
+    pub MAYBE_MISUSED_CFG,
+    "this lint has been replaced by `unexpected_cfgs`"
+}
+
+declare_deprecated_lint! {
+    /// ### What it does
+    /// Nothing. This lint has been deprecated.
+    ///
+    /// ### Deprecation reason
+    /// This lint has been superseded by rustc's own [`unexpected_cfgs`] lint that is able to detect invalid `#[cfg(linux)]` attributes.
+    ///
+    /// [`unexpected_cfgs`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#unexpected-cfgs
+    #[clippy::version = "1.80.0"]
+    pub MISMATCHED_TARGET_OS,
+    "this lint has been replaced by `unexpected_cfgs`"
+}
diff --git a/clippy_lints/src/doc/missing_headers.rs b/clippy_lints/src/doc/missing_headers.rs
index 010fab803d9..e3d74840726 100644
--- a/clippy_lints/src/doc/missing_headers.rs
+++ b/clippy_lints/src/doc/missing_headers.rs
@@ -37,7 +37,7 @@ pub fn check(
             cx,
             MISSING_SAFETY_DOC,
             span,
-            "unsafe function's docs miss `# Safety` section",
+            "unsafe function's docs are missing a `# Safety` section",
         ),
         (true, Safety::Safe) => span_lint(
             cx,
diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs
index 48c4c4206fe..42ec2c00823 100644
--- a/clippy_lints/src/eta_reduction.rs
+++ b/clippy_lints/src/eta_reduction.rs
@@ -9,8 +9,8 @@ use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, Saf
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{
-    self, Binder, ClosureArgs, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, RegionKind, Ty,
-    TypeVisitableExt, TypeckResults, TyCtxt,
+    self, Binder, ClosureArgs, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, RegionKind, Ty, TyCtxt,
+    TypeVisitableExt, TypeckResults,
 };
 use rustc_session::declare_lint_pass;
 use rustc_span::symbol::sym;
@@ -123,7 +123,8 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
                     ExprKind::Path(QPath::Resolved(..) | QPath::TypeRelative(..))
                 ) =>
             {
-                let callee_ty = typeck.expr_ty(callee).peel_refs();
+                let callee_ty_raw = typeck.expr_ty(callee);
+                let callee_ty = callee_ty_raw.peel_refs();
                 if matches!(type_diagnostic_name(cx, callee_ty), Some(sym::Arc | sym::Rc))
                     || !check_inputs(typeck, body.params, None, args)
                 {
@@ -170,15 +171,25 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
                 {
                     span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
                         if let Some(mut snippet) = snippet_opt(cx, callee.span) {
-                            if let Ok((ClosureKind::FnMut, _)) = cx.tcx.infer_ctxt().build().type_implements_fn_trait(
-                                cx.param_env,
-                                Binder::bind_with_vars(callee_ty_adjusted, List::empty()),
-                                ty::PredicatePolarity::Positive,
-                            ) && path_to_local(callee).map_or(false, |l| {
+                            if path_to_local(callee).map_or(false, |l| {
+                                // FIXME: Do we really need this `local_used_in` check?
+                                // Isn't it checking something like... `callee(callee)`?
+                                // If somehow this check is needed, add some test for it,
+                                // 'cuz currently nothing changes after deleting this check.
                                 local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr)
                             }) {
-                                // Mutable closure is used after current expr; we cannot consume it.
-                                snippet = format!("&mut {snippet}");
+                                match cx.tcx.infer_ctxt().build().type_implements_fn_trait(
+                                    cx.param_env,
+                                    Binder::bind_with_vars(callee_ty_adjusted, List::empty()),
+                                    ty::PredicatePolarity::Positive,
+                                ) {
+                                    // Mutable closure is used after current expr; we cannot consume it.
+                                    Ok((ClosureKind::FnMut, _)) => snippet = format!("&mut {snippet}"),
+                                    Ok((ClosureKind::Fn, _)) if !callee_ty_raw.is_ref() => {
+                                        snippet = format!("&{snippet}");
+                                    },
+                                    _ => (),
+                                }
                             }
                             diag.span_suggestion(
                                 expr.span,
diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs
index 4ec9bd757ff..4d301daabe4 100644
--- a/clippy_lints/src/float_literal.rs
+++ b/clippy_lints/src/float_literal.rs
@@ -103,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
                 return;
             }
 
-            if is_whole && !sym_str.contains(|c| c == 'e' || c == 'E') {
+            if is_whole && !sym_str.contains(['e', 'E']) {
                 // Normalize the literal by stripping the fractional portion
                 if sym_str.split('.').next().unwrap() != float_str {
                     // If the type suffix is missing the suggestion would be
diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs
index 46d47e217b0..68bdf88d0a7 100644
--- a/clippy_lints/src/floating_point_arithmetic.rs
+++ b/clippy_lints/src/floating_point_arithmetic.rs
@@ -2,7 +2,8 @@ use clippy_utils::consts::Constant::{Int, F32, F64};
 use clippy_utils::consts::{constant, constant_simple, Constant};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::{
-    eq_expr_value, get_parent_expr, higher, in_constant, is_no_std_crate, numeric_literal, peel_blocks, sugg,
+    eq_expr_value, get_parent_expr, higher, in_constant, is_inherent_method_call, is_no_std_crate, numeric_literal,
+    peel_blocks, sugg,
 };
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
@@ -759,7 +760,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
         if let ExprKind::MethodCall(path, receiver, args, _) = &expr.kind {
             let recv_ty = cx.typeck_results().expr_ty(receiver);
 
-            if recv_ty.is_floating_point() && !is_no_std_crate(cx) {
+            if recv_ty.is_floating_point() && !is_no_std_crate(cx) && is_inherent_method_call(cx, expr) {
                 match path.ident.name.as_str() {
                     "ln" => check_ln1p(cx, expr, receiver),
                     "log" => check_log_base(cx, expr, receiver, args),
diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs
index 86115807aa4..99def199af0 100644
--- a/clippy_lints/src/format_args.rs
+++ b/clippy_lints/src/format_args.rs
@@ -424,14 +424,14 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> {
                 count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter())
             && implements_trait(cx, target, display_trait_id, &[])
             && let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait()
-            && let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
+            && let Some(receiver_snippet) = snippet_opt(cx, receiver.span.source_callsite())
         {
             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,
-                    to_string_span.with_lo(receiver.span.hi()),
+                    to_string_span.with_lo(receiver.span.source_callsite().hi()),
                     format!("`to_string` applied to a type that implements `Display` in `{name}!` args"),
                     "remove this",
                     String::new(),
diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs
index 1933a00891b..93527bcdf5c 100644
--- a/clippy_lints/src/from_over_into.rs
+++ b/clippy_lints/src/from_over_into.rs
@@ -181,6 +181,9 @@ fn convert_to_from(
     let from = snippet_opt(cx, self_ty.span)?;
     let into = snippet_opt(cx, target_ty.span)?;
 
+    let return_type = matches!(sig.decl.output, FnRetTy::Return(_))
+        .then_some(String::from("Self"))
+        .unwrap_or_default();
     let mut suggestions = vec![
         // impl Into<T> for U  ->  impl From<T> for U
         //      ~~~~                    ~~~~
@@ -197,13 +200,10 @@ fn convert_to_from(
         // fn into([mut] self) -> T  ->  fn into([mut] v: T) -> T
         //               ~~~~                          ~~~~
         (self_ident.span, format!("val: {from}")),
-    ];
-
-    if let FnRetTy::Return(_) = sig.decl.output {
         // fn into(self) -> T  ->  fn into(self) -> Self
         //                  ~                       ~~~~
-        suggestions.push((sig.decl.output.span(), String::from("Self")));
-    }
+        (sig.decl.output.span(), return_type),
+    ];
 
     let mut finder = SelfFinder {
         cx,
diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs
index e7ec2b3151e..cce8617821e 100644
--- a/clippy_lints/src/functions/must_use.rs
+++ b/clippy_lints/src/functions/must_use.rs
@@ -14,7 +14,7 @@ use clippy_utils::attrs::is_proc_macro;
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::is_must_use_ty;
-use clippy_utils::visitors::for_each_expr;
+use clippy_utils::visitors::for_each_expr_without_closures;
 use clippy_utils::{return_ty, trait_ref_of_method};
 
 use core::ops::ControlFlow;
@@ -226,7 +226,7 @@ fn is_mutated_static(e: &hir::Expr<'_>) -> bool {
 }
 
 fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bool {
-    for_each_expr(body.value, |e| {
+    for_each_expr_without_closures(body.value, |e| {
         use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall};
 
         match e.kind {
diff --git a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs
index b44a5f20ef6..1aeefe73cf6 100644
--- a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs
+++ b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs
@@ -5,7 +5,7 @@ use rustc_span::def_id::LocalDefId;
 
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::ty::type_is_unsafe_function;
-use clippy_utils::visitors::for_each_expr_with_closures;
+use clippy_utils::visitors::for_each_expr;
 use clippy_utils::{iter_input_pats, path_to_local};
 
 use core::ops::ControlFlow;
@@ -49,7 +49,7 @@ fn check_raw_ptr<'tcx>(
 
         if !raw_ptrs.is_empty() {
             let typeck = cx.tcx.typeck_body(body.id());
-            let _: Option<!> = for_each_expr_with_closures(cx, body.value, |e| {
+            let _: Option<!> = for_each_expr(cx, body.value, |e| {
                 match e.kind {
                     hir::ExprKind::Call(f, args) if type_is_unsafe_function(cx, typeck.expr_ty(f)) => {
                         for arg in args {
diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs
index cc342007ec6..2f543781c44 100644
--- a/clippy_lints/src/implicit_return.rs
+++ b/clippy_lints/src/implicit_return.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context};
-use clippy_utils::visitors::for_each_expr;
+use clippy_utils::visitors::for_each_expr_without_closures;
 use clippy_utils::{get_async_fn_body, is_async_fn};
 use core::ops::ControlFlow;
 use rustc_errors::Applicability;
@@ -153,7 +153,7 @@ fn lint_implicit_returns(
 
         ExprKind::Loop(block, ..) => {
             let mut add_return = false;
-            let _: Option<!> = for_each_expr(block, |e| {
+            let _: Option<!> = for_each_expr_without_closures(block, |e| {
                 if let ExprKind::Break(dest, sub_expr) = e.kind {
                     if dest.target_id.ok() == Some(expr.hir_id) {
                         if call_site_span.is_none() && e.span.ctxt() == ctxt {
diff --git a/clippy_lints/src/implied_bounds_in_impls.rs b/clippy_lints/src/implied_bounds_in_impls.rs
index 2b389d4f9b1..170ecf896b4 100644
--- a/clippy_lints/src/implied_bounds_in_impls.rs
+++ b/clippy_lints/src/implied_bounds_in_impls.rs
@@ -3,8 +3,8 @@ use clippy_utils::source::snippet;
 use rustc_errors::{Applicability, SuggestionStyle};
 use rustc_hir::def_id::DefId;
 use rustc_hir::{
-    GenericArg, GenericBound, GenericBounds, ItemKind, PredicateOrigin, TraitBoundModifier, TyKind, AssocItemConstraint,
-    WherePredicate,
+    AssocItemConstraint, GenericArg, GenericBound, GenericBounds, ItemKind, PredicateOrigin, TraitBoundModifier,
+    TyKind, WherePredicate,
 };
 use rustc_hir_analysis::lower_ty;
 use rustc_lint::{LateContext, LateLintPass};
@@ -83,8 +83,8 @@ fn emit_lint(
 
             let mut sugg = vec![(implied_span_extended, String::new())];
 
-            // We also might need to include associated item constraints that were specified in the implied bound,
-            // but omitted in the implied-by bound:
+            // We also might need to include associated item constraints that were specified in the implied
+            // bound, but omitted in the implied-by bound:
             // `fn f() -> impl Deref<Target = u8> + DerefMut`
             // If we're going to suggest removing `Deref<..>`, we'll need to put `<Target = u8>` on `DerefMut`
             let omitted_constraints: Vec<_> = implied_constraints
diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs
index e3e79749bea..d54f2af65cd 100644
--- a/clippy_lints/src/indexing_slicing.rs
+++ b/clippy_lints/src/indexing_slicing.rs
@@ -2,12 +2,14 @@
 
 use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
-use clippy_utils::higher;
+use clippy_utils::ty::{deref_chain, get_adt_inherent_method};
+use clippy_utils::{higher, is_from_proc_macro};
 use rustc_ast::ast::RangeLimits;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
+use rustc_middle::ty::{self, Ty};
 use rustc_session::impl_lint_pass;
+use rustc_span::sym;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -100,11 +102,21 @@ impl IndexingSlicing {
 
 impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if self.suppress_restriction_lint_in_const && cx.tcx.hir().is_inside_const_context(expr.hir_id) {
+        if (self.suppress_restriction_lint_in_const && cx.tcx.hir().is_inside_const_context(expr.hir_id))
+            || is_from_proc_macro(cx, expr)
+        {
             return;
         }
 
-        if let ExprKind::Index(array, index, _) = &expr.kind {
+        if let ExprKind::Index(array, index, _) = &expr.kind
+            && let expr_ty = cx.typeck_results().expr_ty(array)
+            && let mut deref = deref_chain(cx, expr_ty)
+            && deref.any(|l| {
+                l.peel_refs().is_slice()
+                    || l.peel_refs().is_array()
+                    || ty_has_applicable_get_function(cx, l.peel_refs(), expr_ty, expr)
+            })
+        {
             let note = "the suggestion might not be applicable in constant blocks";
             let ty = cx.typeck_results().expr_ty(array).peel_refs();
             if let Some(range) = higher::Range::hir(index) {
@@ -231,3 +243,33 @@ fn to_const_range(cx: &LateContext<'_>, range: higher::Range<'_>, array_size: u1
 
     (start, end)
 }
+
+/// Checks if the output Ty of the `get` method on this Ty (if any) matches the Ty returned by the
+/// indexing operation (if any).
+fn ty_has_applicable_get_function<'tcx>(
+    cx: &LateContext<'tcx>,
+    ty: Ty<'tcx>,
+    array_ty: Ty<'tcx>,
+    index_expr: &Expr<'_>,
+) -> bool {
+    if let ty::Adt(_, _) = array_ty.kind()
+        && let Some(get_output_ty) = get_adt_inherent_method(cx, ty, sym!(get)).map(|m| {
+            cx.tcx
+                .fn_sig(m.def_id)
+                .skip_binder()
+                .output()
+                .skip_binder()
+        })
+        && let ty::Adt(def, args) = get_output_ty.kind()
+        && cx.tcx.is_diagnostic_item(sym::Option, def.0.did)
+        && let Some(option_generic_param) = args.first()
+        && let generic_ty = option_generic_param.expect_ty().peel_refs()
+        // FIXME: ideally this would handle type params and projections properly, for now just assume it's the same type
+        && (cx.typeck_results().expr_ty(index_expr).peel_refs() == generic_ty.peel_refs()
+            || matches!(generic_ty.peel_refs().kind(), ty::Param(_) | ty::Alias(_, _)))
+    {
+        true
+    } else {
+        false
+    }
+}
diff --git a/clippy_lints/src/integer_division_remainder_used.rs b/clippy_lints/src/integer_division_remainder_used.rs
index cf598d5045e..a1215491b48 100644
--- a/clippy_lints/src/integer_division_remainder_used.rs
+++ b/clippy_lints/src/integer_division_remainder_used.rs
@@ -22,7 +22,7 @@ declare_clippy_lint! {
     /// ```no_run
     /// let my_div = 10 >> 1;
     /// ```
-    #[clippy::version = "1.78.0"]
+    #[clippy::version = "1.79.0"]
     pub INTEGER_DIVISION_REMAINDER_USED,
     restriction,
     "use of disallowed default division and remainder operations"
diff --git a/clippy_lints/src/iter_without_into_iter.rs b/clippy_lints/src/iter_without_into_iter.rs
index 601d0e151aa..6b03f2597b0 100644
--- a/clippy_lints/src/iter_without_into_iter.rs
+++ b/clippy_lints/src/iter_without_into_iter.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::get_parent_as_impl;
 use clippy_utils::source::snippet;
-use clippy_utils::ty::{implements_trait, make_normalized_projection};
+use clippy_utils::ty::{deref_chain, get_adt_inherent_method, implements_trait, make_normalized_projection};
 use rustc_ast::Mutability;
 use rustc_errors::Applicability;
 use rustc_hir::{FnRetTy, ImplItemKind, ImplicitSelfKind, ItemKind, TyKind};
@@ -9,8 +9,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, Ty};
 use rustc_session::declare_lint_pass;
-use rustc_span::{sym, Symbol};
-use std::iter;
+use rustc_span::sym;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -124,33 +123,6 @@ fn is_ty_exported(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
         .is_some_and(|did| cx.effective_visibilities.is_exported(did))
 }
 
-/// Returns the deref chain of a type, starting with the type itself.
-fn deref_chain<'cx, 'tcx>(cx: &'cx LateContext<'tcx>, ty: Ty<'tcx>) -> impl Iterator<Item = Ty<'tcx>> + 'cx {
-    iter::successors(Some(ty), |&ty| {
-        if let Some(deref_did) = cx.tcx.lang_items().deref_trait()
-            && implements_trait(cx, ty, deref_did, &[])
-        {
-            make_normalized_projection(cx.tcx, cx.param_env, deref_did, sym::Target, [ty])
-        } else {
-            None
-        }
-    })
-}
-
-fn adt_has_inherent_method(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool {
-    if let Some(ty_did) = ty.ty_adt_def().map(ty::AdtDef::did) {
-        cx.tcx.inherent_impls(ty_did).into_iter().flatten().any(|&did| {
-            cx.tcx
-                .associated_items(did)
-                .filter_by_name_unhygienic(method_name)
-                .next()
-                .is_some_and(|item| item.kind == ty::AssocKind::Fn)
-        })
-    } else {
-        false
-    }
-}
-
 impl LateLintPass<'_> for IterWithoutIntoIter {
     fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) {
         if !in_external_macro(cx.sess(), item.span)
@@ -167,7 +139,7 @@ impl LateLintPass<'_> for IterWithoutIntoIter {
             }
             && !deref_chain(cx, ty).any(|ty| {
                 // We can't check inherent impls for slices, but we know that they have an `iter(_mut)` method
-                ty.peel_refs().is_slice() || adt_has_inherent_method(cx, ty, expected_method_name)
+                ty.peel_refs().is_slice() || get_adt_inherent_method(cx, ty, expected_method_name).is_some()
             })
             && let Some(iter_assoc_span) = imp.items.iter().find_map(|item| {
                 if item.ident.name == sym!(IntoIter) {
diff --git a/clippy_lints/src/legacy_numeric_constants.rs b/clippy_lints/src/legacy_numeric_constants.rs
index 00124dcdd91..eadfeb6e341 100644
--- a/clippy_lints/src/legacy_numeric_constants.rs
+++ b/clippy_lints/src/legacy_numeric_constants.rs
@@ -28,7 +28,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let eps = f32::EPSILON;
     /// ```
-    #[clippy::version = "1.72.0"]
+    #[clippy::version = "1.79.0"]
     pub LEGACY_NUMERIC_CONSTANTS,
     style,
     "checks for usage of legacy std numeric constants and methods"
diff --git a/clippy_lints/src/lib.deprecated.rs b/clippy_lints/src/lib.deprecated.rs
index 80bde1b1138..0d21261822d 100644
--- a/clippy_lints/src/lib.deprecated.rs
+++ b/clippy_lints/src/lib.deprecated.rs
@@ -67,4 +67,12 @@
         "clippy::wrong_pub_self_convention",
         "set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items",
     );
+    store.register_removed(
+        "clippy::maybe_misused_cfg",
+        "this lint has been replaced by `unexpected_cfgs`",
+    );
+    store.register_removed(
+        "clippy::mismatched_target_os",
+        "this lint has been replaced by `unexpected_cfgs`",
+    );
 }
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 3328d642bd8..c65581d5203 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -251,6 +251,7 @@ mod needless_else;
 mod needless_for_each;
 mod needless_if;
 mod needless_late_init;
+mod needless_maybe_sized;
 mod needless_parens_on_range_literals;
 mod needless_pass_by_ref_mut;
 mod needless_pass_by_value;
@@ -325,6 +326,7 @@ mod size_of_in_element_count;
 mod size_of_ref;
 mod slow_vector_initialization;
 mod std_instead_of_core;
+mod string_patterns;
 mod strings;
 mod strlen_on_c_strings;
 mod suspicious_operation_groupings;
@@ -1058,6 +1060,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     store.register_late_pass(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi));
     store.register_late_pass(|_| Box::new(collection_is_never_read::CollectionIsNeverRead));
     store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage));
+    store.register_late_pass(|_| Box::new(needless_maybe_sized::NeedlessMaybeSized));
     store.register_late_pass(|_| Box::new(redundant_async_block::RedundantAsyncBlock));
     store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped));
     store.register_late_pass(|_| Box::new(allow_attributes::AllowAttribute));
@@ -1165,6 +1168,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
             ..Default::default()
         })
     });
+    store.register_late_pass(|_| Box::new(string_patterns::StringPatterns));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/clippy_lints/src/manual_unwrap_or_default.rs b/clippy_lints/src/manual_unwrap_or_default.rs
index 84fb183e3f7..17399fb2cc2 100644
--- a/clippy_lints/src/manual_unwrap_or_default.rs
+++ b/clippy_lints/src/manual_unwrap_or_default.rs
@@ -43,7 +43,7 @@ declare_clippy_lint! {
     /// let x: Option<Vec<String>> = Some(Vec::new());
     /// let y: Vec<String> = x.unwrap_or_default();
     /// ```
-    #[clippy::version = "1.78.0"]
+    #[clippy::version = "1.79.0"]
     pub MANUAL_UNWRAP_OR_DEFAULT,
     suspicious,
     "check if a `match` or `if let` can be simplified with `unwrap_or_default`"
@@ -57,7 +57,8 @@ fn get_some<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<HirId> {
         // Since it comes from a pattern binding, we need to get the parent to actually match
         // against it.
         && let Some(def_id) = cx.tcx.opt_parent(def_id)
-        && cx.tcx.lang_items().get(LangItem::OptionSome) == Some(def_id)
+        && (cx.tcx.lang_items().get(LangItem::OptionSome) == Some(def_id)
+        || cx.tcx.lang_items().get(LangItem::ResultOk) == Some(def_id))
     {
         let mut bindings = Vec::new();
         pat.each_binding(|_, id, _, _| bindings.push(id));
@@ -80,6 +81,14 @@ fn get_none<'tcx>(cx: &LateContext<'tcx>, arm: &Arm<'tcx>) -> Option<&'tcx Expr<
         && cx.tcx.lang_items().get(LangItem::OptionNone) == Some(def_id)
     {
         Some(arm.body)
+    } else if let PatKind::TupleStruct(QPath::Resolved(_, path), _, _)= arm.pat.kind
+        && let Some(def_id) = path.res.opt_def_id()
+        // Since it comes from a pattern binding, we need to get the parent to actually match
+        // against it.
+        && let Some(def_id) = cx.tcx.opt_parent(def_id)
+        && cx.tcx.lang_items().get(LangItem::ResultErr) == Some(def_id)
+    {
+        Some(arm.body)
     } else if let PatKind::Wild = arm.pat.kind {
         // We consider that the `Some` check will filter it out if it's not right.
         Some(arm.body)
diff --git a/clippy_lints/src/matches/redundant_guards.rs b/clippy_lints/src/matches/redundant_guards.rs
index a75cf37945f..c2c0fbf439d 100644
--- a/clippy_lints/src/matches/redundant_guards.rs
+++ b/clippy_lints/src/matches/redundant_guards.rs
@@ -2,7 +2,7 @@ use clippy_config::msrvs::Msrv;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::macros::matching_root_macro_call;
 use clippy_utils::source::snippet;
-use clippy_utils::visitors::{for_each_expr, is_local_used};
+use clippy_utils::visitors::{for_each_expr_without_closures, is_local_used};
 use clippy_utils::{in_constant, path_to_local};
 use rustc_ast::{BorrowKind, LitKind};
 use rustc_errors::Applicability;
@@ -249,7 +249,7 @@ fn emit_redundant_guards<'tcx>(
 /// an error in the future, and rustc already actively warns against this (see rust#41620),
 /// so we don't consider those as usable within patterns for linting purposes.
 fn expr_can_be_pat(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    for_each_expr(expr, |expr| {
+    for_each_expr_without_closures(expr, |expr| {
         if match expr.kind {
             ExprKind::ConstBlock(..) => cx.tcx.features().inline_const_pat,
             ExprKind::Call(c, ..) if let ExprKind::Path(qpath) = c.kind => {
diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs
index 78973984fb0..0e4b2d9d34a 100644
--- a/clippy_lints/src/matches/redundant_pattern_match.rs
+++ b/clippy_lints/src/matches/redundant_pattern_match.rs
@@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::{snippet, walk_span_to_context};
 use clippy_utils::sugg::{make_unop, Sugg};
 use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop};
-use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr};
+use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr_without_closures};
 use clippy_utils::{higher, is_expn_of, is_trait_method};
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
@@ -283,7 +283,7 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op
                 // to see that there aren't any let chains anywhere in the guard, as that would break
                 // if we suggest `t.is_none() && (let X = y && z)` for:
                 // `match t { None if let X = y && z => true, _ => false }`
-                let has_nested_let_chain = for_each_expr(guard, |expr| {
+                let has_nested_let_chain = for_each_expr_without_closures(guard, |expr| {
                     if matches!(expr.kind, ExprKind::Let(..)) {
                         ControlFlow::Break(())
                     } else {
diff --git a/clippy_lints/src/methods/collapsible_str_replace.rs b/clippy_lints/src/methods/collapsible_str_replace.rs
index 5409ede6008..1fab6c0e499 100644
--- a/clippy_lints/src/methods/collapsible_str_replace.rs
+++ b/clippy_lints/src/methods/collapsible_str_replace.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet;
-use clippy_utils::visitors::for_each_expr;
+use clippy_utils::visitors::for_each_expr_without_closures;
 use clippy_utils::{eq_expr_value, get_parent_expr};
 use core::ops::ControlFlow;
 use rustc_errors::Applicability;
@@ -46,7 +46,7 @@ fn collect_replace_calls<'tcx>(
     let mut methods = VecDeque::new();
     let mut from_args = VecDeque::new();
 
-    let _: Option<()> = for_each_expr(expr, |e| {
+    let _: Option<()> = for_each_expr_without_closures(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);
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index 75a86c0c834..6200716afbe 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -66,6 +66,7 @@ mod map_flatten;
 mod map_identity;
 mod map_unwrap_or;
 mod mut_mutex_lock;
+mod needless_character_iteration;
 mod needless_collect;
 mod needless_option_as_deref;
 mod needless_option_take;
@@ -93,7 +94,6 @@ mod seek_from_current;
 mod seek_to_start_instead_of_rewind;
 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;
@@ -1142,38 +1142,6 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for string methods that receive a single-character
-    /// `str` as an argument, e.g., `_.split("x")`.
-    ///
-    /// ### Why is this bad?
-    /// While this can make a perf difference on some systems,
-    /// benchmarks have proven inconclusive. But at least using a
-    /// char literal makes it clear that we are looking at a single
-    /// character.
-    ///
-    /// ### Known problems
-    /// Does not catch multi-byte unicode characters. This is by
-    /// design, on many machines, splitting by a non-ascii char is
-    /// actually slower. Please do your own measurements instead of
-    /// relying solely on the results of this lint.
-    ///
-    /// ### Example
-    /// ```rust,ignore
-    /// _.split("x");
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust,ignore
-    /// _.split('x');
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub SINGLE_CHAR_PATTERN,
-    pedantic,
-    "using a single-character str where a char could be used, e.g., `_.split(\"x\")`"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
     /// Checks for calling `.step_by(0)` on iterators which panics.
     ///
     /// ### Why is this bad?
@@ -4084,12 +4052,33 @@ declare_clippy_lint! {
     /// ```no_run
     /// println!("the string is empty");
     /// ```
-    #[clippy::version = "1.78.0"]
+    #[clippy::version = "1.79.0"]
     pub CONST_IS_EMPTY,
     suspicious,
     "is_empty() called on strings known at compile time"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks if an iterator is used to check if a string is ascii.
+    ///
+    /// ### Why is this bad?
+    /// The `str` type already implements the `is_ascii` method.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// "foo".chars().all(|c| c.is_ascii());
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// "foo".is_ascii();
+    /// ```
+    #[clippy::version = "1.80.0"]
+    pub NEEDLESS_CHARACTER_ITERATION,
+    suspicious,
+    "is_ascii() called on a char iterator"
+}
+
 pub struct Methods {
     avoid_breaking_exported_api: bool,
     msrv: Msrv,
@@ -4147,7 +4136,6 @@ impl_lint_pass!(Methods => [
     FLAT_MAP_OPTION,
     INEFFICIENT_TO_STRING,
     NEW_RET_NO_SELF,
-    SINGLE_CHAR_PATTERN,
     SINGLE_CHAR_ADD_STR,
     SEARCH_IS_SOME,
     FILTER_NEXT,
@@ -4255,6 +4243,7 @@ impl_lint_pass!(Methods => [
     UNNECESSARY_RESULT_MAP_OR_ELSE,
     MANUAL_C_STR_LITERALS,
     UNNECESSARY_GET_THEN_CHECK,
+    NEEDLESS_CHARACTER_ITERATION,
 ]);
 
 /// Extracts a method call name, args, and `Span` of the method name.
@@ -4301,7 +4290,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
                 inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args);
                 single_char_add_str::check(cx, expr, receiver, args);
                 into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, receiver);
-                single_char_pattern::check(cx, expr, method_call.ident.name, receiver, args);
                 unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, &self.msrv);
             },
             ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
@@ -4462,6 +4450,7 @@ impl Methods {
                 },
                 ("all", [arg]) => {
                     unused_enumerate_index::check(cx, expr, recv, arg);
+                    needless_character_iteration::check(cx, expr, recv, arg, true);
                     if let Some(("cloned", recv2, [], _, _)) = method_call(recv) {
                         iter_overeager_cloned::check(
                             cx,
@@ -4482,6 +4471,7 @@ impl Methods {
                 },
                 ("any", [arg]) => {
                     unused_enumerate_index::check(cx, expr, recv, arg);
+                    needless_character_iteration::check(cx, expr, recv, arg, false);
                     match method_call(recv) {
                         Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(
                             cx,
diff --git a/clippy_lints/src/methods/needless_character_iteration.rs b/clippy_lints/src/methods/needless_character_iteration.rs
new file mode 100644
index 00000000000..e3d78207715
--- /dev/null
+++ b/clippy_lints/src/methods/needless_character_iteration.rs
@@ -0,0 +1,124 @@
+use rustc_errors::Applicability;
+use rustc_hir::{Closure, Expr, ExprKind, HirId, StmtKind, UnOp};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_span::Span;
+
+use super::utils::get_last_chain_binding_hir_id;
+use super::NEEDLESS_CHARACTER_ITERATION;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::{match_def_path, path_to_local_id, peel_blocks};
+
+fn peels_expr_ref<'a, 'tcx>(mut expr: &'a Expr<'tcx>) -> &'a Expr<'tcx> {
+    while let ExprKind::AddrOf(_, _, e) = expr.kind {
+        expr = e;
+    }
+    expr
+}
+
+fn handle_expr(
+    cx: &LateContext<'_>,
+    expr: &Expr<'_>,
+    first_param: HirId,
+    span: Span,
+    before_chars: Span,
+    revert: bool,
+    is_all: bool,
+) {
+    match expr.kind {
+        ExprKind::MethodCall(method, receiver, [], _) => {
+            // If we have `!is_ascii`, then only `.any()` should warn. And if the condition is
+            // `is_ascii`, then only `.all()` should warn.
+            if revert != is_all
+                && method.ident.name.as_str() == "is_ascii"
+                && path_to_local_id(receiver, first_param)
+                && let char_arg_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs()
+                && *char_arg_ty.kind() == ty::Char
+                && let Some(snippet) = snippet_opt(cx, before_chars)
+            {
+                span_lint_and_sugg(
+                    cx,
+                    NEEDLESS_CHARACTER_ITERATION,
+                    span,
+                    "checking if a string is ascii using iterators",
+                    "try",
+                    format!("{}{snippet}.is_ascii()", if revert { "!" } else { "" }),
+                    Applicability::MachineApplicable,
+                );
+            }
+        },
+        ExprKind::Block(block, _) => {
+            if block.stmts.iter().any(|stmt| !matches!(stmt.kind, StmtKind::Let(_))) {
+                // If there is something else than let bindings, then better not emit the lint.
+                return;
+            }
+            if let Some(block_expr) = block.expr
+                // First we ensure that this is a "binding chain" (each statement is a binding
+                // of the previous one) and that it is a binding of the closure argument.
+                && let Some(last_chain_binding_id) =
+                    get_last_chain_binding_hir_id(first_param, block.stmts)
+            {
+                handle_expr(
+                    cx,
+                    block_expr,
+                    last_chain_binding_id,
+                    span,
+                    before_chars,
+                    revert,
+                    is_all,
+                );
+            }
+        },
+        ExprKind::Unary(UnOp::Not, expr) => handle_expr(cx, expr, first_param, span, before_chars, !revert, is_all),
+        ExprKind::Call(fn_path, [arg]) => {
+            // If we have `!is_ascii`, then only `.any()` should warn. And if the condition is
+            // `is_ascii`, then only `.all()` should warn.
+            if revert != is_all
+                && let ExprKind::Path(path) = fn_path.kind
+                && let Some(fn_def_id) = cx.qpath_res(&path, fn_path.hir_id).opt_def_id()
+                && match_def_path(cx, fn_def_id, &["core", "char", "methods", "<impl char>", "is_ascii"])
+                && path_to_local_id(peels_expr_ref(arg), first_param)
+                && let Some(snippet) = snippet_opt(cx, before_chars)
+            {
+                span_lint_and_sugg(
+                    cx,
+                    NEEDLESS_CHARACTER_ITERATION,
+                    span,
+                    "checking if a string is ascii using iterators",
+                    "try",
+                    format!("{}{snippet}.is_ascii()", if revert { "!" } else { "" }),
+                    Applicability::MachineApplicable,
+                );
+            }
+        },
+        _ => {},
+    }
+}
+
+pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, closure_arg: &Expr<'_>, is_all: bool) {
+    if let ExprKind::Closure(&Closure { body, .. }) = closure_arg.kind
+        && let body = cx.tcx.hir().body(body)
+        && let Some(first_param) = body.params.first()
+        && let ExprKind::MethodCall(method, mut recv, [], _) = recv.kind
+        && method.ident.name.as_str() == "chars"
+        && let str_ty = cx.typeck_results().expr_ty_adjusted(recv).peel_refs()
+        && *str_ty.kind() == ty::Str
+    {
+        let expr_start = recv.span;
+        while let ExprKind::MethodCall(_, new_recv, _, _) = recv.kind {
+            recv = new_recv;
+        }
+        let body_expr = peel_blocks(body.value);
+
+        handle_expr(
+            cx,
+            body_expr,
+            first_param.pat.hir_id,
+            recv.span.with_hi(call_expr.span.hi()),
+            recv.span.with_hi(expr_start.hi()),
+            false,
+            is_all,
+        );
+    }
+}
diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs
index ca331f3e756..3d326bc99f9 100644
--- a/clippy_lints/src/methods/option_map_unwrap_or.rs
+++ b/clippy_lints/src/methods/option_map_unwrap_or.rs
@@ -60,7 +60,7 @@ pub(super) fn check<'tcx>(
 
             let map = cx.tcx.hir();
             let body = map.body_owned_by(map.enclosing_body_owner(expr.hir_id));
-            reference_visitor.visit_body(&body);
+            reference_visitor.visit_body(body);
 
             if reference_visitor.found_reference {
                 return;
diff --git a/clippy_lints/src/methods/path_buf_push_overwrite.rs b/clippy_lints/src/methods/path_buf_push_overwrite.rs
index 04a27cc98f3..2d3007e50b8 100644
--- a/clippy_lints/src/methods/path_buf_push_overwrite.rs
+++ b/clippy_lints/src/methods/path_buf_push_overwrite.rs
@@ -27,7 +27,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t
             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 == '\\')),
+            format!("\"{}\"", pushed_path_lit.trim_start_matches(['/', '\\'])),
             Applicability::MachineApplicable,
         );
     }
diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs
index f5f1e94bbf4..3b2dd285b8c 100644
--- a/clippy_lints/src/methods/search_is_some.rs
+++ b/clippy_lints/src/methods/search_is_some.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
 use clippy_utils::source::{snippet, snippet_with_applicability};
 use clippy_utils::sugg::deref_closure_args;
 use clippy_utils::ty::is_type_lang_item;
-use clippy_utils::{get_parent_expr, is_trait_method, strip_pat_refs};
+use clippy_utils::{is_receiver_of_method_call, is_trait_method, strip_pat_refs};
 use hir::ExprKind;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -156,13 +156,3 @@ pub(super) fn check<'tcx>(
         }
     }
 }
-
-fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
-    if let Some(parent_expr) = get_parent_expr(cx, expr)
-        && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
-        && receiver.hir_id == expr.hir_id
-    {
-        return true;
-    }
-    false
-}
diff --git a/clippy_lints/src/methods/single_char_insert_string.rs b/clippy_lints/src/methods/single_char_insert_string.rs
index 20ec2b74d81..e2f76ac114c 100644
--- a/clippy_lints/src/methods/single_char_insert_string.rs
+++ b/clippy_lints/src/methods/single_char_insert_string.rs
@@ -1,8 +1,8 @@
-use super::utils::get_hint_if_single_char_arg;
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::{snippet_with_applicability, str_literal_to_char_literal};
+use rustc_ast::BorrowKind;
 use rustc_errors::Applicability;
-use rustc_hir as hir;
+use rustc_hir::{self as hir, ExprKind};
 use rustc_lint::LateContext;
 
 use super::SINGLE_CHAR_ADD_STR;
@@ -10,7 +10,7 @@ use super::SINGLE_CHAR_ADD_STR;
 /// lint for length-1 `str`s as argument for `insert_str`
 pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
     let mut applicability = Applicability::MachineApplicable;
-    if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability, false) {
+    if let Some(extension_string) = str_literal_to_char_literal(cx, &args[1], &mut applicability, false) {
         let base_string_snippet =
             snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability);
         let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability);
@@ -25,4 +25,43 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::
             applicability,
         );
     }
+
+    if let ExprKind::AddrOf(BorrowKind::Ref, _, arg) = &args[1].kind
+        && let ExprKind::MethodCall(path_segment, method_arg, _, _) = &arg.kind
+        && path_segment.ident.name == rustc_span::sym::to_string
+        && (is_ref_char(cx, method_arg) || is_char(cx, method_arg))
+    {
+        let base_string_snippet =
+            snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability);
+        let extension_string =
+            snippet_with_applicability(cx, method_arg.span.source_callsite(), "..", &mut applicability);
+        let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability);
+        let deref_string = if is_ref_char(cx, method_arg) { "*" } else { "" };
+
+        let sugg = format!("{base_string_snippet}.insert({pos_arg}, {deref_string}{extension_string})");
+        span_lint_and_sugg(
+            cx,
+            SINGLE_CHAR_ADD_STR,
+            expr.span,
+            "calling `insert_str()` using a single-character converted to string",
+            "consider using `insert` without `to_string()`",
+            sugg,
+            applicability,
+        );
+    }
+}
+
+fn is_ref_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
+    if cx.typeck_results().expr_ty(expr).is_ref()
+        && let rustc_middle::ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(expr).kind()
+        && ty.is_char()
+    {
+        return true;
+    }
+
+    false
+}
+
+fn is_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
+    cx.typeck_results().expr_ty(expr).is_char()
 }
diff --git a/clippy_lints/src/methods/single_char_pattern.rs b/clippy_lints/src/methods/single_char_pattern.rs
deleted file mode 100644
index 982a7901c45..00000000000
--- a/clippy_lints/src/methods/single_char_pattern.rs
+++ /dev/null
@@ -1,64 +0,0 @@
-use super::utils::get_hint_if_single_char_arg;
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use rustc_errors::Applicability;
-use rustc_hir as hir;
-use rustc_lint::LateContext;
-use rustc_middle::ty;
-use rustc_span::symbol::Symbol;
-
-use super::SINGLE_CHAR_PATTERN;
-
-const PATTERN_METHODS: [(&str, usize); 22] = [
-    ("contains", 0),
-    ("starts_with", 0),
-    ("ends_with", 0),
-    ("find", 0),
-    ("rfind", 0),
-    ("split", 0),
-    ("split_inclusive", 0),
-    ("rsplit", 0),
-    ("split_terminator", 0),
-    ("rsplit_terminator", 0),
-    ("splitn", 1),
-    ("rsplitn", 1),
-    ("split_once", 0),
-    ("rsplit_once", 0),
-    ("matches", 0),
-    ("rmatches", 0),
-    ("match_indices", 0),
-    ("rmatch_indices", 0),
-    ("trim_start_matches", 0),
-    ("trim_end_matches", 0),
-    ("replace", 0),
-    ("replacen", 0),
-];
-
-/// lint for length-1 `str`s for methods in `PATTERN_METHODS`
-pub(super) fn check(
-    cx: &LateContext<'_>,
-    _expr: &hir::Expr<'_>,
-    method_name: Symbol,
-    receiver: &hir::Expr<'_>,
-    args: &[hir::Expr<'_>],
-) {
-    for &(method, pos) in &PATTERN_METHODS {
-        if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(receiver).kind()
-            && ty.is_str()
-            && method_name.as_str() == method
-            && args.len() > pos
-            && let arg = &args[pos]
-            && let mut applicability = Applicability::MachineApplicable
-            && let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability, true)
-        {
-            span_lint_and_sugg(
-                cx,
-                SINGLE_CHAR_PATTERN,
-                arg.span,
-                "single-character string constant used as pattern",
-                "consider using a `char`",
-                hint,
-                applicability,
-            );
-        }
-    }
-}
diff --git a/clippy_lints/src/methods/single_char_push_string.rs b/clippy_lints/src/methods/single_char_push_string.rs
index 97c13825bc1..4ae8634305d 100644
--- a/clippy_lints/src/methods/single_char_push_string.rs
+++ b/clippy_lints/src/methods/single_char_push_string.rs
@@ -1,8 +1,8 @@
-use super::utils::get_hint_if_single_char_arg;
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::{snippet_with_applicability, str_literal_to_char_literal};
+use rustc_ast::BorrowKind;
 use rustc_errors::Applicability;
-use rustc_hir as hir;
+use rustc_hir::{self as hir, ExprKind};
 use rustc_lint::LateContext;
 
 use super::SINGLE_CHAR_ADD_STR;
@@ -10,7 +10,7 @@ use super::SINGLE_CHAR_ADD_STR;
 /// lint for length-1 `str`s as argument for `push_str`
 pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
     let mut applicability = Applicability::MachineApplicable;
-    if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[0], &mut applicability, false) {
+    if let Some(extension_string) = str_literal_to_char_literal(cx, &args[0], &mut applicability, false) {
         let base_string_snippet =
             snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability);
         let sugg = format!("{base_string_snippet}.push({extension_string})");
@@ -24,4 +24,42 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::
             applicability,
         );
     }
+
+    if let ExprKind::AddrOf(BorrowKind::Ref, _, arg) = &args[0].kind
+        && let ExprKind::MethodCall(path_segment, method_arg, _, _) = &arg.kind
+        && path_segment.ident.name == rustc_span::sym::to_string
+        && (is_ref_char(cx, method_arg) || is_char(cx, method_arg))
+    {
+        let base_string_snippet =
+            snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability);
+        let extension_string =
+            snippet_with_applicability(cx, method_arg.span.source_callsite(), "..", &mut applicability);
+        let deref_string = if is_ref_char(cx, method_arg) { "*" } else { "" };
+
+        let sugg = format!("{base_string_snippet}.push({deref_string}{extension_string})");
+        span_lint_and_sugg(
+            cx,
+            SINGLE_CHAR_ADD_STR,
+            expr.span,
+            "calling `push_str()` using a single-character converted to string",
+            "consider using `push` without `to_string()`",
+            sugg,
+            applicability,
+        );
+    }
+}
+
+fn is_ref_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
+    if cx.typeck_results().expr_ty(expr).is_ref()
+        && let rustc_middle::ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(expr).kind()
+        && ty.is_char()
+    {
+        return true;
+    }
+
+    false
+}
+
+fn is_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
+    cx.typeck_results().expr_ty(expr).is_char()
 }
diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs
index e8c12bbeea0..4f42fb73547 100644
--- a/clippy_lints/src/methods/str_splitn.rs
+++ b/clippy_lints/src/methods/str_splitn.rs
@@ -3,7 +3,7 @@ use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::usage::local_used_after_expr;
-use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
+use clippy_utils::visitors::{for_each_expr, Descend};
 use clippy_utils::{is_diag_item_method, match_def_path, path_to_local_id, paths};
 use core::ops::ControlFlow;
 use rustc_errors::Applicability;
@@ -209,7 +209,7 @@ fn indirect_usage<'tcx>(
     }) = stmt.kind
     {
         let mut path_to_binding = None;
-        let _: Option<!> = for_each_expr_with_closures(cx, init_expr, |e| {
+        let _: Option<!> = for_each_expr(cx, init_expr, |e| {
             if path_to_local_id(e, binding) {
                 path_to_binding = Some(e);
             }
diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs
index daf99d98614..c9b9d98dbe6 100644
--- a/clippy_lints/src/methods/unnecessary_filter_map.rs
+++ b/clippy_lints/src/methods/unnecessary_filter_map.rs
@@ -2,7 +2,7 @@ use super::utils::clone_or_copy_needed;
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::ty::is_copy;
 use clippy_utils::usage::mutated_variables;
-use clippy_utils::visitors::{for_each_expr, Descend};
+use clippy_utils::visitors::{for_each_expr_without_closures, Descend};
 use clippy_utils::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id};
 use core::ops::ControlFlow;
 use rustc_hir as hir;
@@ -26,7 +26,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a
 
         let (mut found_mapping, mut found_filtering) = check_expression(cx, arg_id, body.value);
 
-        let _: Option<!> = for_each_expr(body.value, |e| {
+        let _: Option<!> = for_each_expr_without_closures(body.value, |e| {
             if let hir::ExprKind::Ret(Some(e)) = &e.kind {
                 let (found_mapping_res, found_filtering_res) = check_expression(cx, arg_id, e);
                 found_mapping |= found_mapping_res;
diff --git a/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/clippy_lints/src/methods/unnecessary_iter_cloned.rs
index d1300dd43c2..70885e46e95 100644
--- a/clippy_lints/src/methods/unnecessary_iter_cloned.rs
+++ b/clippy_lints/src/methods/unnecessary_iter_cloned.rs
@@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::higher::ForLoop;
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::{get_iterator_item_ty, implements_trait};
-use clippy_utils::visitors::for_each_expr;
+use clippy_utils::visitors::for_each_expr_without_closures;
 use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr, path_to_local};
 use core::ops::ControlFlow;
 use rustc_errors::Applicability;
@@ -61,7 +61,7 @@ pub fn check_for_loop_iter(
         fn is_caller_or_fields_change(cx: &LateContext<'_>, body: &Expr<'_>, caller: &Expr<'_>) -> bool {
             let mut change = false;
             if let ExprKind::Block(block, ..) = body.kind {
-                for_each_expr(block, |e| {
+                for_each_expr_without_closures(block, |e| {
                     match e.kind {
                         ExprKind::Assign(assignee, _, _) | ExprKind::AssignOp(_, assignee, _) => {
                             change |= !can_mut_borrow_both(cx, caller, assignee);
diff --git a/clippy_lints/src/methods/unnecessary_result_map_or_else.rs b/clippy_lints/src/methods/unnecessary_result_map_or_else.rs
index cdfaa690d5f..c14a87c1534 100644
--- a/clippy_lints/src/methods/unnecessary_result_map_or_else.rs
+++ b/clippy_lints/src/methods/unnecessary_result_map_or_else.rs
@@ -4,10 +4,11 @@ use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
-use rustc_hir::{Closure, Expr, ExprKind, HirId, QPath, Stmt, StmtKind};
+use rustc_hir::{Closure, Expr, ExprKind, HirId, QPath};
 use rustc_lint::LateContext;
 use rustc_span::symbol::sym;
 
+use super::utils::get_last_chain_binding_hir_id;
 use super::UNNECESSARY_RESULT_MAP_OR_ELSE;
 
 fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>) {
@@ -25,22 +26,6 @@ fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &E
     );
 }
 
-fn get_last_chain_binding_hir_id(mut hir_id: HirId, statements: &[Stmt<'_>]) -> Option<HirId> {
-    for stmt in statements {
-        if let StmtKind::Let(local) = stmt.kind
-            && let Some(init) = local.init
-            && let ExprKind::Path(QPath::Resolved(_, path)) = init.kind
-            && let hir::def::Res::Local(local_hir_id) = path.res
-            && local_hir_id == hir_id
-        {
-            hir_id = local.pat.hir_id;
-        } else {
-            return None;
-        }
-    }
-    Some(hir_id)
-}
-
 fn handle_qpath(
     cx: &LateContext<'_>,
     expr: &Expr<'_>,
diff --git a/clippy_lints/src/methods/unnecessary_sort_by.rs b/clippy_lints/src/methods/unnecessary_sort_by.rs
index 6911da69b94..a92fe9e55a4 100644
--- a/clippy_lints/src/methods/unnecessary_sort_by.rs
+++ b/clippy_lints/src/methods/unnecessary_sort_by.rs
@@ -5,7 +5,8 @@ use clippy_utils::ty::implements_trait;
 use rustc_errors::Applicability;
 use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
 use rustc_lint::LateContext;
-use rustc_middle::ty::{self, GenericArgKind};
+use rustc_middle::ty;
+use rustc_middle::ty::GenericArgKind;
 use rustc_span::sym;
 use rustc_span::symbol::Ident;
 use std::iter;
diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs
index 1a55b7160fb..0d2b0a31317 100644
--- a/clippy_lints/src/methods/utils.rs
+++ b/clippy_lints/src/methods/utils.rs
@@ -1,10 +1,7 @@
-use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{get_parent_expr, path_to_local_id, usage};
-use rustc_ast::ast;
-use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_expr, Visitor};
-use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Pat};
+use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, QPath, Stmt, StmtKind};
 use rustc_lint::LateContext;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::{self, Ty};
@@ -49,48 +46,6 @@ pub(super) fn derefs_to_slice<'tcx>(
     }
 }
 
-pub(super) fn get_hint_if_single_char_arg(
-    cx: &LateContext<'_>,
-    arg: &Expr<'_>,
-    applicability: &mut Applicability,
-    ascii_only: bool,
-) -> Option<String> {
-    if let ExprKind::Lit(lit) = &arg.kind
-        && let ast::LitKind::Str(r, style) = lit.node
-        && let string = r.as_str()
-        && let len = if ascii_only {
-            string.len()
-        } else {
-            string.chars().count()
-        }
-        && len == 1
-    {
-        let snip = snippet_with_applicability(cx, arg.span, string, applicability);
-        let ch = if let ast::StrStyle::Raw(nhash) = style {
-            let nhash = nhash as usize;
-            // for raw string: r##"a"##
-            &snip[(nhash + 2)..(snip.len() - 1 - nhash)]
-        } else {
-            // for regular string: "a"
-            &snip[1..(snip.len() - 1)]
-        };
-
-        let hint = format!(
-            "'{}'",
-            match ch {
-                "'" => "\\'",
-                r"\" => "\\\\",
-                "\\\"" => "\"", // no need to escape `"` in `'"'`
-                _ => ch,
-            }
-        );
-
-        Some(hint)
-    } else {
-        None
-    }
-}
-
 /// The core logic of `check_for_loop_iter` in `unnecessary_iter_cloned.rs`, this function wraps a
 /// use of `CloneOrCopyVisitor`.
 pub(super) fn clone_or_copy_needed<'tcx>(
@@ -175,3 +130,19 @@ impl<'cx, 'tcx> CloneOrCopyVisitor<'cx, 'tcx> {
             .any(|hir_id| path_to_local_id(expr, *hir_id))
     }
 }
+
+pub(super) fn get_last_chain_binding_hir_id(mut hir_id: HirId, statements: &[Stmt<'_>]) -> Option<HirId> {
+    for stmt in statements {
+        if let StmtKind::Let(local) = stmt.kind
+            && let Some(init) = local.init
+            && let ExprKind::Path(QPath::Resolved(_, path)) = init.kind
+            && let rustc_hir::def::Res::Local(local_hir_id) = path.res
+            && local_hir_id == hir_id
+        {
+            hir_id = local.pat.hir_id;
+        } else {
+            return None;
+        }
+    }
+    Some(hir_id)
+}
diff --git a/clippy_lints/src/misc_early/zero_prefixed_literal.rs b/clippy_lints/src/misc_early/zero_prefixed_literal.rs
index 4f9578d1b25..61f4684c9e3 100644
--- a/clippy_lints/src/misc_early/zero_prefixed_literal.rs
+++ b/clippy_lints/src/misc_early/zero_prefixed_literal.rs
@@ -6,7 +6,7 @@ use rustc_span::Span;
 use super::ZERO_PREFIXED_LITERAL;
 
 pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, lit_snip: &str) {
-    let trimmed_lit_snip = lit_snip.trim_start_matches(|c| c == '_' || c == '0');
+    let trimmed_lit_snip = lit_snip.trim_start_matches(['_', '0']);
     span_lint_and_then(
         cx,
         ZERO_PREFIXED_LITERAL,
@@ -20,7 +20,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, lit_snip: &str) {
                 Applicability::MaybeIncorrect,
             );
             // do not advise to use octal form if the literal cannot be expressed in base 8.
-            if !lit_snip.contains(|c| c == '8' || c == '9') {
+            if !lit_snip.contains(['8', '9']) {
                 diag.span_suggestion(
                     lit_span,
                     "if you mean to use an octal constant, use `0o`",
diff --git a/clippy_lints/src/missing_asserts_for_indexing.rs b/clippy_lints/src/missing_asserts_for_indexing.rs
index 752723a0c68..94c91b09517 100644
--- a/clippy_lints/src/missing_asserts_for_indexing.rs
+++ b/clippy_lints/src/missing_asserts_for_indexing.rs
@@ -4,7 +4,7 @@ use std::ops::ControlFlow;
 use clippy_utils::comparisons::{normalize_comparison, Rel};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet;
-use clippy_utils::visitors::for_each_expr;
+use clippy_utils::visitors::for_each_expr_without_closures;
 use clippy_utils::{eq_expr_value, hash_expr, higher};
 use rustc_ast::{LitKind, RangeLimits};
 use rustc_data_structures::packed::Pu128;
@@ -405,7 +405,7 @@ impl LateLintPass<'_> for MissingAssertsForIndexing {
     fn check_body(&mut self, cx: &LateContext<'_>, body: &Body<'_>) {
         let mut map = UnhashMap::default();
 
-        for_each_expr(body.value, |expr| {
+        for_each_expr_without_closures(body.value, |expr| {
             check_index(cx, expr, &mut map);
             check_assert(cx, expr, &mut map);
             ControlFlow::<!, ()>::Continue(())
diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs
index 9ba19e0a865..4592324809f 100644
--- a/clippy_lints/src/missing_const_for_fn.rs
+++ b/clippy_lints/src/missing_const_for_fn.rs
@@ -1,7 +1,6 @@
 use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::qualify_min_const_fn::is_min_const_fn;
-use clippy_utils::ty::has_drop;
 use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, trait_ref_of_method};
 use rustc_hir as hir;
 use rustc_hir::def_id::CRATE_DEF_ID;
@@ -121,10 +120,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
                 }
             },
             FnKind::Method(_, sig, ..) => {
-                if trait_ref_of_method(cx, def_id).is_some()
-                    || already_const(sig.header)
-                    || method_accepts_droppable(cx, def_id)
-                {
+                if trait_ref_of_method(cx, def_id).is_some() || already_const(sig.header) {
                     return;
                 }
             },
@@ -151,26 +147,13 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
 
         let mir = cx.tcx.optimized_mir(def_id);
 
-        if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, &self.msrv) {
-            if cx.tcx.is_const_fn_raw(def_id.to_def_id()) {
-                cx.tcx.dcx().span_err(span, err);
-            }
-        } else {
+        if let Ok(()) = is_min_const_fn(cx.tcx, mir, &self.msrv) {
             span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`");
         }
     }
     extract_msrv_attr!(LateContext);
 }
 
-/// Returns true if any of the method parameters is a type that implements `Drop`. The method
-/// can't be made const then, because `drop` can't be const-evaluated.
-fn method_accepts_droppable(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
-    let sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder();
-
-    // If any of the params are droppable, return true
-    sig.inputs().iter().any(|&ty| has_drop(cx, ty))
-}
-
 // We don't have to lint on something that's already `const`
 #[must_use]
 fn already_const(header: hir::FnHeader) -> bool {
diff --git a/clippy_lints/src/missing_fields_in_debug.rs b/clippy_lints/src/missing_fields_in_debug.rs
index a64faa124f0..77595b121aa 100644
--- a/clippy_lints/src/missing_fields_in_debug.rs
+++ b/clippy_lints/src/missing_fields_in_debug.rs
@@ -110,7 +110,7 @@ fn should_lint<'tcx>(
     // Is there a call to `DebugStruct::debug_struct`? Do lint if there is.
     let mut has_debug_struct = false;
 
-    for_each_expr(block, |expr| {
+    for_each_expr(cx, block, |expr| {
         if let ExprKind::MethodCall(path, recv, ..) = &expr.kind {
             let recv_ty = typeck_results.expr_ty(recv).peel_refs();
 
@@ -165,7 +165,7 @@ fn check_struct<'tcx>(
     let mut has_direct_field_access = false;
     let mut field_accesses = FxHashSet::default();
 
-    for_each_expr(block, |expr| {
+    for_each_expr(cx, block, |expr| {
         if let ExprKind::Field(target, ident) = expr.kind
             && let target_ty = typeck_results.expr_ty_adjusted(target).peel_refs()
             && target_ty == self_ty
diff --git a/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/clippy_lints/src/multiple_unsafe_ops_per_block.rs
index 5b4ef852f0d..da74a7c7145 100644
--- a/clippy_lints/src/multiple_unsafe_ops_per_block.rs
+++ b/clippy_lints/src/multiple_unsafe_ops_per_block.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::visitors::{for_each_expr_with_closures, Descend, Visitable};
+use clippy_utils::visitors::{for_each_expr, Descend, Visitable};
 use core::ops::ControlFlow::Continue;
 use hir::def::{DefKind, Res};
 use hir::{BlockCheckMode, ExprKind, QPath, Safety, UnOp};
@@ -96,7 +96,7 @@ fn collect_unsafe_exprs<'tcx>(
     node: impl Visitable<'tcx>,
     unsafe_ops: &mut Vec<(&'static str, Span)>,
 ) {
-    for_each_expr_with_closures(cx, node, |expr| {
+    for_each_expr(cx, node, |expr| {
         match expr.kind {
             ExprKind::InlineAsm(_) => unsafe_ops.push(("inline assembly used here", expr.span)),
 
diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs
index e92ba93942e..563ce2d82ea 100644
--- a/clippy_lints/src/mutable_debug_assertion.rs
+++ b/clippy_lints/src/mutable_debug_assertion.rs
@@ -96,10 +96,6 @@ impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> {
                 self.found = true;
                 return;
             },
-            ExprKind::If(..) => {
-                self.found = true;
-                return;
-            },
             ExprKind::Path(_) => {
                 if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
                     if adj
diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs
index e1866eaa18a..9cb4fa41c73 100644
--- a/clippy_lints/src/needless_bool.rs
+++ b/clippy_lints/src/needless_bool.rs
@@ -6,8 +6,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::{
-    higher, is_block_like, is_else_clause, is_expn_of, is_parent_stmt, peel_blocks, peel_blocks_with_stmt,
-    span_extract_comment, SpanlessEq,
+    get_parent_expr, higher, is_block_like, is_else_clause, is_expn_of, is_parent_stmt, is_receiver_of_method_call,
+    peel_blocks, peel_blocks_with_stmt, span_extract_comment, SpanlessEq,
 };
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
@@ -154,7 +154,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
                     snip = snip.blockify();
                 }
 
-                if condition_needs_parentheses(cond) && is_parent_stmt(cx, e.hir_id) {
+                if (condition_needs_parentheses(cond) && is_parent_stmt(cx, e.hir_id))
+                    || is_receiver_of_method_call(cx, e)
+                    || is_as_argument(cx, e)
+                {
                     snip = snip.maybe_par();
                 }
 
@@ -442,3 +445,7 @@ fn fetch_assign<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, bool)
         None
     }
 }
+
+fn is_as_argument(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
+    matches!(get_parent_expr(cx, e).map(|e| e.kind), Some(ExprKind::Cast(_, _)))
+}
diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs
index 5a0ae1a4d6d..4bfc30fa5cf 100644
--- a/clippy_lints/src/needless_late_init.rs
+++ b/clippy_lints/src/needless_late_init.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::path_to_local;
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::needs_ordered_drop;
-use clippy_utils::visitors::{for_each_expr, for_each_expr_with_closures, is_local_used};
+use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures, is_local_used};
 use core::ops::ControlFlow;
 use rustc_errors::{Applicability, MultiSpan};
 use rustc_hir::{
@@ -63,7 +63,7 @@ declare_clippy_lint! {
 declare_lint_pass!(NeedlessLateInit => [NEEDLESS_LATE_INIT]);
 
 fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> bool {
-    for_each_expr_with_closures(cx, stmt, |e| {
+    for_each_expr(cx, stmt, |e| {
         if matches!(e.kind, ExprKind::Assign(..)) {
             ControlFlow::Break(())
         } else {
@@ -74,7 +74,7 @@ fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) ->
 }
 
 fn contains_let(cond: &Expr<'_>) -> bool {
-    for_each_expr(cond, |e| {
+    for_each_expr_without_closures(cond, |e| {
         if matches!(e.kind, ExprKind::Let(_)) {
             ControlFlow::Break(())
         } else {
diff --git a/clippy_lints/src/needless_maybe_sized.rs b/clippy_lints/src/needless_maybe_sized.rs
new file mode 100644
index 00000000000..06ae1723a03
--- /dev/null
+++ b/clippy_lints/src/needless_maybe_sized.rs
@@ -0,0 +1,164 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_errors::Applicability;
+use rustc_hir::def_id::{DefId, DefIdMap};
+use rustc_hir::{GenericBound, Generics, PolyTraitRef, TraitBoundModifier, WherePredicate};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::{ClauseKind, PredicatePolarity};
+use rustc_session::declare_lint_pass;
+use rustc_span::symbol::Ident;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Lints `?Sized` bounds applied to type parameters that cannot be unsized
+    ///
+    /// ### Why is this bad?
+    /// The `?Sized` bound is misleading because it cannot be satisfied by an
+    /// unsized type
+    ///
+    /// ### Example
+    /// ```rust
+    /// // `T` cannot be unsized because `Clone` requires it to be `Sized`
+    /// fn f<T: Clone + ?Sized>(t: &T) {}
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// fn f<T: Clone>(t: &T) {}
+    ///
+    /// // or choose alternative bounds for `T` so that it can be unsized
+    /// ```
+    #[clippy::version = "1.79.0"]
+    pub NEEDLESS_MAYBE_SIZED,
+    suspicious,
+    "a `?Sized` bound that is unusable due to a `Sized` requirement"
+}
+declare_lint_pass!(NeedlessMaybeSized => [NEEDLESS_MAYBE_SIZED]);
+
+#[allow(clippy::struct_field_names)]
+struct Bound<'tcx> {
+    /// The [`DefId`] of the type parameter the bound refers to
+    param: DefId,
+    ident: Ident,
+
+    trait_bound: &'tcx PolyTraitRef<'tcx>,
+    modifier: TraitBoundModifier,
+
+    predicate_pos: usize,
+    bound_pos: usize,
+}
+
+/// Finds all of the [`Bound`]s that refer to a type parameter and are not from a macro expansion
+fn type_param_bounds<'tcx>(generics: &'tcx Generics<'tcx>) -> impl Iterator<Item = Bound<'tcx>> {
+    generics
+        .predicates
+        .iter()
+        .enumerate()
+        .filter_map(|(predicate_pos, predicate)| {
+            let WherePredicate::BoundPredicate(bound_predicate) = predicate else {
+                return None;
+            };
+
+            let (param, ident) = bound_predicate.bounded_ty.as_generic_param()?;
+
+            Some(
+                bound_predicate
+                    .bounds
+                    .iter()
+                    .enumerate()
+                    .filter_map(move |(bound_pos, bound)| match bound {
+                        &GenericBound::Trait(ref trait_bound, modifier) => Some(Bound {
+                            param,
+                            ident,
+                            trait_bound,
+                            modifier,
+                            predicate_pos,
+                            bound_pos,
+                        }),
+                        GenericBound::Outlives(_) => None,
+                    })
+                    .filter(|bound| !bound.trait_bound.span.from_expansion()),
+            )
+        })
+        .flatten()
+}
+
+/// Searches the supertraits of the trait referred to by `trait_bound` recursively, returning the
+/// path taken to find a `Sized` bound if one is found
+fn path_to_sized_bound(cx: &LateContext<'_>, trait_bound: &PolyTraitRef<'_>) -> Option<Vec<DefId>> {
+    fn search(cx: &LateContext<'_>, path: &mut Vec<DefId>) -> bool {
+        let trait_def_id = *path.last().unwrap();
+
+        if Some(trait_def_id) == cx.tcx.lang_items().sized_trait() {
+            return true;
+        }
+
+        for &(predicate, _) in cx.tcx.super_predicates_of(trait_def_id).predicates {
+            if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
+                && trait_predicate.polarity == PredicatePolarity::Positive
+                && !path.contains(&trait_predicate.def_id())
+            {
+                path.push(trait_predicate.def_id());
+                if search(cx, path) {
+                    return true;
+                }
+                path.pop();
+            }
+        }
+
+        false
+    }
+
+    let mut path = vec![trait_bound.trait_ref.trait_def_id()?];
+    search(cx, &mut path).then_some(path)
+}
+
+impl LateLintPass<'_> for NeedlessMaybeSized {
+    fn check_generics(&mut self, cx: &LateContext<'_>, generics: &Generics<'_>) {
+        let Some(sized_trait) = cx.tcx.lang_items().sized_trait() else {
+            return;
+        };
+
+        let maybe_sized_params: DefIdMap<_> = type_param_bounds(generics)
+            .filter(|bound| {
+                bound.trait_bound.trait_ref.trait_def_id() == Some(sized_trait)
+                    && bound.modifier == TraitBoundModifier::Maybe
+            })
+            .map(|bound| (bound.param, bound))
+            .collect();
+
+        for bound in type_param_bounds(generics) {
+            if bound.modifier == TraitBoundModifier::None
+                && let Some(sized_bound) = maybe_sized_params.get(&bound.param)
+                && let Some(path) = path_to_sized_bound(cx, bound.trait_bound)
+            {
+                span_lint_and_then(
+                    cx,
+                    NEEDLESS_MAYBE_SIZED,
+                    sized_bound.trait_bound.span,
+                    "`?Sized` bound is ignored because of a `Sized` requirement",
+                    |diag| {
+                        let ty_param = sized_bound.ident;
+                        diag.span_note(
+                            bound.trait_bound.span,
+                            format!("`{ty_param}` cannot be unsized because of the bound"),
+                        );
+
+                        for &[current_id, next_id] in path.array_windows() {
+                            let current = cx.tcx.item_name(current_id);
+                            let next = cx.tcx.item_name(next_id);
+                            diag.note(format!("...because `{current}` has the bound `{next}`"));
+                        }
+
+                        diag.span_suggestion_verbose(
+                            generics.span_for_bound_removal(sized_bound.predicate_pos, sized_bound.bound_pos),
+                            "change the bounds that require `Sized`, or remove the `?Sized` bound",
+                            "",
+                            Applicability::MaybeIncorrect,
+                        );
+                    },
+                );
+
+                return;
+            }
+        }
+    }
+}
diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs
index da6ed5fb96f..57ba0da5331 100644
--- a/clippy_lints/src/needless_pass_by_ref_mut.rs
+++ b/clippy_lints/src/needless_pass_by_ref_mut.rs
@@ -1,7 +1,7 @@
 use super::needless_pass_by_value::requires_exact_signature;
 use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::source::snippet;
-use clippy_utils::visitors::for_each_expr_with_closures;
+use clippy_utils::visitors::for_each_expr;
 use clippy_utils::{inherits_cfg, is_from_proc_macro, is_self};
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
 use rustc_errors::Applicability;
@@ -205,7 +205,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
             // We retrieve all the closures declared in the function because they will not be found
             // by `euv::Delegate`.
             let mut closures: FxHashSet<LocalDefId> = FxHashSet::default();
-            for_each_expr_with_closures(cx, body, |expr| {
+            for_each_expr(cx, body, |expr| {
                 if let ExprKind::Closure(closure) = expr.kind {
                     closures.insert(closure.def_id);
                 }
diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs
index 932d6fe54d6..de6a1a36f3e 100644
--- a/clippy_lints/src/non_canonical_impls.rs
+++ b/clippy_lints/src/non_canonical_impls.rs
@@ -1,10 +1,11 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::ty::implements_trait;
-use clippy_utils::{is_res_lang_ctor, last_path_segment, path_res, std_or_core};
+use clippy_utils::{is_from_proc_macro, is_res_lang_ctor, last_path_segment, path_res, std_or_core};
 use rustc_errors::Applicability;
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, LangItem, Node, UnOp};
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::EarlyBinder;
 use rustc_session::declare_lint_pass;
 use rustc_span::sym;
@@ -111,7 +112,7 @@ declare_lint_pass!(NonCanonicalImpls => [NON_CANONICAL_CLONE_IMPL, NON_CANONICAL
 
 impl LateLintPass<'_> for NonCanonicalImpls {
     #[expect(clippy::too_many_lines)]
-    fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) {
+    fn check_impl_item<'tcx>(&mut self, cx: &LateContext<'tcx>, impl_item: &ImplItem<'tcx>) {
         let Node::Item(item) = cx.tcx.parent_hir_node(impl_item.hir_id()) else {
             return;
         };
@@ -128,6 +129,9 @@ impl LateLintPass<'_> for NonCanonicalImpls {
         let ExprKind::Block(block, ..) = body.value.kind else {
             return;
         };
+        if in_external_macro(cx.sess(), block.span) || is_from_proc_macro(cx, impl_item) {
+            return;
+        }
 
         if cx.tcx.is_diagnostic_item(sym::Clone, trait_impl.def_id)
             && let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy)
diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs
index 20a97645af9..5cb8e7bfab2 100644
--- a/clippy_lints/src/non_copy_const.rs
+++ b/clippy_lints/src/non_copy_const.rs
@@ -7,7 +7,7 @@ use std::ptr;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::in_constant;
 use clippy_utils::macros::macro_backtrace;
-use clippy_utils::ty::InteriorMut;
+use clippy_utils::ty::{implements_trait, InteriorMut};
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::{
@@ -18,7 +18,7 @@ use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId};
 use rustc_middle::ty::adjustment::Adjust;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_session::impl_lint_pass;
-use rustc_span::{sym, InnerSpan, Span, DUMMY_SP};
+use rustc_span::{sym, Span, DUMMY_SP};
 use rustc_target::abi::VariantIdx;
 
 // FIXME: this is a correctness problem but there's no suitable
@@ -127,19 +127,19 @@ declare_clippy_lint! {
 }
 
 #[derive(Copy, Clone)]
-enum Source {
-    Item { item: Span },
+enum Source<'tcx> {
+    Item { item: Span, ty: Ty<'tcx> },
     Assoc { item: Span },
     Expr { expr: Span },
 }
 
-impl Source {
+impl Source<'_> {
     #[must_use]
     fn lint(&self) -> (&'static Lint, &'static str, Span) {
         match self {
-            Self::Item { item } | Self::Assoc { item, .. } => (
+            Self::Item { item, .. } | Self::Assoc { item, .. } => (
                 DECLARE_INTERIOR_MUTABLE_CONST,
-                "a `const` item should never be interior mutable",
+                "a `const` item should not be interior mutable",
                 *item,
             ),
             Self::Expr { expr } => (
@@ -151,16 +151,24 @@ impl Source {
     }
 }
 
-fn lint(cx: &LateContext<'_>, source: Source) {
+fn lint<'tcx>(cx: &LateContext<'tcx>, source: Source<'tcx>) {
     let (lint, msg, span) = source.lint();
     span_lint_and_then(cx, lint, span, msg, |diag| {
         if span.from_expansion() {
             return; // Don't give suggestions into macros.
         }
         match source {
-            Source::Item { .. } => {
-                let const_kw_span = span.from_inner(InnerSpan::new(0, 5));
-                diag.span_label(const_kw_span, "make this a static item (maybe with lazy_static)");
+            Source::Item { ty, .. } => {
+                let Some(sync_trait) = cx.tcx.lang_items().sync_trait() else {
+                    return;
+                };
+                if implements_trait(cx, ty, sync_trait, &[]) {
+                    diag.help("consider making this a static item");
+                } else {
+                    diag.help(
+                        "consider making this `Sync` so that it can go in a static item or using a `thread_local`",
+                    );
+                }
             },
             Source::Assoc { .. } => (),
             Source::Expr { .. } => {
@@ -311,7 +319,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
                 && self.interior_mut.is_interior_mut_ty(cx, ty)
                 && Self::is_value_unfrozen_poly(cx, body_id, ty)
             {
-                lint(cx, Source::Item { item: it.span });
+                lint(cx, Source::Item { item: it.span, ty });
             }
         }
     }
diff --git a/clippy_lints/src/operators/assign_op_pattern.rs b/clippy_lints/src/operators/assign_op_pattern.rs
index 910e584a7a0..641d881d974 100644
--- a/clippy_lints/src/operators/assign_op_pattern.rs
+++ b/clippy_lints/src/operators/assign_op_pattern.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::implements_trait;
-use clippy_utils::visitors::for_each_expr;
+use clippy_utils::visitors::for_each_expr_without_closures;
 use clippy_utils::{binop_traits, eq_expr_value, trait_ref_of_method};
 use core::ops::ControlFlow;
 use rustc_errors::Applicability;
@@ -62,7 +62,7 @@ pub(super) fn check<'tcx>(
         };
 
         let mut found = false;
-        let found_multiple = for_each_expr(e, |e| {
+        let found_multiple = for_each_expr_without_closures(e, |e| {
             if eq_expr_value(cx, assignee, e) {
                 if found {
                     return ControlFlow::Break(());
diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs
index 806638c0505..381975199d2 100644
--- a/clippy_lints/src/panic_in_result_fn.rs
+++ b/clippy_lints/src/panic_in_result_fn.rs
@@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn {
 
 fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) {
     let mut panics = Vec::new();
-    let _: Option<!> = for_each_expr(body.value, |e| {
+    let _: Option<!> = for_each_expr(cx, body.value, |e| {
         let Some(macro_call) = root_macro_call_first_node(cx, e) else {
             return ControlFlow::Continue(Descend::Yes);
         };
diff --git a/clippy_lints/src/redundant_async_block.rs b/clippy_lints/src/redundant_async_block.rs
index 0ed957f1f2f..313e4083256 100644
--- a/clippy_lints/src/redundant_async_block.rs
+++ b/clippy_lints/src/redundant_async_block.rs
@@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::peel_blocks;
 use clippy_utils::source::{snippet, walk_span_to_context};
 use clippy_utils::ty::implements_trait;
-use clippy_utils::visitors::for_each_expr;
+use clippy_utils::visitors::for_each_expr_without_closures;
 use rustc_errors::Applicability;
 use rustc_hir::{
     Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, MatchSource,
@@ -107,7 +107,7 @@ fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
     if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
         && let ExprKind::Call(_, [into_future_arg]) = match_value.kind
         && let ctxt = expr.span.ctxt()
-        && for_each_expr(into_future_arg, |e| {
+        && for_each_expr_without_closures(into_future_arg, |e| {
             walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
         })
         .is_none()
diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs
index 16086ba6637..8f32cf5f2a1 100644
--- a/clippy_lints/src/reference.rs
+++ b/clippy_lints/src/reference.rs
@@ -48,6 +48,9 @@ impl EarlyLintPass for DerefAddrOf {
     fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
         if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind
             && let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind
+            // NOTE(tesuji): `*&` forces rustc to const-promote the array to `.rodata` section.
+            // See #12854 for details.
+            && !matches!(addrof_target.kind, ExprKind::Array(_))
             && deref_target.span.eq_ctxt(e.span)
             && !addrof_target.span.from_expansion()
         {
diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs
index 48d6fb3c037..c11da3147ef 100644
--- a/clippy_lints/src/returns.rs
+++ b/clippy_lints/src/returns.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
 use clippy_utils::source::{snippet_opt, snippet_with_context};
 use clippy_utils::sugg::has_enclosing_paren;
-use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
+use clippy_utils::visitors::{for_each_expr, Descend};
 use clippy_utils::{
     binary_expr_needs_parentheses, fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res,
     path_to_local_id, span_contains_cfg, span_find_starting_semi,
@@ -436,7 +436,7 @@ fn emit_return_lint(
 }
 
 fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
-    for_each_expr_with_closures(cx, expr, |e| {
+    for_each_expr(cx, expr, |e| {
         if let Some(def_id) = fn_def_id(cx, e)
             && cx
                 .tcx
diff --git a/clippy_lints/src/significant_drop_tightening.rs b/clippy_lints/src/significant_drop_tightening.rs
index 038eb92d652..979d6dc77ae 100644
--- a/clippy_lints/src/significant_drop_tightening.rs
+++ b/clippy_lints/src/significant_drop_tightening.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::{indent_of, snippet};
 use clippy_utils::{expr_or_init, get_attr, path_to_local, peel_hir_expr_unary};
-use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
+use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::intravisit::{walk_expr, Visitor};
@@ -12,6 +12,7 @@ use rustc_session::impl_lint_pass;
 use rustc_span::symbol::Ident;
 use rustc_span::{sym, Span, DUMMY_SP};
 use std::borrow::Cow;
+use std::collections::hash_map::Entry;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -57,7 +58,6 @@ impl_lint_pass!(SignificantDropTightening<'_> => [SIGNIFICANT_DROP_TIGHTENING]);
 pub struct SignificantDropTightening<'tcx> {
     apas: FxIndexMap<HirId, AuxParamsAttr>,
     /// Auxiliary structure used to avoid having to verify the same type multiple times.
-    seen_types: FxHashSet<Ty<'tcx>>,
     type_cache: FxHashMap<Ty<'tcx>, bool>,
 }
 
@@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for SignificantDropTightening<'tcx> {
         self.apas.clear();
         let initial_dummy_stmt = dummy_stmt_expr(body.value);
         let mut ap = AuxParams::new(&mut self.apas, &initial_dummy_stmt);
-        StmtsChecker::new(&mut ap, cx, &mut self.seen_types, &mut self.type_cache).visit_body(body);
+        StmtsChecker::new(&mut ap, cx, &mut self.type_cache).visit_body(body);
         for apa in ap.apas.values() {
             if apa.counter <= 1 || !apa.has_expensive_expr_after_last_attr {
                 continue;
@@ -142,28 +142,25 @@ impl<'tcx> LateLintPass<'tcx> for SignificantDropTightening<'tcx> {
 /// Checks the existence of the `#[has_significant_drop]` attribute.
 struct AttrChecker<'cx, 'others, 'tcx> {
     cx: &'cx LateContext<'tcx>,
-    seen_types: &'others mut FxHashSet<Ty<'tcx>>,
     type_cache: &'others mut FxHashMap<Ty<'tcx>, bool>,
 }
 
 impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> {
-    pub(crate) fn new(
-        cx: &'cx LateContext<'tcx>,
-        seen_types: &'others mut FxHashSet<Ty<'tcx>>,
-        type_cache: &'others mut FxHashMap<Ty<'tcx>, bool>,
-    ) -> Self {
-        seen_types.clear();
-        Self {
-            cx,
-            seen_types,
-            type_cache,
-        }
+    pub(crate) fn new(cx: &'cx LateContext<'tcx>, type_cache: &'others mut FxHashMap<Ty<'tcx>, bool>) -> Self {
+        Self { cx, type_cache }
     }
 
     fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>) -> bool {
-        // The borrow checker prevents us from using something fancier like or_insert_with.
-        if let Some(ty) = self.type_cache.get(&ty) {
-            return *ty;
+        let ty = self
+            .cx
+            .tcx
+            .try_normalize_erasing_regions(self.cx.param_env, ty)
+            .unwrap_or(ty);
+        match self.type_cache.entry(ty) {
+            Entry::Occupied(e) => return *e.get(),
+            Entry::Vacant(e) => {
+                e.insert(false);
+            },
         }
         let value = self.has_sig_drop_attr_uncached(ty);
         self.type_cache.insert(ty, value);
@@ -185,7 +182,7 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> {
             rustc_middle::ty::Adt(a, b) => {
                 for f in a.all_fields() {
                     let ty = f.ty(self.cx.tcx, b);
-                    if !self.has_seen_ty(ty) && self.has_sig_drop_attr(ty) {
+                    if self.has_sig_drop_attr(ty) {
                         return true;
                     }
                 }
@@ -205,16 +202,11 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> {
             _ => false,
         }
     }
-
-    fn has_seen_ty(&mut self, ty: Ty<'tcx>) -> bool {
-        !self.seen_types.insert(ty)
-    }
 }
 
 struct StmtsChecker<'ap, 'lc, 'others, 'stmt, 'tcx> {
     ap: &'ap mut AuxParams<'others, 'stmt, 'tcx>,
     cx: &'lc LateContext<'tcx>,
-    seen_types: &'others mut FxHashSet<Ty<'tcx>>,
     type_cache: &'others mut FxHashMap<Ty<'tcx>, bool>,
 }
 
@@ -222,15 +214,9 @@ impl<'ap, 'lc, 'others, 'stmt, 'tcx> StmtsChecker<'ap, 'lc, 'others, 'stmt, 'tcx
     fn new(
         ap: &'ap mut AuxParams<'others, 'stmt, 'tcx>,
         cx: &'lc LateContext<'tcx>,
-        seen_types: &'others mut FxHashSet<Ty<'tcx>>,
         type_cache: &'others mut FxHashMap<Ty<'tcx>, bool>,
     ) -> Self {
-        Self {
-            ap,
-            cx,
-            seen_types,
-            type_cache,
-        }
+        Self { ap, cx, type_cache }
     }
 
     fn manage_has_expensive_expr_after_last_attr(&mut self) {
@@ -288,7 +274,7 @@ impl<'ap, 'lc, 'others, 'stmt, 'tcx> Visitor<'tcx> for StmtsChecker<'ap, 'lc, 'o
             apa.counter = apa.counter.wrapping_add(1);
             apa.has_expensive_expr_after_last_attr = false;
         };
-        let mut ac = AttrChecker::new(self.cx, self.seen_types, self.type_cache);
+        let mut ac = AttrChecker::new(self.cx, self.type_cache);
         if ac.has_sig_drop_attr(self.cx.typeck_results().expr_ty(expr)) {
             if let hir::StmtKind::Let(local) = self.ap.curr_stmt.kind
                 && let hir::PatKind::Binding(_, hir_id, ident, _) = local.pat.kind
diff --git a/clippy_lints/src/string_patterns.rs b/clippy_lints/src/string_patterns.rs
new file mode 100644
index 00000000000..64b5b8f9f27
--- /dev/null
+++ b/clippy_lints/src/string_patterns.rs
@@ -0,0 +1,227 @@
+use std::ops::ControlFlow;
+
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::eager_or_lazy::switch_to_eager_eval;
+use clippy_utils::macros::matching_root_macro_call;
+use clippy_utils::path_to_local_id;
+use clippy_utils::source::{snippet, str_literal_to_char_literal};
+use clippy_utils::visitors::{for_each_expr, Descend};
+use itertools::Itertools;
+use rustc_ast::{BinOpKind, LitKind};
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind, PatKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
+use rustc_session::declare_lint_pass;
+use rustc_span::{sym, Span};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for manual `char` comparison in string patterns
+    ///
+    /// ### Why is this bad?
+    /// This can be written more concisely using a `char` or an array of `char`.
+    /// This is more readable and more optimized when comparing to only one `char`.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// "Hello World!".trim_end_matches(|c| c == '.' || c == ',' || c == '!' || c == '?');
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// "Hello World!".trim_end_matches(['.', ',', '!', '?']);
+    /// ```
+    #[clippy::version = "1.80.0"]
+    pub MANUAL_PATTERN_CHAR_COMPARISON,
+    style,
+    "manual char comparison in string patterns"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for string methods that receive a single-character
+    /// `str` as an argument, e.g., `_.split("x")`.
+    ///
+    /// ### Why is this bad?
+    /// While this can make a perf difference on some systems,
+    /// benchmarks have proven inconclusive. But at least using a
+    /// char literal makes it clear that we are looking at a single
+    /// character.
+    ///
+    /// ### Known problems
+    /// Does not catch multi-byte unicode characters. This is by
+    /// design, on many machines, splitting by a non-ascii char is
+    /// actually slower. Please do your own measurements instead of
+    /// relying solely on the results of this lint.
+    ///
+    /// ### Example
+    /// ```rust,ignore
+    /// _.split("x");
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust,ignore
+    /// _.split('x');
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub SINGLE_CHAR_PATTERN,
+    pedantic,
+    "using a single-character str where a char could be used, e.g., `_.split(\"x\")`"
+}
+
+declare_lint_pass!(StringPatterns => [MANUAL_PATTERN_CHAR_COMPARISON, SINGLE_CHAR_PATTERN]);
+
+const PATTERN_METHODS: [(&str, usize); 22] = [
+    ("contains", 0),
+    ("starts_with", 0),
+    ("ends_with", 0),
+    ("find", 0),
+    ("rfind", 0),
+    ("split", 0),
+    ("split_inclusive", 0),
+    ("rsplit", 0),
+    ("split_terminator", 0),
+    ("rsplit_terminator", 0),
+    ("splitn", 1),
+    ("rsplitn", 1),
+    ("split_once", 0),
+    ("rsplit_once", 0),
+    ("matches", 0),
+    ("rmatches", 0),
+    ("match_indices", 0),
+    ("rmatch_indices", 0),
+    ("trim_start_matches", 0),
+    ("trim_end_matches", 0),
+    ("replace", 0),
+    ("replacen", 0),
+];
+
+fn check_single_char_pattern_lint(cx: &LateContext<'_>, arg: &Expr<'_>) {
+    let mut applicability = Applicability::MachineApplicable;
+    if let Some(hint) = str_literal_to_char_literal(cx, arg, &mut applicability, true) {
+        span_lint_and_sugg(
+            cx,
+            SINGLE_CHAR_PATTERN,
+            arg.span,
+            "single-character string constant used as pattern",
+            "consider using a `char`",
+            hint,
+            applicability,
+        );
+    }
+}
+
+fn get_char_span<'tcx>(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<Span> {
+    if cx.typeck_results().expr_ty_adjusted(expr).is_char()
+        && !expr.span.from_expansion()
+        && switch_to_eager_eval(cx, expr)
+    {
+        Some(expr.span)
+    } else {
+        None
+    }
+}
+
+fn check_manual_pattern_char_comparison(cx: &LateContext<'_>, method_arg: &Expr<'_>) {
+    if let ExprKind::Closure(closure) = method_arg.kind
+        && let body = cx.tcx.hir().body(closure.body)
+        && let Some(PatKind::Binding(_, binding, ..)) = body.params.first().map(|p| p.pat.kind)
+    {
+        let mut set_char_spans: Vec<Span> = Vec::new();
+
+        // We want to retrieve all the comparisons done.
+        // They are ordered in a nested way and so we need to traverse the AST to collect them all.
+        if for_each_expr(cx, body.value, |sub_expr| -> ControlFlow<(), Descend> {
+            match sub_expr.kind {
+                ExprKind::Binary(op, left, right) if op.node == BinOpKind::Eq => {
+                    if path_to_local_id(left, binding)
+                        && let Some(span) = get_char_span(cx, right)
+                    {
+                        set_char_spans.push(span);
+                        ControlFlow::Continue(Descend::No)
+                    } else if path_to_local_id(right, binding)
+                        && let Some(span) = get_char_span(cx, left)
+                    {
+                        set_char_spans.push(span);
+                        ControlFlow::Continue(Descend::No)
+                    } else {
+                        ControlFlow::Break(())
+                    }
+                },
+                ExprKind::Binary(op, _, _) if op.node == BinOpKind::Or => ControlFlow::Continue(Descend::Yes),
+                ExprKind::Match(match_value, [arm, _], _) => {
+                    if matching_root_macro_call(cx, sub_expr.span, sym::matches_macro).is_none()
+                        || arm.guard.is_some()
+                        || !path_to_local_id(match_value, binding)
+                    {
+                        return ControlFlow::Break(());
+                    }
+                    if arm.pat.walk_short(|pat| match pat.kind {
+                        PatKind::Lit(expr) if let ExprKind::Lit(lit) = expr.kind => {
+                            if let LitKind::Char(_) = lit.node {
+                                set_char_spans.push(lit.span);
+                            }
+                            true
+                        },
+                        PatKind::Or(_) => true,
+                        _ => false,
+                    }) {
+                        ControlFlow::Continue(Descend::No)
+                    } else {
+                        ControlFlow::Break(())
+                    }
+                },
+                _ => ControlFlow::Break(()),
+            }
+        })
+        .is_some()
+        {
+            return;
+        }
+        span_lint_and_then(
+            cx,
+            MANUAL_PATTERN_CHAR_COMPARISON,
+            method_arg.span,
+            "this manual char comparison can be written more succinctly",
+            |diag| {
+                if let [set_char_span] = set_char_spans[..] {
+                    diag.span_suggestion(
+                        method_arg.span,
+                        "consider using a `char`",
+                        snippet(cx, set_char_span, "c"),
+                        Applicability::MachineApplicable,
+                    );
+                } else {
+                    diag.span_suggestion(
+                        method_arg.span,
+                        "consider using an array of `char`",
+                        format!(
+                            "[{}]",
+                            set_char_spans.into_iter().map(|span| snippet(cx, span, "c")).join(", ")
+                        ),
+                        Applicability::MachineApplicable,
+                    );
+                }
+            },
+        );
+    }
+}
+
+impl<'tcx> LateLintPass<'tcx> for StringPatterns {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if !expr.span.from_expansion()
+            && let ExprKind::MethodCall(method, receiver, args, _) = expr.kind
+            && let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(receiver).kind()
+            && ty.is_str()
+            && let method_name = method.ident.name.as_str()
+            && let Some(&(_, pos)) = PATTERN_METHODS
+                .iter()
+                .find(|(array_method_name, _)| *array_method_name == method_name)
+            && let Some(arg) = args.get(pos)
+        {
+            check_single_char_pattern_lint(cx, arg);
+
+            check_manual_pattern_char_comparison(cx, arg);
+        }
+    }
+}
diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs
index f8d36d6b92f..7da661485ab 100644
--- a/clippy_lints/src/strings.rs
+++ b/clippy_lints/src/strings.rs
@@ -399,13 +399,17 @@ impl<'tcx> LateLintPass<'tcx> for StrToString {
             && let ty::Ref(_, ty, ..) = ty.kind()
             && ty.is_str()
         {
-            span_lint_and_help(
+            let mut applicability = Applicability::MachineApplicable;
+            let snippet = snippet_with_applicability(cx, self_arg.span, "..", &mut applicability);
+
+            span_lint_and_sugg(
                 cx,
                 STR_TO_STRING,
                 expr.span,
                 "`to_string()` called on a `&str`",
-                None,
-                "consider using `.to_owned()`",
+                "try",
+                format!("{snippet}.to_owned()"),
+                applicability,
             );
         }
     }
diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs
index 3f030b80331..744d6392e06 100644
--- a/clippy_lints/src/suspicious_trait_impl.rs
+++ b/clippy_lints/src/suspicious_trait_impl.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::visitors::for_each_expr;
+use clippy_utils::visitors::for_each_expr_without_closures;
 use clippy_utils::{binop_traits, trait_ref_of_method, BINOP_TRAITS, OP_ASSIGN_TRAITS};
 use core::ops::ControlFlow;
 use rustc_hir as hir;
@@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl {
 
 fn count_binops(expr: &hir::Expr<'_>) -> u32 {
     let mut count = 0u32;
-    let _: Option<!> = for_each_expr(expr, |e| {
+    let _: Option<!> = for_each_expr_without_closures(expr, |e| {
         if matches!(
             e.kind,
             hir::ExprKind::Binary(..)
diff --git a/clippy_lints/src/transmute/missing_transmute_annotations.rs b/clippy_lints/src/transmute/missing_transmute_annotations.rs
index f98ea59a15d..b2892d136fa 100644
--- a/clippy_lints/src/transmute/missing_transmute_annotations.rs
+++ b/clippy_lints/src/transmute/missing_transmute_annotations.rs
@@ -31,7 +31,6 @@ fn get_parent_local_binding_ty<'tcx>(cx: &LateContext<'tcx>, expr_hir_id: HirId)
 fn is_function_block(cx: &LateContext<'_>, expr_hir_id: HirId) -> bool {
     let def_id = cx.tcx.hir().enclosing_body_owner(expr_hir_id);
     if let Some(body) = cx.tcx.hir().maybe_body_owned_by(def_id) {
-        let body = cx.tcx.hir().body(body.id());
         return body.value.peel_blocks().hir_id == expr_hir_id;
     }
     false
diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs
index 598032ccdeb..aa329ec3366 100644
--- a/clippy_lints/src/transmute/mod.rs
+++ b/clippy_lints/src/transmute/mod.rs
@@ -546,7 +546,7 @@ declare_clippy_lint! {
     /// let x = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]);
     /// # }
     /// ```
-    #[clippy::version = "1.77.0"]
+    #[clippy::version = "1.79.0"]
     pub MISSING_TRANSMUTE_ANNOTATIONS,
     suspicious,
     "warns if a transmute call doesn't have all generics specified"
diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs
index 6cf9229fdd0..93a1089a970 100644
--- a/clippy_lints/src/undocumented_unsafe_blocks.rs
+++ b/clippy_lints/src/undocumented_unsafe_blocks.rs
@@ -3,7 +3,7 @@ use std::ops::ControlFlow;
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::is_lint_allowed;
 use clippy_utils::source::walk_span_to_context;
-use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
+use clippy_utils::visitors::{for_each_expr, Descend};
 use hir::HirId;
 use rustc_data_structures::sync::Lrc;
 use rustc_hir as hir;
@@ -296,7 +296,7 @@ fn expr_has_unnecessary_safety_comment<'tcx>(
     }
 
     // this should roughly be the reverse of `block_parents_have_safety_comment`
-    if for_each_expr_with_closures(cx, expr, |expr| match expr.kind {
+    if for_each_expr(cx, expr, |expr| match expr.kind {
         hir::ExprKind::Block(
             Block {
                 rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
diff --git a/clippy_lints/src/unused_self.rs b/clippy_lints/src/unused_self.rs
index a67f53f00ae..3e6102f5982 100644
--- a/clippy_lints/src/unused_self.rs
+++ b/clippy_lints/src/unused_self.rs
@@ -59,7 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf {
         let parent_item = cx.tcx.hir().expect_item(parent);
         let assoc_item = cx.tcx.associated_item(impl_item.owner_id);
         let contains_todo = |cx, body: &'_ Body<'_>| -> bool {
-            clippy_utils::visitors::for_each_expr(body.value, |e| {
+            clippy_utils::visitors::for_each_expr_without_closures(body.value, |e| {
                 if let Some(macro_call) = root_macro_call_first_node(cx, e) {
                     if cx.tcx.item_name(macro_call.def_id).as_str() == "todo" {
                         ControlFlow::Break(())
diff --git a/clippy_lints/src/unwrap_in_result.rs b/clippy_lints/src/unwrap_in_result.rs
index 197ab0f173b..f77badd97b7 100644
--- a/clippy_lints/src/unwrap_in_result.rs
+++ b/clippy_lints/src/unwrap_in_result.rs
@@ -77,7 +77,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tc
         let body = cx.tcx.hir().body(body_id);
         let typeck = cx.tcx.typeck(impl_item.owner_id.def_id);
         let mut result = Vec::new();
-        let _: Option<!> = for_each_expr(body.value, |e| {
+        let _: Option<!> = for_each_expr(cx, body.value, |e| {
             // check for `expect`
             if let Some(arglists) = method_chain_args(e, &["expect"]) {
                 let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs();
diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs
index 18a31abddd0..e9d69407df8 100644
--- a/clippy_lints/src/utils/author.rs
+++ b/clippy_lints/src/utils/author.rs
@@ -733,7 +733,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
         match stmt.value.kind {
             StmtKind::Let(local) => {
                 bind!(self, local);
-                kind!("Local({local})");
+                kind!("Let({local})");
                 self.option(field!(local.init), "init", |init| {
                     self.expr(init);
                 });
diff --git a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
index 9be225759df..84f84781e71 100644
--- a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
+++ b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
@@ -213,7 +213,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
                     output: &mut self.registered_lints,
                     cx,
                 };
-                let body_id = cx.tcx.hir().body_owned_by(
+                let body = cx.tcx.hir().body_owned_by(
                     impl_item_refs
                         .iter()
                         .find(|iiref| iiref.ident.as_str() == "get_lints")
@@ -222,7 +222,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
                         .owner_id
                         .def_id,
                 );
-                collector.visit_expr(cx.tcx.hir().body(body_id).value);
+                collector.visit_expr(body.value);
             }
         }
     }
diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs
index 5c1ebb922f1..1c149f20456 100644
--- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs
+++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs
@@ -719,7 +719,7 @@ fn get_lint_group_and_level_or_lint(
         Some(sym::clippy),
         &std::iter::once(Ident::with_dummy_span(sym::clippy)).collect(),
     );
-    if let CheckLintNameResult::Tool(Ok(lint_lst)) = result {
+    if let CheckLintNameResult::Tool(lint_lst, None) = result {
         if let Some(group) = get_lint_group(cx, lint_lst[0]) {
             if EXCLUDED_LINT_GROUPS.contains(&group.as_str()) {
                 return None;
diff --git a/clippy_lints/src/zero_repeat_side_effects.rs b/clippy_lints/src/zero_repeat_side_effects.rs
index 8796b8f61d1..ad041e55bda 100644
--- a/clippy_lints/src/zero_repeat_side_effects.rs
+++ b/clippy_lints/src/zero_repeat_side_effects.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::higher::VecArgs;
 use clippy_utils::source::snippet;
-use clippy_utils::visitors::for_each_expr;
+use clippy_utils::visitors::for_each_expr_without_closures;
 use rustc_ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::{ExprKind, Node};
@@ -36,7 +36,7 @@ declare_clippy_lint! {
     /// side_effect();
     /// let a: [i32; 0] = [];
     /// ```
-    #[clippy::version = "1.75.0"]
+    #[clippy::version = "1.79.0"]
     pub ZERO_REPEAT_SIDE_EFFECTS,
     suspicious,
     "usage of zero-sized initializations of arrays or vecs causing side effects"
@@ -65,7 +65,7 @@ impl LateLintPass<'_> for ZeroRepeatSideEffects {
 
 fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: &'_ rustc_hir::Expr<'_>, is_vec: bool) {
     // check if expr is a call or has a call inside it
-    if for_each_expr(inner_expr, |x| {
+    if for_each_expr_without_closures(inner_expr, |x| {
         if let ExprKind::Call(_, _) | ExprKind::MethodCall(_, _, _, _) = x.kind {
             std::ops::ControlFlow::Break(())
         } else {