about summary refs log tree commit diff
path: root/clippy_lints/src
diff options
context:
space:
mode:
Diffstat (limited to 'clippy_lints/src')
-rw-r--r--clippy_lints/src/attrs/allow_attributes_without_reason.rs2
-rw-r--r--clippy_lints/src/attrs/duplicated_attributes.rs2
-rw-r--r--clippy_lints/src/attrs/empty_line_after.rs52
-rw-r--r--clippy_lints/src/attrs/mod.rs95
-rw-r--r--clippy_lints/src/attrs/useless_attribute.rs1
-rw-r--r--clippy_lints/src/byte_char_slices.rs2
-rw-r--r--clippy_lints/src/casts/mod.rs11
-rw-r--r--clippy_lints/src/casts/ptr_cast_constness.rs79
-rw-r--r--clippy_lints/src/cfg_not_test.rs2
-rw-r--r--clippy_lints/src/declared_lints.rs13
-rw-r--r--clippy_lints/src/doc/empty_line_after.rs329
-rw-r--r--clippy_lints/src/doc/lazy_continuation.rs24
-rw-r--r--clippy_lints/src/doc/mod.rs87
-rw-r--r--clippy_lints/src/doc/suspicious_doc_comments.rs6
-rw-r--r--clippy_lints/src/doc/too_long_first_doc_paragraph.rs9
-rw-r--r--clippy_lints/src/field_scoped_visibility_modifiers.rs2
-rw-r--r--clippy_lints/src/from_over_into.rs10
-rw-r--r--clippy_lints/src/if_then_some_else_none.rs2
-rw-r--r--clippy_lints/src/implicit_saturating_sub.rs359
-rw-r--r--clippy_lints/src/lib.rs17
-rw-r--r--clippy_lints/src/loops/mod.rs14
-rw-r--r--clippy_lints/src/manual_div_ceil.rs158
-rw-r--r--clippy_lints/src/manual_is_power_of_two.rs142
-rw-r--r--clippy_lints/src/manual_non_exhaustive.rs182
-rw-r--r--clippy_lints/src/manual_range_patterns.rs5
-rw-r--r--clippy_lints/src/matches/single_match.rs27
-rw-r--r--clippy_lints/src/methods/iter_kv_map.rs1
-rw-r--r--clippy_lints/src/methods/mod.rs50
-rw-r--r--clippy_lints/src/methods/unnecessary_first_then_check.rs56
-rw-r--r--clippy_lints/src/methods/unnecessary_fold.rs26
-rw-r--r--clippy_lints/src/methods/unnecessary_min_or_max.rs45
-rw-r--r--clippy_lints/src/methods/unnecessary_sort_by.rs3
-rw-r--r--clippy_lints/src/methods/unnecessary_to_owned.rs2
-rw-r--r--clippy_lints/src/misc.rs180
-rw-r--r--clippy_lints/src/mixed_read_write_in_expression.rs6
-rw-r--r--clippy_lints/src/mutable_debug_assertion.rs4
-rw-r--r--clippy_lints/src/needless_maybe_sized.rs2
-rw-r--r--clippy_lints/src/needless_pass_by_value.rs17
-rw-r--r--clippy_lints/src/non_zero_suggestions.rs143
-rw-r--r--clippy_lints/src/panic_in_result_fn.rs12
-rw-r--r--clippy_lints/src/panic_unimplemented.rs78
-rw-r--r--clippy_lints/src/pointers_in_nomem_asm_block.rs88
-rw-r--r--clippy_lints/src/ptr.rs102
-rw-r--r--clippy_lints/src/returns.rs22
-rw-r--r--clippy_lints/src/set_contains_or_insert.rs2
-rw-r--r--clippy_lints/src/string_patterns.rs2
-rw-r--r--clippy_lints/src/transmute/mod.rs32
-rw-r--r--clippy_lints/src/types/mod.rs55
-rw-r--r--clippy_lints/src/unused_trait_names.rs94
-rw-r--r--clippy_lints/src/useless_conversion.rs4
-rw-r--r--clippy_lints/src/zombie_processes.rs334
51 files changed, 2396 insertions, 596 deletions
diff --git a/clippy_lints/src/attrs/allow_attributes_without_reason.rs b/clippy_lints/src/attrs/allow_attributes_without_reason.rs
index 4ab97118df1..4c7e07478c1 100644
--- a/clippy_lints/src/attrs/allow_attributes_without_reason.rs
+++ b/clippy_lints/src/attrs/allow_attributes_without_reason.rs
@@ -26,7 +26,7 @@ pub(super) fn check<'cx>(cx: &LateContext<'cx>, name: Symbol, items: &[NestedMet
         cx,
         ALLOW_ATTRIBUTES_WITHOUT_REASON,
         attr.span,
-        format!("`{}` attribute without specifying a reason", name.as_str()),
+        format!("`{name}` attribute without specifying a reason"),
         |diag| {
             diag.help("try adding a reason at the end with `, reason = \"..\"`");
         },
diff --git a/clippy_lints/src/attrs/duplicated_attributes.rs b/clippy_lints/src/attrs/duplicated_attributes.rs
index 40a1c4e2884..199e07565b0 100644
--- a/clippy_lints/src/attrs/duplicated_attributes.rs
+++ b/clippy_lints/src/attrs/duplicated_attributes.rs
@@ -36,7 +36,7 @@ fn check_duplicated_attr(
     }
     let Some(ident) = attr.ident() else { return };
     let name = ident.name;
-    if name == sym::doc || name == sym::cfg_attr || name == sym::rustc_on_unimplemented {
+    if name == sym::doc || name == sym::cfg_attr || name == sym::rustc_on_unimplemented || name == sym::reason {
         // FIXME: Would be nice to handle `cfg_attr` as well. Only problem is to check that cfg
         // conditions are the same.
         // `#[rustc_on_unimplemented]` contains duplicated subattributes, that's expected.
diff --git a/clippy_lints/src/attrs/empty_line_after.rs b/clippy_lints/src/attrs/empty_line_after.rs
deleted file mode 100644
index 7ff644b4c44..00000000000
--- a/clippy_lints/src/attrs/empty_line_after.rs
+++ /dev/null
@@ -1,52 +0,0 @@
-use super::{EMPTY_LINE_AFTER_DOC_COMMENTS, EMPTY_LINE_AFTER_OUTER_ATTR};
-use clippy_utils::diagnostics::span_lint;
-use clippy_utils::source::{is_present_in_source, without_block_comments, SpanRangeExt};
-use rustc_ast::{AttrKind, AttrStyle};
-use rustc_lint::EarlyContext;
-use rustc_span::Span;
-
-/// Check for empty lines after outer attributes.
-///
-/// Attributes and documentation comments are both considered outer attributes
-/// by the AST. However, the average user likely considers them to be different.
-/// Checking for empty lines after each of these attributes is split into two different
-/// lints but can share the same logic.
-pub(super) fn check(cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
-    let mut iter = item.attrs.iter().peekable();
-    while let Some(attr) = iter.next() {
-        if (matches!(attr.kind, AttrKind::Normal(..)) || matches!(attr.kind, AttrKind::DocComment(..)))
-            && attr.style == AttrStyle::Outer
-            && is_present_in_source(cx, attr.span)
-        {
-            let begin_of_attr_to_item = Span::new(attr.span.lo(), item.span.lo(), item.span.ctxt(), item.span.parent());
-            let end_of_attr_to_next_attr_or_item = Span::new(
-                attr.span.hi(),
-                iter.peek().map_or(item.span.lo(), |next_attr| next_attr.span.lo()),
-                item.span.ctxt(),
-                item.span.parent(),
-            );
-
-            if let Some(snippet) = end_of_attr_to_next_attr_or_item.get_source_text(cx) {
-                let lines = snippet.split('\n').collect::<Vec<_>>();
-                let lines = without_block_comments(lines);
-
-                if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 {
-                    let (lint_msg, lint_type) = match attr.kind {
-                        AttrKind::DocComment(..) => (
-                            "found an empty line after a doc comment. \
-                            Perhaps you need to use `//!` to make a comment on a module, remove the empty line, or make a regular comment with `//`?",
-                            EMPTY_LINE_AFTER_DOC_COMMENTS,
-                        ),
-                        AttrKind::Normal(..) => (
-                            "found an empty line after an outer attribute. \
-                            Perhaps you forgot to add a `!` to make it an inner attribute?",
-                            EMPTY_LINE_AFTER_OUTER_ATTR,
-                        ),
-                    };
-
-                    span_lint(cx, lint_type, begin_of_attr_to_item, lint_msg);
-                }
-            }
-        }
-    }
-}
diff --git a/clippy_lints/src/attrs/mod.rs b/clippy_lints/src/attrs/mod.rs
index 3b14e9aee7f..c8fea25c9f2 100644
--- a/clippy_lints/src/attrs/mod.rs
+++ b/clippy_lints/src/attrs/mod.rs
@@ -4,7 +4,6 @@ mod blanket_clippy_restriction_lints;
 mod deprecated_cfg_attr;
 mod deprecated_semver;
 mod duplicated_attributes;
-mod empty_line_after;
 mod inline_always;
 mod mixed_attributes_style;
 mod non_minimal_cfg;
@@ -128,94 +127,6 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for empty lines after outer attributes
-    ///
-    /// ### Why is this bad?
-    /// Most likely the attribute was meant to be an inner attribute using a '!'.
-    /// If it was meant to be an outer attribute, then the following item
-    /// should not be separated by empty lines.
-    ///
-    /// ### Known problems
-    /// Can cause false positives.
-    ///
-    /// From the clippy side it's difficult to detect empty lines between an attributes and the
-    /// following item because empty lines and comments are not part of the AST. The parsing
-    /// currently works for basic cases but is not perfect.
-    ///
-    /// ### Example
-    /// ```no_run
-    /// #[allow(dead_code)]
-    ///
-    /// fn not_quite_good_code() { }
-    /// ```
-    ///
-    /// Use instead:
-    /// ```no_run
-    /// // Good (as inner attribute)
-    /// #![allow(dead_code)]
-    ///
-    /// fn this_is_fine() { }
-    ///
-    /// // or
-    ///
-    /// // Good (as outer attribute)
-    /// #[allow(dead_code)]
-    /// fn this_is_fine_too() { }
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub EMPTY_LINE_AFTER_OUTER_ATTR,
-    nursery,
-    "empty line after outer attribute"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for empty lines after documentation comments.
-    ///
-    /// ### Why is this bad?
-    /// The documentation comment was most likely meant to be an inner attribute or regular comment.
-    /// If it was intended to be a documentation comment, then the empty line should be removed to
-    /// be more idiomatic.
-    ///
-    /// ### Known problems
-    /// Only detects empty lines immediately following the documentation. If the doc comment is followed
-    /// by an attribute and then an empty line, this lint will not trigger. Use `empty_line_after_outer_attr`
-    /// in combination with this lint to detect both cases.
-    ///
-    /// Does not detect empty lines after doc attributes (e.g. `#[doc = ""]`).
-    ///
-    /// ### Example
-    /// ```no_run
-    /// /// Some doc comment with a blank line after it.
-    ///
-    /// fn not_quite_good_code() { }
-    /// ```
-    ///
-    /// Use instead:
-    /// ```no_run
-    /// /// Good (no blank line)
-    /// fn this_is_fine() { }
-    /// ```
-    ///
-    /// ```no_run
-    /// // Good (convert to a regular comment)
-    ///
-    /// fn this_is_fine_too() { }
-    /// ```
-    ///
-    /// ```no_run
-    /// //! Good (convert to a comment on an inner attribute)
-    ///
-    /// fn this_is_fine_as_well() { }
-    /// ```
-    #[clippy::version = "1.70.0"]
-    pub EMPTY_LINE_AFTER_DOC_COMMENTS,
-    nursery,
-    "empty line after documentation comments"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
     /// Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category.
     ///
     /// ### Why is this bad?
@@ -601,18 +512,12 @@ impl EarlyAttributes {
 
 impl_lint_pass!(EarlyAttributes => [
     DEPRECATED_CFG_ATTR,
-    EMPTY_LINE_AFTER_OUTER_ATTR,
-    EMPTY_LINE_AFTER_DOC_COMMENTS,
     NON_MINIMAL_CFG,
     DEPRECATED_CLIPPY_CFG_ATTR,
     UNNECESSARY_CLIPPY_CFG,
 ]);
 
 impl EarlyLintPass for EarlyAttributes {
-    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
-        empty_line_after::check(cx, item);
-    }
-
     fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
         deprecated_cfg_attr::check(cx, attr, &self.msrv);
         deprecated_cfg_attr::check_clippy(cx, attr);
diff --git a/clippy_lints/src/attrs/useless_attribute.rs b/clippy_lints/src/attrs/useless_attribute.rs
index 67ba605a59f..1296b59f2e1 100644
--- a/clippy_lints/src/attrs/useless_attribute.rs
+++ b/clippy_lints/src/attrs/useless_attribute.rs
@@ -51,6 +51,7 @@ pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute])
                                         | "module_name_repetitions"
                                         | "single_component_path_imports"
                                         | "disallowed_types"
+                                        | "unused_trait_names"
                                 )
                             }) {
                                 return;
diff --git a/clippy_lints/src/byte_char_slices.rs b/clippy_lints/src/byte_char_slices.rs
index a9fe190f177..dd2620b0b9d 100644
--- a/clippy_lints/src/byte_char_slices.rs
+++ b/clippy_lints/src/byte_char_slices.rs
@@ -22,7 +22,7 @@ declare_clippy_lint! {
     /// ```ignore
     /// b"Hello"
     /// ```
-    #[clippy::version = "1.68.0"]
+    #[clippy::version = "1.81.0"]
     pub BYTE_CHAR_SLICES,
     style,
     "hard to read byte char slice"
diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs
index c31716fbcee..39f50a347c0 100644
--- a/clippy_lints/src/casts/mod.rs
+++ b/clippy_lints/src/casts/mod.rs
@@ -410,19 +410,27 @@ declare_clippy_lint! {
     /// ### Why is this bad?
     /// Though `as` casts between raw pointers are not terrible, `pointer::cast_mut` and
     /// `pointer::cast_const` are safer because they cannot accidentally cast the pointer to another
-    /// type.
+    /// type. Or, when null pointers are involved, `null()` and `null_mut()` can be used directly.
     ///
     /// ### Example
     /// ```no_run
     /// let ptr: *const u32 = &42_u32;
     /// let mut_ptr = ptr as *mut u32;
     /// let ptr = mut_ptr as *const u32;
+    /// let ptr1 = std::ptr::null::<u32>() as *mut u32;
+    /// let ptr2 = std::ptr::null_mut::<u32>() as *const u32;
+    /// let ptr3 = std::ptr::null::<u32>().cast_mut();
+    /// let ptr4 = std::ptr::null_mut::<u32>().cast_const();
     /// ```
     /// Use instead:
     /// ```no_run
     /// let ptr: *const u32 = &42_u32;
     /// let mut_ptr = ptr.cast_mut();
     /// let ptr = mut_ptr.cast_const();
+    /// let ptr1 = std::ptr::null_mut::<u32>();
+    /// let ptr2 = std::ptr::null::<u32>();
+    /// let ptr3 = std::ptr::null_mut::<u32>();
+    /// let ptr4 = std::ptr::null::<u32>();
     /// ```
     #[clippy::version = "1.72.0"]
     pub PTR_CAST_CONSTNESS,
@@ -809,6 +817,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
         char_lit_as_u8::check(cx, expr);
         ptr_as_ptr::check(cx, expr, &self.msrv);
         cast_slice_different_sizes::check(cx, expr, &self.msrv);
+        ptr_cast_constness::check_null_ptr_cast_method(cx, expr);
     }
 
     extract_msrv_attr!(LateContext);
diff --git a/clippy_lints/src/casts/ptr_cast_constness.rs b/clippy_lints/src/casts/ptr_cast_constness.rs
index 7513e18d408..7518dd2435a 100644
--- a/clippy_lints/src/casts/ptr_cast_constness.rs
+++ b/clippy_lints/src/casts/ptr_cast_constness.rs
@@ -1,10 +1,12 @@
 use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::std_or_core;
 use clippy_utils::sugg::Sugg;
 use rustc_errors::Applicability;
-use rustc_hir::{Expr, Mutability};
+use rustc_hir::{Expr, ExprKind, Mutability, QPath};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty, TypeVisitableExt};
+use rustc_span::sym;
 
 use super::PTR_CAST_CONSTNESS;
 
@@ -16,8 +18,7 @@ pub(super) fn check<'tcx>(
     cast_to: Ty<'tcx>,
     msrv: &Msrv,
 ) {
-    if msrv.meets(msrvs::POINTER_CAST_CONSTNESS)
-        && let ty::RawPtr(from_ty, from_mutbl) = cast_from.kind()
+    if let ty::RawPtr(from_ty, from_mutbl) = cast_from.kind()
         && let ty::RawPtr(to_ty, to_mutbl) = cast_to.kind()
         && matches!(
             (from_mutbl, to_mutbl),
@@ -26,20 +27,74 @@ pub(super) fn check<'tcx>(
         && from_ty == to_ty
         && !from_ty.has_erased_regions()
     {
-        let sugg = Sugg::hir(cx, cast_expr, "_");
-        let constness = match *to_mutbl {
-            Mutability::Not => "const",
-            Mutability::Mut => "mut",
-        };
+        if let ExprKind::Call(func, []) = cast_expr.kind
+            && let ExprKind::Path(QPath::Resolved(None, path)) = func.kind
+            && let Some(defid) = path.res.opt_def_id()
+            && let Some(prefix) = std_or_core(cx)
+            && let mut app = Applicability::MachineApplicable
+            && let sugg = format!("{}", Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app))
+            && let Some((_, after_lt)) = sugg.split_once("::<")
+            && let Some((source, target, target_func)) = match cx.tcx.get_diagnostic_name(defid) {
+                Some(sym::ptr_null) => Some(("const", "mutable", "null_mut")),
+                Some(sym::ptr_null_mut) => Some(("mutable", "const", "null")),
+                _ => None,
+            }
+        {
+            span_lint_and_sugg(
+                cx,
+                PTR_CAST_CONSTNESS,
+                expr.span,
+                format!("`as` casting to make a {source} null pointer into a {target} null pointer"),
+                format!("use `{target_func}()` directly instead"),
+                format!("{prefix}::ptr::{target_func}::<{after_lt}"),
+                app,
+            );
+            return;
+        }
 
+        if msrv.meets(msrvs::POINTER_CAST_CONSTNESS) {
+            let sugg = Sugg::hir(cx, cast_expr, "_");
+            let constness = match *to_mutbl {
+                Mutability::Not => "const",
+                Mutability::Mut => "mut",
+            };
+
+            span_lint_and_sugg(
+                cx,
+                PTR_CAST_CONSTNESS,
+                expr.span,
+                "`as` casting between raw pointers while changing only its constness",
+                format!("try `pointer::cast_{constness}`, a safer alternative"),
+                format!("{}.cast_{constness}()", sugg.maybe_par()),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+}
+
+pub(super) fn check_null_ptr_cast_method(cx: &LateContext<'_>, expr: &Expr<'_>) {
+    if let ExprKind::MethodCall(method, cast_expr, [], _) = expr.kind
+        && let ExprKind::Call(func, []) = cast_expr.kind
+        && let ExprKind::Path(QPath::Resolved(None, path)) = func.kind
+        && let Some(defid) = path.res.opt_def_id()
+        && let method = match (cx.tcx.get_diagnostic_name(defid), method.ident.as_str()) {
+            (Some(sym::ptr_null), "cast_mut") => "null_mut",
+            (Some(sym::ptr_null_mut), "cast_const") => "null",
+            _ => return,
+        }
+        && let Some(prefix) = std_or_core(cx)
+        && let mut app = Applicability::MachineApplicable
+        && let sugg = format!("{}", Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app))
+        && let Some((_, after_lt)) = sugg.split_once("::<")
+    {
         span_lint_and_sugg(
             cx,
             PTR_CAST_CONSTNESS,
             expr.span,
-            "`as` casting between raw pointers while changing only its constness",
-            format!("try `pointer::cast_{constness}`, a safer alternative"),
-            format!("{}.cast_{constness}()", sugg.maybe_par()),
-            Applicability::MachineApplicable,
+            "changing constness of a null pointer",
+            format!("use `{method}()` directly instead"),
+            format!("{prefix}::ptr::{method}::<{after_lt}"),
+            app,
         );
     }
 }
diff --git a/clippy_lints/src/cfg_not_test.rs b/clippy_lints/src/cfg_not_test.rs
index d820c1e0720..884d15cabff 100644
--- a/clippy_lints/src/cfg_not_test.rs
+++ b/clippy_lints/src/cfg_not_test.rs
@@ -22,7 +22,7 @@ declare_clippy_lint! {
     /// # fn important_check() {}
     /// important_check();
     /// ```
-    #[clippy::version = "1.73.0"]
+    #[clippy::version = "1.81.0"]
     pub CFG_NOT_TEST,
     restriction,
     "enforce against excluding code from test builds"
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index 60e51713173..2b229d2fe6a 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -49,8 +49,6 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::attrs::DEPRECATED_CLIPPY_CFG_ATTR_INFO,
     crate::attrs::DEPRECATED_SEMVER_INFO,
     crate::attrs::DUPLICATED_ATTRIBUTES_INFO,
-    crate::attrs::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO,
-    crate::attrs::EMPTY_LINE_AFTER_OUTER_ATTR_INFO,
     crate::attrs::INLINE_ALWAYS_INFO,
     crate::attrs::MIXED_ATTRIBUTES_STYLE_INFO,
     crate::attrs::NON_MINIMAL_CFG_INFO,
@@ -138,6 +136,8 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::doc::DOC_LINK_WITH_QUOTES_INFO,
     crate::doc::DOC_MARKDOWN_INFO,
     crate::doc::EMPTY_DOCS_INFO,
+    crate::doc::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO,
+    crate::doc::EMPTY_LINE_AFTER_OUTER_ATTR_INFO,
     crate::doc::MISSING_ERRORS_DOC_INFO,
     crate::doc::MISSING_PANICS_DOC_INFO,
     crate::doc::MISSING_SAFETY_DOC_INFO,
@@ -217,6 +217,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::implicit_return::IMPLICIT_RETURN_INFO,
     crate::implicit_saturating_add::IMPLICIT_SATURATING_ADD_INFO,
     crate::implicit_saturating_sub::IMPLICIT_SATURATING_SUB_INFO,
+    crate::implicit_saturating_sub::INVERTED_SATURATING_SUB_INFO,
     crate::implied_bounds_in_impls::IMPLIED_BOUNDS_IN_IMPLS_INFO,
     crate::incompatible_msrv::INCOMPATIBLE_MSRV_INFO,
     crate::inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR_INFO,
@@ -300,10 +301,12 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::manual_async_fn::MANUAL_ASYNC_FN_INFO,
     crate::manual_bits::MANUAL_BITS_INFO,
     crate::manual_clamp::MANUAL_CLAMP_INFO,
+    crate::manual_div_ceil::MANUAL_DIV_CEIL_INFO,
     crate::manual_float_methods::MANUAL_IS_FINITE_INFO,
     crate::manual_float_methods::MANUAL_IS_INFINITE_INFO,
     crate::manual_hash_one::MANUAL_HASH_ONE_INFO,
     crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO,
+    crate::manual_is_power_of_two::MANUAL_IS_POWER_OF_TWO_INFO,
     crate::manual_let_else::MANUAL_LET_ELSE_INFO,
     crate::manual_main_separator_str::MANUAL_MAIN_SEPARATOR_STR_INFO,
     crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO,
@@ -464,6 +467,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::methods::UNNECESSARY_FALLIBLE_CONVERSIONS_INFO,
     crate::methods::UNNECESSARY_FILTER_MAP_INFO,
     crate::methods::UNNECESSARY_FIND_MAP_INFO,
+    crate::methods::UNNECESSARY_FIRST_THEN_CHECK_INFO,
     crate::methods::UNNECESSARY_FOLD_INFO,
     crate::methods::UNNECESSARY_GET_THEN_CHECK_INFO,
     crate::methods::UNNECESSARY_JOIN_INFO,
@@ -486,6 +490,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::misc::SHORT_CIRCUIT_STATEMENT_INFO,
     crate::misc::TOPLEVEL_REF_ARG_INFO,
     crate::misc::USED_UNDERSCORE_BINDING_INFO,
+    crate::misc::USED_UNDERSCORE_ITEMS_INFO,
     crate::misc_early::BUILTIN_TYPE_SHADOW_INFO,
     crate::misc_early::DOUBLE_NEG_INFO,
     crate::misc_early::DUPLICATE_UNDERSCORE_ARGUMENT_INFO,
@@ -553,6 +558,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::non_expressive_names::SIMILAR_NAMES_INFO,
     crate::non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS_INFO,
     crate::non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY_INFO,
+    crate::non_zero_suggestions::NON_ZERO_SUGGESTIONS_INFO,
     crate::nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES_INFO,
     crate::octal_escapes::OCTAL_ESCAPES_INFO,
     crate::only_used_in_recursion::ONLY_USED_IN_RECURSION_INFO,
@@ -598,6 +604,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::pathbuf_init_then_push::PATHBUF_INIT_THEN_PUSH_INFO,
     crate::pattern_type_mismatch::PATTERN_TYPE_MISMATCH_INFO,
     crate::permissions_set_readonly_false::PERMISSIONS_SET_READONLY_FALSE_INFO,
+    crate::pointers_in_nomem_asm_block::POINTERS_IN_NOMEM_ASM_BLOCK_INFO,
     crate::precedence::PRECEDENCE_INFO,
     crate::ptr::CMP_NULL_INFO,
     crate::ptr::INVALID_NULL_PTR_USAGE_INFO,
@@ -741,6 +748,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::unused_result_ok::UNUSED_RESULT_OK_INFO,
     crate::unused_rounding::UNUSED_ROUNDING_INFO,
     crate::unused_self::UNUSED_SELF_INFO,
+    crate::unused_trait_names::UNUSED_TRAIT_NAMES_INFO,
     crate::unused_unit::UNUSED_UNIT_INFO,
     crate::unwrap::PANICKING_UNWRAP_INFO,
     crate::unwrap::UNNECESSARY_UNWRAP_INFO,
@@ -767,4 +775,5 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::zero_div_zero::ZERO_DIVIDED_BY_ZERO_INFO,
     crate::zero_repeat_side_effects::ZERO_REPEAT_SIDE_EFFECTS_INFO,
     crate::zero_sized_map_values::ZERO_SIZED_MAP_VALUES_INFO,
+    crate::zombie_processes::ZOMBIE_PROCESSES_INFO,
 ];
diff --git a/clippy_lints/src/doc/empty_line_after.rs b/clippy_lints/src/doc/empty_line_after.rs
new file mode 100644
index 00000000000..289debe0a6a
--- /dev/null
+++ b/clippy_lints/src/doc/empty_line_after.rs
@@ -0,0 +1,329 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::{snippet_indent, SpanRangeExt};
+use clippy_utils::tokenize_with_text;
+use itertools::Itertools;
+use rustc_ast::token::CommentKind;
+use rustc_ast::{AttrKind, AttrStyle, Attribute};
+use rustc_errors::{Applicability, Diag, SuggestionStyle};
+use rustc_hir::{ItemKind, Node};
+use rustc_lexer::TokenKind;
+use rustc_lint::LateContext;
+use rustc_span::{ExpnKind, InnerSpan, Span, SpanData};
+
+use super::{EMPTY_LINE_AFTER_DOC_COMMENTS, EMPTY_LINE_AFTER_OUTER_ATTR};
+
+#[derive(Debug, PartialEq, Clone, Copy)]
+enum StopKind {
+    Attr,
+    Doc(CommentKind),
+}
+
+impl StopKind {
+    fn is_doc(self) -> bool {
+        matches!(self, StopKind::Doc(_))
+    }
+}
+
+#[derive(Debug)]
+struct Stop {
+    span: Span,
+    kind: StopKind,
+    first: usize,
+    last: usize,
+}
+
+impl Stop {
+    fn convert_to_inner(&self) -> (Span, String) {
+        let inner = match self.kind {
+            // #|[...]
+            StopKind::Attr => InnerSpan::new(1, 1),
+            // /// or /**
+            //   ^      ^
+            StopKind::Doc(_) => InnerSpan::new(2, 3),
+        };
+        (self.span.from_inner(inner), "!".into())
+    }
+
+    fn comment_out(&self, cx: &LateContext<'_>, suggestions: &mut Vec<(Span, String)>) {
+        match self.kind {
+            StopKind::Attr => {
+                if cx.tcx.sess.source_map().is_multiline(self.span) {
+                    suggestions.extend([
+                        (self.span.shrink_to_lo(), "/* ".into()),
+                        (self.span.shrink_to_hi(), " */".into()),
+                    ]);
+                } else {
+                    suggestions.push((self.span.shrink_to_lo(), "// ".into()));
+                }
+            },
+            StopKind::Doc(CommentKind::Line) => suggestions.push((self.span.shrink_to_lo(), "// ".into())),
+            StopKind::Doc(CommentKind::Block) => {
+                // /** outer */  /*! inner */
+                //  ^             ^
+                let asterisk = self.span.from_inner(InnerSpan::new(1, 2));
+                suggestions.push((asterisk, String::new()));
+            },
+        }
+    }
+
+    fn from_attr(cx: &LateContext<'_>, attr: &Attribute) -> Option<Self> {
+        let SpanData { lo, hi, .. } = attr.span.data();
+        let file = cx.tcx.sess.source_map().lookup_source_file(lo);
+
+        Some(Self {
+            span: attr.span,
+            kind: match attr.kind {
+                AttrKind::Normal(_) => StopKind::Attr,
+                AttrKind::DocComment(comment_kind, _) => StopKind::Doc(comment_kind),
+            },
+            first: file.lookup_line(file.relative_position(lo))?,
+            last: file.lookup_line(file.relative_position(hi))?,
+        })
+    }
+}
+
+/// Represents a set of attrs/doc comments separated by 1 or more empty lines
+///
+/// ```ignore
+/// /// chunk 1 docs
+/// // not an empty line so also part of chunk 1
+/// #[chunk_1_attrs] // <-- prev_stop
+///
+/// /* gap */
+///
+/// /// chunk 2 docs // <-- next_stop
+/// #[chunk_2_attrs]
+/// ```
+struct Gap<'a> {
+    /// The span of individual empty lines including the newline at the end of the line
+    empty_lines: Vec<Span>,
+    has_comment: bool,
+    next_stop: &'a Stop,
+    prev_stop: &'a Stop,
+    /// The chunk that includes [`prev_stop`](Self::prev_stop)
+    prev_chunk: &'a [Stop],
+}
+
+impl<'a> Gap<'a> {
+    fn new(cx: &LateContext<'_>, prev_chunk: &'a [Stop], next_chunk: &'a [Stop]) -> Option<Self> {
+        let prev_stop = prev_chunk.last()?;
+        let next_stop = next_chunk.first()?;
+        let gap_span = prev_stop.span.between(next_stop.span);
+        let gap_snippet = gap_span.get_source_text(cx)?;
+
+        let mut has_comment = false;
+        let mut empty_lines = Vec::new();
+
+        for (token, source, inner_span) in tokenize_with_text(&gap_snippet) {
+            match token {
+                TokenKind::BlockComment {
+                    doc_style: None,
+                    terminated: true,
+                }
+                | TokenKind::LineComment { doc_style: None } => has_comment = true,
+                TokenKind::Whitespace => {
+                    let newlines = source.bytes().positions(|b| b == b'\n');
+                    empty_lines.extend(
+                        newlines
+                            .tuple_windows()
+                            .map(|(a, b)| InnerSpan::new(inner_span.start + a + 1, inner_span.start + b))
+                            .map(|inner_span| gap_span.from_inner(inner_span)),
+                    );
+                },
+                // Ignore cfg_attr'd out attributes as they may contain empty lines, could also be from macro
+                // shenanigans
+                _ => return None,
+            }
+        }
+
+        (!empty_lines.is_empty()).then_some(Self {
+            empty_lines,
+            has_comment,
+            next_stop,
+            prev_stop,
+            prev_chunk,
+        })
+    }
+}
+
+/// If the node the attributes/docs apply to is the first in the module/crate suggest converting
+/// them to inner attributes/docs
+fn suggest_inner(cx: &LateContext<'_>, diag: &mut Diag<'_, ()>, kind: StopKind, gaps: &[Gap<'_>]) {
+    let Some(owner) = cx.last_node_with_lint_attrs.as_owner() else {
+        return;
+    };
+    let parent_desc = match cx.tcx.parent_hir_node(owner.into()) {
+        Node::Item(item)
+            if let ItemKind::Mod(parent_mod) = item.kind
+                && let [first, ..] = parent_mod.item_ids
+                && first.owner_id == owner =>
+        {
+            "parent module"
+        },
+        Node::Crate(crate_mod)
+            if let Some(first) = crate_mod
+                .item_ids
+                .iter()
+                .map(|&id| cx.tcx.hir().item(id))
+                // skip prelude imports
+                .find(|item| !matches!(item.span.ctxt().outer_expn_data().kind, ExpnKind::AstPass(_)))
+                && first.owner_id == owner =>
+        {
+            "crate"
+        },
+        _ => return,
+    };
+
+    diag.multipart_suggestion_verbose(
+        match kind {
+            StopKind::Attr => format!("if the attribute should apply to the {parent_desc} use an inner attribute"),
+            StopKind::Doc(_) => format!("if the comment should document the {parent_desc} use an inner doc comment"),
+        },
+        gaps.iter()
+            .flat_map(|gap| gap.prev_chunk)
+            .map(Stop::convert_to_inner)
+            .collect(),
+        Applicability::MaybeIncorrect,
+    );
+}
+
+fn check_gaps(cx: &LateContext<'_>, gaps: &[Gap<'_>]) -> bool {
+    let Some(first_gap) = gaps.first() else {
+        return false;
+    };
+    let empty_lines = || gaps.iter().flat_map(|gap| gap.empty_lines.iter().copied());
+    let mut has_comment = false;
+    let mut has_attr = false;
+    for gap in gaps {
+        has_comment |= gap.has_comment;
+        if !has_attr {
+            has_attr = gap.prev_chunk.iter().any(|stop| stop.kind == StopKind::Attr);
+        }
+    }
+    let kind = first_gap.prev_stop.kind;
+    let (lint, kind_desc) = match kind {
+        StopKind::Attr => (EMPTY_LINE_AFTER_OUTER_ATTR, "outer attribute"),
+        StopKind::Doc(_) => (EMPTY_LINE_AFTER_DOC_COMMENTS, "doc comment"),
+    };
+    let (lines, are, them) = if empty_lines().nth(1).is_some() {
+        ("lines", "are", "them")
+    } else {
+        ("line", "is", "it")
+    };
+    span_lint_and_then(
+        cx,
+        lint,
+        first_gap.prev_stop.span.to(empty_lines().last().unwrap()),
+        format!("empty {lines} after {kind_desc}"),
+        |diag| {
+            if let Some(owner) = cx.last_node_with_lint_attrs.as_owner() {
+                let def_id = owner.to_def_id();
+                let def_descr = cx.tcx.def_descr(def_id);
+                diag.span_label(
+                    cx.tcx.def_span(def_id),
+                    match kind {
+                        StopKind::Attr => format!("the attribute applies to this {def_descr}"),
+                        StopKind::Doc(_) => format!("the comment documents this {def_descr}"),
+                    },
+                );
+            }
+
+            diag.multipart_suggestion_with_style(
+                format!("if the empty {lines} {are} unintentional remove {them}"),
+                empty_lines().map(|empty_line| (empty_line, String::new())).collect(),
+                Applicability::MaybeIncorrect,
+                SuggestionStyle::HideCodeAlways,
+            );
+
+            if has_comment && kind.is_doc() {
+                // Likely doc comments that applied to some now commented out code
+                //
+                // /// Old docs for Foo
+                // // struct Foo;
+
+                let mut suggestions = Vec::new();
+                for stop in gaps.iter().flat_map(|gap| gap.prev_chunk) {
+                    stop.comment_out(cx, &mut suggestions);
+                }
+                let name = match cx.tcx.hir().opt_name(cx.last_node_with_lint_attrs) {
+                    Some(name) => format!("`{name}`"),
+                    None => "this".into(),
+                };
+                diag.multipart_suggestion_verbose(
+                    format!("if the doc comment should not document {name} comment it out"),
+                    suggestions,
+                    Applicability::MaybeIncorrect,
+                );
+            } else {
+                suggest_inner(cx, diag, kind, gaps);
+            }
+
+            if kind == StopKind::Doc(CommentKind::Line)
+                && gaps
+                    .iter()
+                    .all(|gap| !gap.has_comment && gap.next_stop.kind == StopKind::Doc(CommentKind::Line))
+            {
+                // Commentless empty gaps between line doc comments, possibly intended to be part of the markdown
+
+                let indent = snippet_indent(cx, first_gap.prev_stop.span).unwrap_or_default();
+                diag.multipart_suggestion_verbose(
+                    format!("if the documentation should include the empty {lines} include {them} in the comment"),
+                    empty_lines()
+                        .map(|empty_line| (empty_line, format!("{indent}///")))
+                        .collect(),
+                    Applicability::MaybeIncorrect,
+                );
+            }
+        },
+    );
+    kind.is_doc()
+}
+
+/// Returns `true` if [`EMPTY_LINE_AFTER_DOC_COMMENTS`] triggered, used to skip other doc comment
+/// lints where they would be confusing
+///
+/// [`EMPTY_LINE_AFTER_OUTER_ATTR`] is also here to share an implementation but does not return
+/// `true` if it triggers
+pub(super) fn check(cx: &LateContext<'_>, attrs: &[Attribute]) -> bool {
+    let mut outer = attrs
+        .iter()
+        .filter(|attr| attr.style == AttrStyle::Outer && !attr.span.from_expansion())
+        .map(|attr| Stop::from_attr(cx, attr))
+        .collect::<Option<Vec<_>>>()
+        .unwrap_or_default();
+
+    if outer.is_empty() {
+        return false;
+    }
+
+    // Push a fake attribute Stop for the item itself so we check for gaps between the last outer
+    // attr/doc comment and the item they apply to
+    let span = cx.tcx.hir().span(cx.last_node_with_lint_attrs);
+    if !span.from_expansion()
+        && let Ok(line) = cx.tcx.sess.source_map().lookup_line(span.lo())
+    {
+        outer.push(Stop {
+            span,
+            kind: StopKind::Attr,
+            first: line.line,
+            // last doesn't need to be accurate here, we don't compare it with anything
+            last: line.line,
+        });
+    }
+
+    let mut gaps = Vec::new();
+    let mut last = 0;
+    for pos in outer
+        .array_windows()
+        .positions(|[a, b]| b.first.saturating_sub(a.last) > 1)
+    {
+        // we want to be after the first stop in the window
+        let pos = pos + 1;
+        if let Some(gap) = Gap::new(cx, &outer[last..pos], &outer[pos..]) {
+            last = pos;
+            gaps.push(gap);
+        }
+    }
+
+    check_gaps(cx, &gaps)
+}
diff --git a/clippy_lints/src/doc/lazy_continuation.rs b/clippy_lints/src/doc/lazy_continuation.rs
index 771bcac2441..f9e4a43c0e7 100644
--- a/clippy_lints/src/doc/lazy_continuation.rs
+++ b/clippy_lints/src/doc/lazy_continuation.rs
@@ -22,7 +22,6 @@ pub(super) fn check(
     range: Range<usize>,
     mut span: Span,
     containers: &[super::Container],
-    line_break_span: Span,
 ) {
     if doc[range.clone()].contains('\t') {
         // We don't do tab stops correctly.
@@ -52,29 +51,6 @@ pub(super) fn check(
             "doc list item without indentation"
         };
         span_lint_and_then(cx, DOC_LAZY_CONTINUATION, span, msg, |diag| {
-            let snippet = clippy_utils::source::snippet(cx, line_break_span, "");
-            if snippet.chars().filter(|&c| c == '\n').count() > 1
-                && let Some(doc_comment_start) = snippet.rfind('\n')
-                && let doc_comment = snippet[doc_comment_start..].trim()
-                && (doc_comment == "///" || doc_comment == "//!")
-            {
-                // suggest filling in a blank line
-                diag.span_suggestion_verbose(
-                    line_break_span.shrink_to_lo(),
-                    "if this should be its own paragraph, add a blank doc comment line",
-                    format!("\n{doc_comment}"),
-                    Applicability::MaybeIncorrect,
-                );
-                if ccount > 0 || blockquote_level > 0 {
-                    diag.help("if this not intended to be a quote at all, escape it with `\\>`");
-                } else {
-                    let indent = list_indentation - lcount;
-                    diag.help(format!(
-                        "if this is intended to be part of the list, indent {indent} spaces"
-                    ));
-                }
-                return;
-            }
             if ccount == 0 && blockquote_level == 0 {
                 // simpler suggestion style for indentation
                 let indent = list_indentation - lcount;
diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs
index 790579b21c9..f2c87f0fd70 100644
--- a/clippy_lints/src/doc/mod.rs
+++ b/clippy_lints/src/doc/mod.rs
@@ -32,6 +32,7 @@ use rustc_span::{sym, Span};
 use std::ops::Range;
 use url::Url;
 
+mod empty_line_after;
 mod link_with_quotes;
 mod markdown;
 mod missing_headers;
@@ -449,13 +450,88 @@ declare_clippy_lint! {
     /// /// and probably spanning a many rows.
     /// struct Foo {}
     /// ```
-    #[clippy::version = "1.81.0"]
+    #[clippy::version = "1.82.0"]
     pub TOO_LONG_FIRST_DOC_PARAGRAPH,
     style,
     "ensure that the first line of a documentation paragraph isn't too long"
 }
 
-#[derive(Clone)]
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for empty lines after outer attributes
+    ///
+    /// ### Why is this bad?
+    /// The attribute may have meant to be an inner attribute (`#![attr]`). If
+    /// it was meant to be an outer attribute (`#[attr]`) then the empty line
+    /// should be removed
+    ///
+    /// ### Example
+    /// ```no_run
+    /// #[allow(dead_code)]
+    ///
+    /// fn not_quite_good_code() {}
+    /// ```
+    ///
+    /// Use instead:
+    /// ```no_run
+    /// // Good (as inner attribute)
+    /// #![allow(dead_code)]
+    ///
+    /// fn this_is_fine() {}
+    ///
+    /// // or
+    ///
+    /// // Good (as outer attribute)
+    /// #[allow(dead_code)]
+    /// fn this_is_fine_too() {}
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub EMPTY_LINE_AFTER_OUTER_ATTR,
+    suspicious,
+    "empty line after outer attribute"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for empty lines after doc comments.
+    ///
+    /// ### Why is this bad?
+    /// The doc comment may have meant to be an inner doc comment, regular
+    /// comment or applied to some old code that is now commented out. If it was
+    /// intended to be a doc comment, then the empty line should be removed.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// /// Some doc comment with a blank line after it.
+    ///
+    /// fn f() {}
+    ///
+    /// /// Docs for `old_code`
+    /// // fn old_code() {}
+    ///
+    /// fn new_code() {}
+    /// ```
+    ///
+    /// Use instead:
+    /// ```no_run
+    /// //! Convert it to an inner doc comment
+    ///
+    /// // Or a regular comment
+    ///
+    /// /// Or remove the empty line
+    /// fn f() {}
+    ///
+    /// // /// Docs for `old_code`
+    /// // fn old_code() {}
+    ///
+    /// fn new_code() {}
+    /// ```
+    #[clippy::version = "1.70.0"]
+    pub EMPTY_LINE_AFTER_DOC_COMMENTS,
+    suspicious,
+    "empty line after doc comments"
+}
+
 pub struct Documentation {
     valid_idents: FxHashSet<String>,
     check_private_items: bool,
@@ -482,6 +558,8 @@ impl_lint_pass!(Documentation => [
     SUSPICIOUS_DOC_COMMENTS,
     EMPTY_DOCS,
     DOC_LAZY_CONTINUATION,
+    EMPTY_LINE_AFTER_OUTER_ATTR,
+    EMPTY_LINE_AFTER_DOC_COMMENTS,
     TOO_LONG_FIRST_DOC_PARAGRAPH,
 ]);
 
@@ -612,12 +690,10 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
         Some(("fake".into(), "fake".into()))
     }
 
-    if is_doc_hidden(attrs) {
+    if suspicious_doc_comments::check(cx, attrs) || empty_line_after::check(cx, attrs) || is_doc_hidden(attrs) {
         return None;
     }
 
-    suspicious_doc_comments::check(cx, attrs);
-
     let (fragments, _) = attrs_to_doc_fragments(
         attrs.iter().filter_map(|attr| {
             if in_external_macro(cx.sess(), attr.span) {
@@ -816,7 +892,6 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
                         range.end..next_range.start,
                         Span::new(span.hi(), next_span.lo(), span.ctxt(), span.parent()),
                         &containers[..],
-                        span,
                     );
                 }
             },
diff --git a/clippy_lints/src/doc/suspicious_doc_comments.rs b/clippy_lints/src/doc/suspicious_doc_comments.rs
index d7ad30efec3..f6f942b10ca 100644
--- a/clippy_lints/src/doc/suspicious_doc_comments.rs
+++ b/clippy_lints/src/doc/suspicious_doc_comments.rs
@@ -7,7 +7,7 @@ use rustc_span::Span;
 
 use super::SUSPICIOUS_DOC_COMMENTS;
 
-pub fn check(cx: &LateContext<'_>, attrs: &[Attribute]) {
+pub fn check(cx: &LateContext<'_>, attrs: &[Attribute]) -> bool {
     let replacements: Vec<_> = collect_doc_replacements(attrs);
 
     if let Some((&(lo_span, _), &(hi_span, _))) = replacements.first().zip(replacements.last()) {
@@ -24,6 +24,10 @@ pub fn check(cx: &LateContext<'_>, attrs: &[Attribute]) {
                 );
             },
         );
+
+        true
+    } else {
+        false
     }
 }
 
diff --git a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs
index 7bb3bb12f2c..0165d24c7df 100644
--- a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs
+++ b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs
@@ -79,10 +79,17 @@ pub(super) fn check(
                 && let new_span = first_span.with_hi(second_span.lo()).with_lo(first_span.hi())
                 && let Some(snippet) = snippet_opt(cx, new_span)
             {
+                let Some(first) = snippet_opt(cx, first_span) else {
+                    return;
+                };
+                let Some(comment_form) = first.get(..3) else {
+                    return;
+                };
+
                 diag.span_suggestion(
                     new_span,
                     "add an empty line",
-                    format!("{snippet}///\n"),
+                    format!("{snippet}{comment_form}{snippet}"),
                     Applicability::MachineApplicable,
                 );
             }
diff --git a/clippy_lints/src/field_scoped_visibility_modifiers.rs b/clippy_lints/src/field_scoped_visibility_modifiers.rs
index 95b8e882da7..ba2b37fbf11 100644
--- a/clippy_lints/src/field_scoped_visibility_modifiers.rs
+++ b/clippy_lints/src/field_scoped_visibility_modifiers.rs
@@ -41,7 +41,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
-    #[clippy::version = "1.78.0"]
+    #[clippy::version = "1.81.0"]
     pub FIELD_SCOPED_VISIBILITY_MODIFIERS,
     restriction,
     "checks for usage of a scoped visibility modifier, like `pub(crate)`, on fields"
diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs
index b84bf7c821c..5e9098474dc 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 = self_ty.span.get_source_text(cx)?;
     let into = target_ty.span.get_source_text(cx)?;
 
+    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/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs
index 0bca53c1536..55f9625709b 100644
--- a/clippy_lints/src/if_then_some_else_none.rs
+++ b/clippy_lints/src/if_then_some_else_none.rs
@@ -105,6 +105,8 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
                             snippet_with_context(cx, first_stmt.span.until(then_arg.span), ctxt, "..", &mut app);
                         let closure = if method_name == "then" { "|| " } else { "" };
                         format!("{closure} {{ {block_snippet}; {arg_snip} }}")
+                    } else if method_name == "then" {
+                        (std::borrow::Cow::Borrowed("|| ") + arg_snip).into_owned()
                     } else {
                         arg_snip.into_owned()
                     };
diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs
index 127c73ed637..3536309d83c 100644
--- a/clippy_lints/src/implicit_saturating_sub.rs
+++ b/clippy_lints/src/implicit_saturating_sub.rs
@@ -1,11 +1,17 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::{higher, is_integer_literal, peel_blocks_with_stmt, SpanlessEq};
+use clippy_config::msrvs::{self, Msrv};
+use clippy_config::Conf;
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::snippet_opt;
+use clippy_utils::{
+    higher, is_in_const_context, is_integer_literal, path_to_local, peel_blocks, peel_blocks_with_stmt, SpanlessEq,
+};
 use rustc_ast::ast::LitKind;
 use rustc_data_structures::packed::Pu128;
 use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Expr, ExprKind, QPath};
+use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, HirId, QPath};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::declare_lint_pass;
+use rustc_session::impl_lint_pass;
+use rustc_span::Span;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -39,7 +45,49 @@ declare_clippy_lint! {
     "Perform saturating subtraction instead of implicitly checking lower bound of data type"
 }
 
-declare_lint_pass!(ImplicitSaturatingSub => [IMPLICIT_SATURATING_SUB]);
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for comparisons between integers, followed by subtracting the greater value from the
+    /// lower one.
+    ///
+    /// ### Why is this bad?
+    /// This could result in an underflow and is most likely not what the user wants. If this was
+    /// intended to be a saturated subtraction, consider using the `saturating_sub` method directly.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// let a = 12u32;
+    /// let b = 13u32;
+    ///
+    /// let result = if a > b { b - a } else { 0 };
+    /// ```
+    ///
+    /// Use instead:
+    /// ```no_run
+    /// let a = 12u32;
+    /// let b = 13u32;
+    ///
+    /// let result = a.saturating_sub(b);
+    /// ```
+    #[clippy::version = "1.44.0"]
+    pub INVERTED_SATURATING_SUB,
+    correctness,
+    "Check if a variable is smaller than another one and still subtract from it even if smaller"
+}
+
+pub struct ImplicitSaturatingSub {
+    msrv: Msrv,
+}
+
+impl_lint_pass!(ImplicitSaturatingSub => [IMPLICIT_SATURATING_SUB, INVERTED_SATURATING_SUB]);
+
+impl ImplicitSaturatingSub {
+    pub fn new(conf: &'static Conf) -> Self {
+        Self {
+            msrv: conf.msrv.clone(),
+        }
+    }
+}
 
 impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
@@ -50,73 +98,260 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
 
             // Check if the conditional expression is a binary operation
             && let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind
+        {
+            check_with_condition(cx, expr, cond_op.node, cond_left, cond_right, then);
+        } else if let Some(higher::If {
+            cond,
+            then: if_block,
+            r#else: Some(else_block),
+        }) = higher::If::hir(expr)
+            && let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind
+        {
+            check_manual_check(
+                cx, expr, cond_op, cond_left, cond_right, if_block, else_block, &self.msrv,
+            );
+        }
+    }
+
+    extract_msrv_attr!(LateContext);
+}
 
-            // Ensure that the binary operator is >, !=, or <
-            && (BinOpKind::Ne == cond_op.node || BinOpKind::Gt == cond_op.node || BinOpKind::Lt == cond_op.node)
+#[allow(clippy::too_many_arguments)]
+fn check_manual_check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &Expr<'tcx>,
+    condition: &BinOp,
+    left_hand: &Expr<'tcx>,
+    right_hand: &Expr<'tcx>,
+    if_block: &Expr<'tcx>,
+    else_block: &Expr<'tcx>,
+    msrv: &Msrv,
+) {
+    let ty = cx.typeck_results().expr_ty(left_hand);
+    if ty.is_numeric() && !ty.is_signed() {
+        match condition.node {
+            BinOpKind::Gt | BinOpKind::Ge => check_gt(
+                cx,
+                condition.span,
+                expr.span,
+                left_hand,
+                right_hand,
+                if_block,
+                else_block,
+                msrv,
+            ),
+            BinOpKind::Lt | BinOpKind::Le => check_gt(
+                cx,
+                condition.span,
+                expr.span,
+                right_hand,
+                left_hand,
+                if_block,
+                else_block,
+                msrv,
+            ),
+            _ => {},
+        }
+    }
+}
 
-            // Check if assign operation is done
-            && let Some(target) = subtracts_one(cx, then)
+#[allow(clippy::too_many_arguments)]
+fn check_gt(
+    cx: &LateContext<'_>,
+    condition_span: Span,
+    expr_span: Span,
+    big_var: &Expr<'_>,
+    little_var: &Expr<'_>,
+    if_block: &Expr<'_>,
+    else_block: &Expr<'_>,
+    msrv: &Msrv,
+) {
+    if let Some(big_var) = Var::new(big_var)
+        && let Some(little_var) = Var::new(little_var)
+    {
+        check_subtraction(
+            cx,
+            condition_span,
+            expr_span,
+            big_var,
+            little_var,
+            if_block,
+            else_block,
+            msrv,
+        );
+    }
+}
 
-            // Extracting out the variable name
-            && let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind
+struct Var {
+    span: Span,
+    hir_id: HirId,
+}
+
+impl Var {
+    fn new(expr: &Expr<'_>) -> Option<Self> {
+        path_to_local(expr).map(|hir_id| Self {
+            span: expr.span,
+            hir_id,
+        })
+    }
+}
+
+#[allow(clippy::too_many_arguments)]
+fn check_subtraction(
+    cx: &LateContext<'_>,
+    condition_span: Span,
+    expr_span: Span,
+    big_var: Var,
+    little_var: Var,
+    if_block: &Expr<'_>,
+    else_block: &Expr<'_>,
+    msrv: &Msrv,
+) {
+    let if_block = peel_blocks(if_block);
+    let else_block = peel_blocks(else_block);
+    if is_integer_literal(if_block, 0) {
+        // We need to check this case as well to prevent infinite recursion.
+        if is_integer_literal(else_block, 0) {
+            // Well, seems weird but who knows?
+            return;
+        }
+        // If the subtraction is done in the `else` block, then we need to also revert the two
+        // variables as it means that the check was reverted too.
+        check_subtraction(
+            cx,
+            condition_span,
+            expr_span,
+            little_var,
+            big_var,
+            else_block,
+            if_block,
+            msrv,
+        );
+        return;
+    }
+    if is_integer_literal(else_block, 0)
+        && let ExprKind::Binary(op, left, right) = if_block.kind
+        && let BinOpKind::Sub = op.node
+    {
+        let local_left = path_to_local(left);
+        let local_right = path_to_local(right);
+        if Some(big_var.hir_id) == local_left && Some(little_var.hir_id) == local_right {
+            // This part of the condition is voluntarily split from the one before to ensure that
+            // if `snippet_opt` fails, it won't try the next conditions.
+            if let Some(big_var_snippet) = snippet_opt(cx, big_var.span)
+                && let Some(little_var_snippet) = snippet_opt(cx, little_var.span)
+                && (!is_in_const_context(cx) || msrv.meets(msrvs::SATURATING_SUB_CONST))
+            {
+                span_lint_and_sugg(
+                    cx,
+                    IMPLICIT_SATURATING_SUB,
+                    expr_span,
+                    "manual arithmetic check found",
+                    "replace it with",
+                    format!("{big_var_snippet}.saturating_sub({little_var_snippet})"),
+                    Applicability::MachineApplicable,
+                );
+            }
+        } else if Some(little_var.hir_id) == local_left
+            && Some(big_var.hir_id) == local_right
+            && let Some(big_var_snippet) = snippet_opt(cx, big_var.span)
+            && let Some(little_var_snippet) = snippet_opt(cx, little_var.span)
         {
-            // Handle symmetric conditions in the if statement
-            let (cond_var, cond_num_val) = if SpanlessEq::new(cx).eq_expr(cond_left, target) {
-                if BinOpKind::Gt == cond_op.node || BinOpKind::Ne == cond_op.node {
-                    (cond_left, cond_right)
-                } else {
-                    return;
-                }
-            } else if SpanlessEq::new(cx).eq_expr(cond_right, target) {
-                if BinOpKind::Lt == cond_op.node || BinOpKind::Ne == cond_op.node {
-                    (cond_right, cond_left)
-                } else {
-                    return;
-                }
+            span_lint_and_then(
+                cx,
+                INVERTED_SATURATING_SUB,
+                condition_span,
+                "inverted arithmetic check before subtraction",
+                |diag| {
+                    diag.span_note(
+                        if_block.span,
+                        format!("this subtraction underflows when `{little_var_snippet} < {big_var_snippet}`"),
+                    );
+                    diag.span_suggestion(
+                        if_block.span,
+                        "try replacing it with",
+                        format!("{big_var_snippet} - {little_var_snippet}"),
+                        Applicability::MaybeIncorrect,
+                    );
+                },
+            );
+        }
+    }
+}
+
+fn check_with_condition<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &Expr<'tcx>,
+    cond_op: BinOpKind,
+    cond_left: &Expr<'tcx>,
+    cond_right: &Expr<'tcx>,
+    then: &Expr<'tcx>,
+) {
+    // Ensure that the binary operator is >, !=, or <
+    if (BinOpKind::Ne == cond_op || BinOpKind::Gt == cond_op || BinOpKind::Lt == cond_op)
+
+        // Check if assign operation is done
+        && let Some(target) = subtracts_one(cx, then)
+
+        // Extracting out the variable name
+        && let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind
+    {
+        // Handle symmetric conditions in the if statement
+        let (cond_var, cond_num_val) = if SpanlessEq::new(cx).eq_expr(cond_left, target) {
+            if BinOpKind::Gt == cond_op || BinOpKind::Ne == cond_op {
+                (cond_left, cond_right)
             } else {
                 return;
-            };
-
-            // Check if the variable in the condition statement is an integer
-            if !cx.typeck_results().expr_ty(cond_var).is_integral() {
+            }
+        } else if SpanlessEq::new(cx).eq_expr(cond_right, target) {
+            if BinOpKind::Lt == cond_op || BinOpKind::Ne == cond_op {
+                (cond_right, cond_left)
+            } else {
                 return;
             }
+        } else {
+            return;
+        };
 
-            // Get the variable name
-            let var_name = ares_path.segments[0].ident.name.as_str();
-            match cond_num_val.kind {
-                ExprKind::Lit(cond_lit) => {
-                    // Check if the constant is zero
-                    if let LitKind::Int(Pu128(0), _) = cond_lit.node {
-                        if cx.typeck_results().expr_ty(cond_left).is_signed() {
-                        } else {
-                            print_lint_and_sugg(cx, var_name, expr);
-                        };
-                    }
-                },
-                ExprKind::Path(QPath::TypeRelative(_, name)) => {
-                    if name.ident.as_str() == "MIN"
-                        && let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id)
-                        && let Some(impl_id) = cx.tcx.impl_of_method(const_id)
-                        && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl
-                        && cx.tcx.type_of(impl_id).instantiate_identity().is_integral()
-                    {
-                        print_lint_and_sugg(cx, var_name, expr);
-                    }
-                },
-                ExprKind::Call(func, []) => {
-                    if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind
-                        && name.ident.as_str() == "min_value"
-                        && let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id)
-                        && let Some(impl_id) = cx.tcx.impl_of_method(func_id)
-                        && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl
-                        && cx.tcx.type_of(impl_id).instantiate_identity().is_integral()
-                    {
+        // Check if the variable in the condition statement is an integer
+        if !cx.typeck_results().expr_ty(cond_var).is_integral() {
+            return;
+        }
+
+        // Get the variable name
+        let var_name = ares_path.segments[0].ident.name.as_str();
+        match cond_num_val.kind {
+            ExprKind::Lit(cond_lit) => {
+                // Check if the constant is zero
+                if let LitKind::Int(Pu128(0), _) = cond_lit.node {
+                    if cx.typeck_results().expr_ty(cond_left).is_signed() {
+                    } else {
                         print_lint_and_sugg(cx, var_name, expr);
-                    }
-                },
-                _ => (),
-            }
+                    };
+                }
+            },
+            ExprKind::Path(QPath::TypeRelative(_, name)) => {
+                if name.ident.as_str() == "MIN"
+                    && let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id)
+                    && let Some(impl_id) = cx.tcx.impl_of_method(const_id)
+                    && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl
+                    && cx.tcx.type_of(impl_id).instantiate_identity().is_integral()
+                {
+                    print_lint_and_sugg(cx, var_name, expr);
+                }
+            },
+            ExprKind::Call(func, []) => {
+                if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind
+                    && name.ident.as_str() == "min_value"
+                    && let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id)
+                    && let Some(impl_id) = cx.tcx.impl_of_method(func_id)
+                    && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl
+                    && cx.tcx.type_of(impl_id).instantiate_identity().is_integral()
+                {
+                    print_lint_and_sugg(cx, var_name, expr);
+                }
+            },
+            _ => (),
         }
     }
 }
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 2ac06b360be..b21d3f8d09e 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -203,9 +203,11 @@ mod manual_assert;
 mod manual_async_fn;
 mod manual_bits;
 mod manual_clamp;
+mod manual_div_ceil;
 mod manual_float_methods;
 mod manual_hash_one;
 mod manual_is_ascii_check;
+mod manual_is_power_of_two;
 mod manual_let_else;
 mod manual_main_separator_str;
 mod manual_non_exhaustive;
@@ -271,6 +273,7 @@ mod non_copy_const;
 mod non_expressive_names;
 mod non_octal_unix_permissions;
 mod non_send_fields_in_send_ty;
+mod non_zero_suggestions;
 mod nonstandard_macro_braces;
 mod octal_escapes;
 mod only_used_in_recursion;
@@ -287,6 +290,7 @@ mod pass_by_ref_or_value;
 mod pathbuf_init_then_push;
 mod pattern_type_mismatch;
 mod permissions_set_readonly_false;
+mod pointers_in_nomem_asm_block;
 mod precedence;
 mod ptr;
 mod ptr_offset_with_cast;
@@ -372,6 +376,7 @@ mod unused_peekable;
 mod unused_result_ok;
 mod unused_rounding;
 mod unused_self;
+mod unused_trait_names;
 mod unused_unit;
 mod unwrap;
 mod unwrap_in_result;
@@ -386,6 +391,7 @@ mod write;
 mod zero_div_zero;
 mod zero_repeat_side_effects;
 mod zero_sized_map_values;
+mod zombie_processes;
 // end lints modules, do not remove this comment, it’s used in `update_lints`
 
 use clippy_config::{get_configuration_metadata, Conf};
@@ -623,7 +629,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     store.register_late_pass(|_| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd));
     store.register_late_pass(|_| Box::new(strings::StringAdd));
     store.register_late_pass(|_| Box::new(implicit_return::ImplicitReturn));
-    store.register_late_pass(|_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub));
+    store.register_late_pass(move |_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub::new(conf)));
     store.register_late_pass(|_| Box::new(default_numeric_fallback::DefaultNumericFallback));
     store.register_late_pass(|_| Box::new(inconsistent_struct_constructor::InconsistentStructConstructor));
     store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
@@ -632,8 +638,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     let format_args = format_args_storage.clone();
     store.register_late_pass(move |_| Box::new(methods::Methods::new(conf, format_args.clone())));
     store.register_late_pass(move |_| Box::new(matches::Matches::new(conf)));
-    store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(conf)));
-    store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(conf)));
+    store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustive::new(conf)));
     store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(conf)));
     store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(conf)));
     store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(conf)));
@@ -933,5 +938,11 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     store.register_late_pass(|_| Box::new(set_contains_or_insert::SetContainsOrInsert));
     store.register_early_pass(|| Box::new(byte_char_slices::ByteCharSlice));
     store.register_early_pass(|| Box::new(cfg_not_test::CfgNotTest));
+    store.register_late_pass(|_| Box::new(zombie_processes::ZombieProcesses));
+    store.register_late_pass(|_| Box::new(pointers_in_nomem_asm_block::PointersInNomemAsmBlock));
+    store.register_late_pass(move |_| Box::new(manual_div_ceil::ManualDivCeil::new(conf)));
+    store.register_late_pass(|_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo));
+    store.register_late_pass(|_| Box::new(non_zero_suggestions::NonZeroSuggestions));
+    store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf)));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs
index 92ccc0cc0a1..5155fd76b25 100644
--- a/clippy_lints/src/loops/mod.rs
+++ b/clippy_lints/src/loops/mod.rs
@@ -188,22 +188,22 @@ declare_clippy_lint! {
     /// The `while let` loop is usually shorter and more
     /// readable.
     ///
-    /// ### Known problems
-    /// Sometimes the wrong binding is displayed ([#383](https://github.com/rust-lang/rust-clippy/issues/383)).
-    ///
     /// ### Example
     /// ```rust,no_run
-    /// # let y = Some(1);
+    /// let y = Some(1);
     /// loop {
     ///     let x = match y {
     ///         Some(x) => x,
     ///         None => break,
     ///     };
-    ///     // .. do something with x
+    ///     // ..
     /// }
-    /// // is easier written as
+    /// ```
+    /// Use instead:
+    /// ```rust,no_run
+    /// let y = Some(1);
     /// while let Some(x) = y {
-    ///     // .. do something with x
+    ///     // ..
     /// };
     /// ```
     #[clippy::version = "pre 1.29.0"]
diff --git a/clippy_lints/src/manual_div_ceil.rs b/clippy_lints/src/manual_div_ceil.rs
new file mode 100644
index 00000000000..024c2547dc6
--- /dev/null
+++ b/clippy_lints/src/manual_div_ceil.rs
@@ -0,0 +1,158 @@
+use clippy_config::msrvs::{self, Msrv};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::SpanlessEq;
+use rustc_ast::{BinOpKind, LitKind};
+use rustc_data_structures::packed::Pu128;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::{self};
+use rustc_session::impl_lint_pass;
+use rustc_span::symbol::Symbol;
+
+use clippy_config::Conf;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for an expression like `(x + (y - 1)) / y` which is a common manual reimplementation
+    /// of `x.div_ceil(y)`.
+    ///
+    /// ### Why is this bad?
+    /// It's simpler, clearer and more readable.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// let x: i32 = 7;
+    /// let y: i32 = 4;
+    /// let div = (x + (y - 1)) / y;
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// #![feature(int_roundings)]
+    /// let x: i32 = 7;
+    /// let y: i32 = 4;
+    /// let div = x.div_ceil(y);
+    /// ```
+    #[clippy::version = "1.81.0"]
+    pub MANUAL_DIV_CEIL,
+    complexity,
+    "manually reimplementing `div_ceil`"
+}
+
+pub struct ManualDivCeil {
+    msrv: Msrv,
+}
+
+impl ManualDivCeil {
+    #[must_use]
+    pub fn new(conf: &'static Conf) -> Self {
+        Self {
+            msrv: conf.msrv.clone(),
+        }
+    }
+}
+
+impl_lint_pass!(ManualDivCeil => [MANUAL_DIV_CEIL]);
+
+impl<'tcx> LateLintPass<'tcx> for ManualDivCeil {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
+        if !self.msrv.meets(msrvs::MANUAL_DIV_CEIL) {
+            return;
+        }
+
+        let mut applicability = Applicability::MachineApplicable;
+
+        if let ExprKind::Binary(div_op, div_lhs, div_rhs) = expr.kind
+            && div_op.node == BinOpKind::Div
+            && check_int_ty_and_feature(cx, div_lhs)
+            && check_int_ty_and_feature(cx, div_rhs)
+            && let ExprKind::Binary(inner_op, inner_lhs, inner_rhs) = div_lhs.kind
+        {
+            // (x + (y - 1)) / y
+            if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_rhs.kind
+                && inner_op.node == BinOpKind::Add
+                && sub_op.node == BinOpKind::Sub
+                && check_literal(sub_rhs)
+                && check_eq_expr(cx, sub_lhs, div_rhs)
+            {
+                build_suggestion(cx, expr, inner_lhs, div_rhs, &mut applicability);
+                return;
+            }
+
+            // ((y - 1) + x) / y
+            if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_lhs.kind
+                && inner_op.node == BinOpKind::Add
+                && sub_op.node == BinOpKind::Sub
+                && check_literal(sub_rhs)
+                && check_eq_expr(cx, sub_lhs, div_rhs)
+            {
+                build_suggestion(cx, expr, inner_rhs, div_rhs, &mut applicability);
+                return;
+            }
+
+            // (x + y - 1) / y
+            if let ExprKind::Binary(add_op, add_lhs, add_rhs) = inner_lhs.kind
+                && inner_op.node == BinOpKind::Sub
+                && add_op.node == BinOpKind::Add
+                && check_literal(inner_rhs)
+                && check_eq_expr(cx, add_rhs, div_rhs)
+            {
+                build_suggestion(cx, expr, add_lhs, div_rhs, &mut applicability);
+            }
+        }
+    }
+
+    extract_msrv_attr!(LateContext);
+}
+
+fn check_int_ty_and_feature(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    let expr_ty = cx.typeck_results().expr_ty(expr);
+    match expr_ty.peel_refs().kind() {
+        ty::Uint(_) => true,
+        ty::Int(_) => cx
+            .tcx
+            .features()
+            .declared_features
+            .contains(&Symbol::intern("int_roundings")),
+
+        _ => false,
+    }
+}
+
+fn check_literal(expr: &Expr<'_>) -> bool {
+    if let ExprKind::Lit(lit) = expr.kind
+        && let LitKind::Int(Pu128(1), _) = lit.node
+    {
+        return true;
+    }
+    false
+}
+
+fn check_eq_expr(cx: &LateContext<'_>, lhs: &Expr<'_>, rhs: &Expr<'_>) -> bool {
+    SpanlessEq::new(cx).eq_expr(lhs, rhs)
+}
+
+fn build_suggestion(
+    cx: &LateContext<'_>,
+    expr: &Expr<'_>,
+    lhs: &Expr<'_>,
+    rhs: &Expr<'_>,
+    applicability: &mut Applicability,
+) {
+    let dividend_sugg = Sugg::hir_with_applicability(cx, lhs, "..", applicability).maybe_par();
+    let divisor_snippet = snippet_with_applicability(cx, rhs.span.source_callsite(), "..", applicability);
+
+    let sugg = format!("{dividend_sugg}.div_ceil({divisor_snippet})");
+
+    span_lint_and_sugg(
+        cx,
+        MANUAL_DIV_CEIL,
+        expr.span,
+        "manually reimplementing `div_ceil`",
+        "consider using `.div_ceil()`",
+        sugg,
+        *applicability,
+    );
+}
diff --git a/clippy_lints/src/manual_is_power_of_two.rs b/clippy_lints/src/manual_is_power_of_two.rs
new file mode 100644
index 00000000000..d7879403ebb
--- /dev/null
+++ b/clippy_lints/src/manual_is_power_of_two.rs
@@ -0,0 +1,142 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::SpanlessEq;
+use rustc_ast::LitKind;
+use rustc_data_structures::packed::Pu128;
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::Uint;
+use rustc_session::declare_lint_pass;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for expressions like `x.count_ones() == 1` or `x & (x - 1) == 0`, with x and unsigned integer, which are manual
+    /// reimplementations of `x.is_power_of_two()`.
+    /// ### Why is this bad?
+    /// Manual reimplementations of `is_power_of_two` increase code complexity for little benefit.
+    /// ### Example
+    /// ```no_run
+    /// let a: u32 = 4;
+    /// let result = a.count_ones() == 1;
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// let a: u32 = 4;
+    /// let result = a.is_power_of_two();
+    /// ```
+    #[clippy::version = "1.82.0"]
+    pub MANUAL_IS_POWER_OF_TWO,
+    complexity,
+    "manually reimplementing `is_power_of_two`"
+}
+
+declare_lint_pass!(ManualIsPowerOfTwo => [MANUAL_IS_POWER_OF_TWO]);
+
+impl LateLintPass<'_> for ManualIsPowerOfTwo {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+        let mut applicability = Applicability::MachineApplicable;
+
+        if let ExprKind::Binary(bin_op, left, right) = expr.kind
+            && bin_op.node == BinOpKind::Eq
+        {
+            // a.count_ones() == 1
+            if let ExprKind::MethodCall(method_name, reciever, _, _) = left.kind
+                && method_name.ident.as_str() == "count_ones"
+                && let &Uint(_) = cx.typeck_results().expr_ty(reciever).kind()
+                && check_lit(right, 1)
+            {
+                build_sugg(cx, expr, reciever, &mut applicability);
+            }
+
+            // 1 == a.count_ones()
+            if let ExprKind::MethodCall(method_name, reciever, _, _) = right.kind
+                && method_name.ident.as_str() == "count_ones"
+                && let &Uint(_) = cx.typeck_results().expr_ty(reciever).kind()
+                && check_lit(left, 1)
+            {
+                build_sugg(cx, expr, reciever, &mut applicability);
+            }
+
+            // a & (a - 1) == 0
+            if let ExprKind::Binary(op1, left1, right1) = left.kind
+                && op1.node == BinOpKind::BitAnd
+                && let ExprKind::Binary(op2, left2, right2) = right1.kind
+                && op2.node == BinOpKind::Sub
+                && check_eq_expr(cx, left1, left2)
+                && let &Uint(_) = cx.typeck_results().expr_ty(left1).kind()
+                && check_lit(right2, 1)
+                && check_lit(right, 0)
+            {
+                build_sugg(cx, expr, left1, &mut applicability);
+            }
+
+            // (a - 1) & a == 0;
+            if let ExprKind::Binary(op1, left1, right1) = left.kind
+                && op1.node == BinOpKind::BitAnd
+                && let ExprKind::Binary(op2, left2, right2) = left1.kind
+                && op2.node == BinOpKind::Sub
+                && check_eq_expr(cx, right1, left2)
+                && let &Uint(_) = cx.typeck_results().expr_ty(right1).kind()
+                && check_lit(right2, 1)
+                && check_lit(right, 0)
+            {
+                build_sugg(cx, expr, right1, &mut applicability);
+            }
+
+            // 0 == a & (a - 1);
+            if let ExprKind::Binary(op1, left1, right1) = right.kind
+                && op1.node == BinOpKind::BitAnd
+                && let ExprKind::Binary(op2, left2, right2) = right1.kind
+                && op2.node == BinOpKind::Sub
+                && check_eq_expr(cx, left1, left2)
+                && let &Uint(_) = cx.typeck_results().expr_ty(left1).kind()
+                && check_lit(right2, 1)
+                && check_lit(left, 0)
+            {
+                build_sugg(cx, expr, left1, &mut applicability);
+            }
+
+            // 0 == (a - 1) & a
+            if let ExprKind::Binary(op1, left1, right1) = right.kind
+                && op1.node == BinOpKind::BitAnd
+                && let ExprKind::Binary(op2, left2, right2) = left1.kind
+                && op2.node == BinOpKind::Sub
+                && check_eq_expr(cx, right1, left2)
+                && let &Uint(_) = cx.typeck_results().expr_ty(right1).kind()
+                && check_lit(right2, 1)
+                && check_lit(left, 0)
+            {
+                build_sugg(cx, expr, right1, &mut applicability);
+            }
+        }
+    }
+}
+
+fn build_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, reciever: &Expr<'_>, applicability: &mut Applicability) {
+    let snippet = snippet_with_applicability(cx, reciever.span, "..", applicability);
+
+    span_lint_and_sugg(
+        cx,
+        MANUAL_IS_POWER_OF_TWO,
+        expr.span,
+        "manually reimplementing `is_power_of_two`",
+        "consider using `.is_power_of_two()`",
+        format!("{snippet}.is_power_of_two()"),
+        *applicability,
+    );
+}
+
+fn check_lit(expr: &Expr<'_>, expected_num: u128) -> bool {
+    if let ExprKind::Lit(lit) = expr.kind
+        && let LitKind::Int(Pu128(num), _) = lit.node
+        && num == expected_num
+    {
+        return true;
+    }
+    false
+}
+
+fn check_eq_expr(cx: &LateContext<'_>, lhs: &Expr<'_>, rhs: &Expr<'_>) -> bool {
+    SpanlessEq::new(cx).eq_expr(lhs, rhs)
+}
diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs
index 6b1d90483cc..01f1d9c3beb 100644
--- a/clippy_lints/src/manual_non_exhaustive.rs
+++ b/clippy_lints/src/manual_non_exhaustive.rs
@@ -2,16 +2,16 @@ use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
 use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
 use clippy_utils::is_doc_hidden;
-use clippy_utils::source::SpanRangeExt;
-use rustc_ast::ast::{self, VisibilityKind};
+use clippy_utils::source::snippet_indent;
+use itertools::Itertools;
 use rustc_ast::attr;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
 use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
-use rustc_hir::{self as hir, Expr, ExprKind, QPath};
-use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
+use rustc_hir::{Expr, ExprKind, Item, ItemKind, QPath, TyKind, VariantData};
+use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
-use rustc_span::def_id::{DefId, LocalDefId};
+use rustc_span::def_id::LocalDefId;
 use rustc_span::{sym, Span};
 
 declare_clippy_lint! {
@@ -62,29 +62,13 @@ declare_clippy_lint! {
     "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]"
 }
 
-#[expect(clippy::module_name_repetitions)]
-pub struct ManualNonExhaustiveStruct {
+pub struct ManualNonExhaustive {
     msrv: Msrv,
-}
-
-impl ManualNonExhaustiveStruct {
-    pub fn new(conf: &'static Conf) -> Self {
-        Self {
-            msrv: conf.msrv.clone(),
-        }
-    }
-}
-
-impl_lint_pass!(ManualNonExhaustiveStruct => [MANUAL_NON_EXHAUSTIVE]);
-
-#[expect(clippy::module_name_repetitions)]
-pub struct ManualNonExhaustiveEnum {
-    msrv: Msrv,
-    constructed_enum_variants: FxHashSet<(DefId, DefId)>,
+    constructed_enum_variants: FxHashSet<LocalDefId>,
     potential_enums: Vec<(LocalDefId, LocalDefId, Span, Span)>,
 }
 
-impl ManualNonExhaustiveEnum {
+impl ManualNonExhaustive {
     pub fn new(conf: &'static Conf) -> Self {
         Self {
             msrv: conf.msrv.clone(),
@@ -94,96 +78,78 @@ impl ManualNonExhaustiveEnum {
     }
 }
 
-impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]);
-
-impl EarlyLintPass for ManualNonExhaustiveStruct {
-    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
-        if let ast::ItemKind::Struct(variant_data, _) = &item.kind
-            && let (fields, delimiter) = match variant_data {
-                ast::VariantData::Struct { fields, .. } => (&**fields, '{'),
-                ast::VariantData::Tuple(fields, _) => (&**fields, '('),
-                ast::VariantData::Unit(_) => return,
-            }
-            && fields.len() > 1
-            && self.msrv.meets(msrvs::NON_EXHAUSTIVE)
-        {
-            let mut iter = fields.iter().filter_map(|f| match f.vis.kind {
-                VisibilityKind::Public => None,
-                VisibilityKind::Inherited => Some(Ok(f)),
-                VisibilityKind::Restricted { .. } => Some(Err(())),
-            });
-            if let Some(Ok(field)) = iter.next()
-                && iter.next().is_none()
-                && field.ty.kind.is_unit()
-            {
-                span_lint_and_then(
-                    cx,
-                    MANUAL_NON_EXHAUSTIVE,
-                    item.span,
-                    "this seems like a manual implementation of the non-exhaustive pattern",
-                    |diag| {
-                        if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive))
-                            && let header_span = cx.sess().source_map().span_until_char(item.span, delimiter)
-                            && let Some(snippet) = header_span.get_source_text(cx)
-                        {
-                            diag.span_suggestion(
-                                header_span,
-                                "add the attribute",
-                                format!("#[non_exhaustive] {snippet}"),
-                                Applicability::Unspecified,
-                            );
-                        }
-                        diag.span_help(field.span, "remove this field");
-                    },
-                );
-            }
-        }
-    }
+impl_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]);
 
-    extract_msrv_attr!(EarlyContext);
-}
-
-impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
-    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
-        if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) {
+impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustive {
+    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
+        if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) || !cx.effective_visibilities.is_exported(item.owner_id.def_id) {
             return;
         }
 
-        if let hir::ItemKind::Enum(def, _) = &item.kind
-            && def.variants.len() > 1
-        {
-            let mut iter = def.variants.iter().filter_map(|v| {
-                (matches!(v.data, hir::VariantData::Unit(_, _))
-                    && is_doc_hidden(cx.tcx.hir().attrs(v.hir_id))
-                    && !attr::contains_name(cx.tcx.hir().attrs(item.hir_id()), sym::non_exhaustive))
-                .then_some((v.def_id, v.span))
-            });
-            if let Some((id, span)) = iter.next()
-                && iter.next().is_none()
-            {
-                self.potential_enums.push((item.owner_id.def_id, id, item.span, span));
-            }
+        match item.kind {
+            ItemKind::Enum(def, _) if def.variants.len() > 1 => {
+                let iter = def.variants.iter().filter_map(|v| {
+                    (matches!(v.data, VariantData::Unit(_, _)) && is_doc_hidden(cx.tcx.hir().attrs(v.hir_id)))
+                        .then_some((v.def_id, v.span))
+                });
+                if let Ok((id, span)) = iter.exactly_one()
+                    && !attr::contains_name(cx.tcx.hir().attrs(item.hir_id()), sym::non_exhaustive)
+                {
+                    self.potential_enums.push((item.owner_id.def_id, id, item.span, span));
+                }
+            },
+            ItemKind::Struct(variant_data, _) => {
+                let fields = variant_data.fields();
+                let private_fields = fields
+                    .iter()
+                    .filter(|field| !cx.effective_visibilities.is_exported(field.def_id));
+                if fields.len() > 1
+                    && let Ok(field) = private_fields.exactly_one()
+                    && let TyKind::Tup([]) = field.ty.kind
+                {
+                    span_lint_and_then(
+                        cx,
+                        MANUAL_NON_EXHAUSTIVE,
+                        item.span,
+                        "this seems like a manual implementation of the non-exhaustive pattern",
+                        |diag| {
+                            if let Some(non_exhaustive) =
+                                attr::find_by_name(cx.tcx.hir().attrs(item.hir_id()), sym::non_exhaustive)
+                            {
+                                diag.span_note(non_exhaustive.span, "the struct is already non-exhaustive");
+                            } else {
+                                let indent = snippet_indent(cx, item.span).unwrap_or_default();
+                                diag.span_suggestion_verbose(
+                                    item.span.shrink_to_lo(),
+                                    "use the `#[non_exhaustive]` attribute instead",
+                                    format!("#[non_exhaustive]\n{indent}"),
+                                    Applicability::MaybeIncorrect,
+                                );
+                            }
+                            diag.span_help(field.span, "remove this field");
+                        },
+                    );
+                }
+            },
+            _ => {},
         }
     }
 
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
         if let ExprKind::Path(QPath::Resolved(None, p)) = &e.kind
-            && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res
+            && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), ctor_id) = p.res
+            && let Some(local_ctor) = ctor_id.as_local()
         {
-            let variant_id = cx.tcx.parent(id);
-            let enum_id = cx.tcx.parent(variant_id);
-
-            self.constructed_enum_variants.insert((enum_id, variant_id));
+            let variant_id = cx.tcx.local_parent(local_ctor);
+            self.constructed_enum_variants.insert(variant_id);
         }
     }
 
     fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
-        for &(enum_id, _, enum_span, variant_span) in
-            self.potential_enums.iter().filter(|&&(enum_id, variant_id, _, _)| {
-                !self
-                    .constructed_enum_variants
-                    .contains(&(enum_id.to_def_id(), variant_id.to_def_id()))
-            })
+        for &(enum_id, _, enum_span, variant_span) in self
+            .potential_enums
+            .iter()
+            .filter(|(_, variant_id, _, _)| !self.constructed_enum_variants.contains(variant_id))
         {
             let hir_id = cx.tcx.local_def_id_to_hir_id(enum_id);
             span_lint_hir_and_then(
@@ -193,15 +159,13 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
                 enum_span,
                 "this seems like a manual implementation of the non-exhaustive pattern",
                 |diag| {
-                    let header_span = cx.sess().source_map().span_until_char(enum_span, '{');
-                    if let Some(snippet) = header_span.get_source_text(cx) {
-                        diag.span_suggestion(
-                            header_span,
-                            "add the attribute",
-                            format!("#[non_exhaustive] {snippet}"),
-                            Applicability::Unspecified,
-                        );
-                    }
+                    let indent = snippet_indent(cx, enum_span).unwrap_or_default();
+                    diag.span_suggestion_verbose(
+                        enum_span.shrink_to_lo(),
+                        "use the `#[non_exhaustive]` attribute instead",
+                        format!("#[non_exhaustive]\n{indent}"),
+                        Applicability::MaybeIncorrect,
+                    );
                     diag.span_help(variant_span, "remove this variant");
                 },
             );
diff --git a/clippy_lints/src/manual_range_patterns.rs b/clippy_lints/src/manual_range_patterns.rs
index 9afb2b5bc84..66d2ab6b24a 100644
--- a/clippy_lints/src/manual_range_patterns.rs
+++ b/clippy_lints/src/manual_range_patterns.rs
@@ -77,9 +77,10 @@ impl Num {
 impl LateLintPass<'_> for ManualRangePatterns {
     fn check_pat(&mut self, cx: &LateContext<'_>, pat: &'_ rustc_hir::Pat<'_>) {
         // a pattern like 1 | 2 seems fine, lint if there are at least 3 alternatives
-        // or at least one range
+        // or more then one range (exclude triggering on stylistic using OR with one element
+        // like described https://github.com/rust-lang/rust-clippy/issues/11825)
         if let PatKind::Or(pats) = pat.kind
-            && (pats.len() >= 3 || pats.iter().any(|p| matches!(p.kind, PatKind::Range(..))))
+            && (pats.len() >= 3 || (pats.len() > 1 && pats.iter().any(|p| matches!(p.kind, PatKind::Range(..)))))
             && !in_external_macro(cx.sess(), pat.span)
         {
             let mut min = Num::dummy(i128::MAX);
diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs
index b6930f7b9d1..e7cef5bdbd7 100644
--- a/clippy_lints/src/matches/single_match.rs
+++ b/clippy_lints/src/matches/single_match.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::{expr_block, snippet, SpanRangeExt};
+use clippy_utils::source::{expr_block, snippet, snippet_block_with_context, SpanRangeExt};
 use clippy_utils::ty::implements_trait;
 use clippy_utils::{
     is_lint_allowed, is_unit_expr, peel_blocks, peel_hir_pat_refs, peel_middle_ty_refs, peel_n_hir_expr_refs,
@@ -9,7 +9,7 @@ use rustc_arena::DroplessArena;
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::intravisit::{walk_pat, Visitor};
-use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind, QPath};
+use rustc_hir::{Arm, Expr, ExprKind, HirId, Node, Pat, PatKind, QPath, StmtKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, AdtDef, ParamEnv, TyCtxt, TypeckResults, VariantDef};
 use rustc_span::{sym, Span};
@@ -91,6 +91,29 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp
         format!(" else {}", expr_block(cx, els, ctxt, "..", Some(expr.span), &mut app))
     });
 
+    if snippet(cx, ex.span, "..") == snippet(cx, arm.pat.span, "..") {
+        let msg = "this pattern is irrefutable, `match` is useless";
+        let (sugg, help) = if is_unit_expr(arm.body) {
+            (String::new(), "`match` expression can be removed")
+        } else {
+            let mut sugg = snippet_block_with_context(cx, arm.body.span, ctxt, "..", Some(expr.span), &mut app)
+                .0
+                .to_string();
+            if let Node::Stmt(stmt) = cx.tcx.parent_hir_node(expr.hir_id)
+                && let StmtKind::Expr(_) = stmt.kind
+                && match arm.body.kind {
+                    ExprKind::Block(block, _) => block.span.from_expansion(),
+                    _ => true,
+                }
+            {
+                sugg.push(';');
+            }
+            (sugg, "try")
+        };
+        span_lint_and_sugg(cx, lint, expr.span, msg, help, sugg.to_string(), app);
+        return;
+    }
+
     let (pat, pat_ref_count) = peel_hir_pat_refs(arm.pat);
     let (msg, sugg) = if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind
         && let (ty, ty_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(ex))
diff --git a/clippy_lints/src/methods/iter_kv_map.rs b/clippy_lints/src/methods/iter_kv_map.rs
index 33de3b87abc..390dd24b505 100644
--- a/clippy_lints/src/methods/iter_kv_map.rs
+++ b/clippy_lints/src/methods/iter_kv_map.rs
@@ -14,7 +14,6 @@ use rustc_span::sym;
 /// - `hashmap.into_iter().map(|(_, v)| v)`
 ///
 /// on `HashMaps` and `BTreeMaps` in std
-
 pub(super) fn check<'tcx>(
     cx: &LateContext<'tcx>,
     map_type: &'tcx str,     // iter / into_iter
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index f61bb3a6bf4..098d3fca9b2 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -111,6 +111,7 @@ mod uninit_assumed_init;
 mod unit_hash;
 mod unnecessary_fallible_conversions;
 mod unnecessary_filter_map;
+mod unnecessary_first_then_check;
 mod unnecessary_fold;
 mod unnecessary_get_then_check;
 mod unnecessary_iter_cloned;
@@ -441,6 +442,17 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    ///
+    /// Use instead:
+    /// ```no_run
+    /// # struct X;
+    /// impl X {
+    ///     fn as_str(&self) -> &'static str {
+    ///         // ..
+    /// # ""
+    ///     }
+    /// }
+    /// ```
     #[clippy::version = "pre 1.29.0"]
     pub WRONG_SELF_CONVENTION,
     style,
@@ -3964,7 +3976,7 @@ declare_clippy_lint! {
     /// ```no_run
     /// let _ = 0;
     /// ```
-    #[clippy::version = "1.78.0"]
+    #[clippy::version = "1.81.0"]
     pub UNNECESSARY_MIN_OR_MAX,
     complexity,
     "using 'min()/max()' when there is no need for it"
@@ -4025,7 +4037,7 @@ declare_clippy_lint! {
     /// ```
     #[clippy::version = "1.78.0"]
     pub MANUAL_C_STR_LITERALS,
-    pedantic,
+    complexity,
     r#"creating a `CStr` through functions when `c""` literals can be used"#
 }
 
@@ -4099,7 +4111,7 @@ declare_clippy_lint! {
     /// ```no_run
     /// "foo".is_ascii();
     /// ```
-    #[clippy::version = "1.80.0"]
+    #[clippy::version = "1.81.0"]
     pub NEEDLESS_CHARACTER_ITERATION,
     suspicious,
     "is_ascii() called on a char iterator"
@@ -4126,6 +4138,34 @@ declare_clippy_lint! {
     "use of `map` returning the original item"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks the usage of `.first().is_some()` or `.first().is_none()` to check if a slice is
+    /// empty.
+    ///
+    /// ### Why is this bad?
+    /// Using `.is_empty()` is shorter and better communicates the intention.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// let v = vec![1, 2, 3];
+    /// if v.first().is_none() {
+    ///     // The vector is empty...
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// let v = vec![1, 2, 3];
+    /// if v.is_empty() {
+    ///     // The vector is empty...
+    /// }
+    /// ```
+    #[clippy::version = "1.83.0"]
+    pub UNNECESSARY_FIRST_THEN_CHECK,
+    complexity,
+    "calling `.first().is_some()` or `.first().is_none()` instead of `.is_empty()`"
+}
+
 pub struct Methods {
     avoid_breaking_exported_api: bool,
     msrv: Msrv,
@@ -4283,6 +4323,7 @@ impl_lint_pass!(Methods => [
     UNNECESSARY_RESULT_MAP_OR_ELSE,
     MANUAL_C_STR_LITERALS,
     UNNECESSARY_GET_THEN_CHECK,
+    UNNECESSARY_FIRST_THEN_CHECK,
     NEEDLESS_CHARACTER_ITERATION,
     MANUAL_INSPECT,
     UNNECESSARY_MIN_OR_MAX,
@@ -5055,6 +5096,9 @@ fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>,
         Some(("get", f_recv, [arg], _, _)) => {
             unnecessary_get_then_check::check(cx, call_span, recv, f_recv, arg, is_some);
         },
+        Some(("first", f_recv, [], _, _)) => {
+            unnecessary_first_then_check::check(cx, call_span, recv, f_recv, is_some);
+        },
         _ => {},
     }
 }
diff --git a/clippy_lints/src/methods/unnecessary_first_then_check.rs b/clippy_lints/src/methods/unnecessary_first_then_check.rs
new file mode 100644
index 00000000000..7ae1bb54e60
--- /dev/null
+++ b/clippy_lints/src/methods/unnecessary_first_then_check.rs
@@ -0,0 +1,56 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::SpanRangeExt;
+
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_span::Span;
+
+use super::UNNECESSARY_FIRST_THEN_CHECK;
+
+pub(super) fn check(
+    cx: &LateContext<'_>,
+    call_span: Span,
+    first_call: &Expr<'_>,
+    first_caller: &Expr<'_>,
+    is_some: bool,
+) {
+    if !cx
+        .typeck_results()
+        .expr_ty_adjusted(first_caller)
+        .peel_refs()
+        .is_slice()
+    {
+        return;
+    }
+
+    let ExprKind::MethodCall(_, _, _, first_call_span) = first_call.kind else {
+        return;
+    };
+
+    let both_calls_span = first_call_span.with_hi(call_span.hi());
+    if let Some(both_calls_snippet) = both_calls_span.get_source_text(cx)
+        && let Some(first_caller_snippet) = first_caller.span.get_source_text(cx)
+    {
+        let (sugg_span, suggestion) = if is_some {
+            (
+                first_caller.span.with_hi(call_span.hi()),
+                format!("!{first_caller_snippet}.is_empty()"),
+            )
+        } else {
+            (both_calls_span, "is_empty()".to_owned())
+        };
+        span_lint_and_sugg(
+            cx,
+            UNNECESSARY_FIRST_THEN_CHECK,
+            sugg_span,
+            format!(
+                "unnecessary use of `{both_calls_snippet}` to check if slice {}",
+                if is_some { "is not empty" } else { "is empty" }
+            ),
+            "replace this with",
+            suggestion,
+            Applicability::MaybeIncorrect,
+        );
+    }
+}
diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs
index ccc8d17970e..380ea317b94 100644
--- a/clippy_lints/src/methods/unnecessary_fold.rs
+++ b/clippy_lints/src/methods/unnecessary_fold.rs
@@ -150,18 +150,20 @@ pub(super) fn check(
                     },
                 );
             },
-            ast::LitKind::Int(Pu128(0), _) => check_fold_with_op(
-                cx,
-                expr,
-                acc,
-                fold_span,
-                hir::BinOpKind::Add,
-                Replacement {
-                    has_args: false,
-                    has_generic_return: needs_turbofish(cx, expr),
-                    method_name: "sum",
-                },
-            ),
+            ast::LitKind::Int(Pu128(0), _) => {
+                check_fold_with_op(
+                    cx,
+                    expr,
+                    acc,
+                    fold_span,
+                    hir::BinOpKind::Add,
+                    Replacement {
+                        has_args: false,
+                        has_generic_return: needs_turbofish(cx, expr),
+                        method_name: "sum",
+                    },
+                );
+            },
             ast::LitKind::Int(Pu128(1), _) => {
                 check_fold_with_op(
                     cx,
diff --git a/clippy_lints/src/methods/unnecessary_min_or_max.rs b/clippy_lints/src/methods/unnecessary_min_or_max.rs
index 86c0a6322b6..ff5fa7d33e3 100644
--- a/clippy_lints/src/methods/unnecessary_min_or_max.rs
+++ b/clippy_lints/src/methods/unnecessary_min_or_max.rs
@@ -1,16 +1,15 @@
 use std::cmp::Ordering;
 
 use super::UNNECESSARY_MIN_OR_MAX;
-use clippy_utils::diagnostics::span_lint_and_sugg;
-
 use clippy_utils::consts::{ConstEvalCtxt, Constant, ConstantSource, FullInt};
+use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet;
 
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_span::Span;
+use rustc_span::{sym, Span};
 
 pub(super) fn check<'tcx>(
     cx: &LateContext<'tcx>,
@@ -21,26 +20,30 @@ pub(super) fn check<'tcx>(
 ) {
     let typeck_results = cx.typeck_results();
     let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.param_env, typeck_results);
-    if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv)
-        && let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(arg)
+    if let Some(id) = typeck_results.type_dependent_def_id(expr.hir_id)
+        && (cx.tcx.is_diagnostic_item(sym::cmp_ord_min, id) || cx.tcx.is_diagnostic_item(sym::cmp_ord_max, id))
     {
-        let Some(ord) = Constant::partial_cmp(cx.tcx, typeck_results.expr_ty(recv), &left, &right) else {
-            return;
-        };
+        if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv)
+            && let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(arg)
+        {
+            let Some(ord) = Constant::partial_cmp(cx.tcx, typeck_results.expr_ty(recv), &left, &right) else {
+                return;
+            };
 
-        lint(cx, expr, name, recv.span, arg.span, ord);
-    } else if let Some(extrema) = detect_extrema(cx, recv) {
-        let ord = match extrema {
-            Extrema::Minimum => Ordering::Less,
-            Extrema::Maximum => Ordering::Greater,
-        };
-        lint(cx, expr, name, recv.span, arg.span, ord);
-    } else if let Some(extrema) = detect_extrema(cx, arg) {
-        let ord = match extrema {
-            Extrema::Minimum => Ordering::Greater,
-            Extrema::Maximum => Ordering::Less,
-        };
-        lint(cx, expr, name, recv.span, arg.span, ord);
+            lint(cx, expr, name, recv.span, arg.span, ord);
+        } else if let Some(extrema) = detect_extrema(cx, recv) {
+            let ord = match extrema {
+                Extrema::Minimum => Ordering::Less,
+                Extrema::Maximum => Ordering::Greater,
+            };
+            lint(cx, expr, name, recv.span, arg.span, ord);
+        } else if let Some(extrema) = detect_extrema(cx, arg) {
+            let ord = match extrema {
+                Extrema::Minimum => Ordering::Greater,
+                Extrema::Maximum => Ordering::Less,
+            };
+            lint(cx, expr, name, recv.span, arg.span, ord);
+        }
     }
 }
 
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/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs
index bf7555a29e2..f62cf55e72a 100644
--- a/clippy_lints/src/methods/unnecessary_to_owned.rs
+++ b/clippy_lints/src/methods/unnecessary_to_owned.rs
@@ -717,7 +717,7 @@ fn check_if_applicable_to_argument<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'tcx
 // check that:
 // 1. This is a method with only one argument that doesn't come from a trait.
 // 2. That it has `Borrow` in its generic predicates.
-// 3. `Self` is a std "map type" (ie `HashSet`, `HashMap`, BTreeSet`, `BTreeMap`).
+// 3. `Self` is a std "map type" (ie `HashSet`, `HashMap`, `BTreeSet`, `BTreeMap`).
 fn check_borrow_predicate<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
     if let ExprKind::MethodCall(_, caller, &[arg], _) = expr.kind
         && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs
index a9aafe7ed56..3e80e48a948 100644
--- a/clippy_lints/src/misc.rs
+++ b/clippy_lints/src/misc.rs
@@ -7,6 +7,7 @@ use clippy_utils::{
 };
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
+use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{
     BinOpKind, BindingMode, Body, ByRef, Expr, ExprKind, FnDecl, Mutability, PatKind, QPath, Stmt, StmtKind,
@@ -82,6 +83,45 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
+    /// Checks for the use of item with a single leading
+    /// underscore.
+    ///
+    /// ### Why is this bad?
+    /// A single leading underscore is usually used to indicate
+    /// that a item will not be used. Using such a item breaks this
+    /// expectation.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// fn _foo() {}
+    ///
+    /// struct _FooStruct {}
+    ///
+    /// fn main() {
+    ///     _foo();
+    ///     let _ = _FooStruct{};
+    /// }
+    /// ```
+    ///
+    /// Use instead:
+    /// ```no_run
+    /// fn foo() {}
+    ///
+    /// struct FooStruct {}
+    ///
+    /// fn main() {
+    ///     foo();
+    ///     let _ = FooStruct{};
+    /// }
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub USED_UNDERSCORE_ITEMS,
+    pedantic,
+    "using a item which is prefixed with an underscore"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
     /// Checks for the use of short circuit boolean conditions as
     /// a
     /// statement.
@@ -104,6 +144,7 @@ declare_clippy_lint! {
 declare_lint_pass!(LintPass => [
     TOPLEVEL_REF_ARG,
     USED_UNDERSCORE_BINDING,
+    USED_UNDERSCORE_ITEMS,
     SHORT_CIRCUIT_STATEMENT,
 ]);
 
@@ -205,51 +246,104 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
         {
             return;
         }
-        let (definition_hir_id, ident) = match expr.kind {
-            ExprKind::Path(ref qpath) => {
-                if let QPath::Resolved(None, path) = qpath
-                    && let Res::Local(id) = path.res
-                    && is_used(cx, expr)
-                {
-                    (id, last_path_segment(qpath).ident)
-                } else {
-                    return;
-                }
-            },
-            ExprKind::Field(recv, ident) => {
-                if let Some(adt_def) = cx.typeck_results().expr_ty_adjusted(recv).ty_adt_def()
-                    && let Some(field) = adt_def.all_fields().find(|field| field.name == ident.name)
-                    && let Some(local_did) = field.did.as_local()
-                    && !cx.tcx.type_of(field.did).skip_binder().is_phantom_data()
-                {
-                    (cx.tcx.local_def_id_to_hir_id(local_did), ident)
-                } else {
-                    return;
-                }
+
+        used_underscore_binding(cx, expr);
+        used_underscore_items(cx, expr);
+    }
+}
+
+fn used_underscore_items<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+    let (def_id, ident) = match expr.kind {
+        ExprKind::Call(func, ..) => {
+            if let ExprKind::Path(QPath::Resolved(.., path)) = func.kind
+                && let Some(last_segment) = path.segments.last()
+                && let Res::Def(_, def_id) = last_segment.res
+            {
+                (def_id, last_segment.ident)
+            } else {
+                return;
+            }
+        },
+        ExprKind::MethodCall(path, ..) => {
+            if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
+                (def_id, path.ident)
+            } else {
+                return;
+            }
+        },
+        ExprKind::Struct(QPath::Resolved(_, path), ..) => {
+            if let Some(last_segment) = path.segments.last()
+                && let Res::Def(_, def_id) = last_segment.res
+            {
+                (def_id, last_segment.ident)
+            } else {
+                return;
+            }
+        },
+        _ => return,
+    };
+
+    let name = ident.name.as_str();
+    let definition_span = cx.tcx.def_span(def_id);
+    if name.starts_with('_')
+        && !name.starts_with("__")
+        && !definition_span.from_expansion()
+        && def_id.krate == LOCAL_CRATE
+    {
+        span_lint_and_then(
+            cx,
+            USED_UNDERSCORE_ITEMS,
+            expr.span,
+            "used underscore-prefixed item".to_string(),
+            |diag| {
+                diag.span_note(definition_span, "item is defined here".to_string());
             },
-            _ => return,
-        };
+        );
+    }
+}
 
-        let name = ident.name.as_str();
-        if name.starts_with('_')
-            && !name.starts_with("__")
-            && let definition_span = cx.tcx.hir().span(definition_hir_id)
-            && !definition_span.from_expansion()
-            && !fulfill_or_allowed(cx, USED_UNDERSCORE_BINDING, [expr.hir_id, definition_hir_id])
-        {
-            span_lint_and_then(
-                cx,
-                USED_UNDERSCORE_BINDING,
-                expr.span,
-                format!(
-                    "used binding `{name}` which is prefixed with an underscore. A leading \
-                     underscore signals that a binding will not be used"
-                ),
-                |diag| {
-                    diag.span_note(definition_span, format!("`{name}` is defined here"));
-                },
-            );
-        }
+fn used_underscore_binding<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+    let (definition_hir_id, ident) = match expr.kind {
+        ExprKind::Path(ref qpath) => {
+            if let QPath::Resolved(None, path) = qpath
+                && let Res::Local(id) = path.res
+                && is_used(cx, expr)
+            {
+                (id, last_path_segment(qpath).ident)
+            } else {
+                return;
+            }
+        },
+        ExprKind::Field(recv, ident) => {
+            if let Some(adt_def) = cx.typeck_results().expr_ty_adjusted(recv).ty_adt_def()
+                && let Some(field) = adt_def.all_fields().find(|field| field.name == ident.name)
+                && let Some(local_did) = field.did.as_local()
+                && !cx.tcx.type_of(field.did).skip_binder().is_phantom_data()
+            {
+                (cx.tcx.local_def_id_to_hir_id(local_did), ident)
+            } else {
+                return;
+            }
+        },
+        _ => return,
+    };
+
+    let name = ident.name.as_str();
+    if name.starts_with('_')
+        && !name.starts_with("__")
+        && let definition_span = cx.tcx.hir().span(definition_hir_id)
+        && !definition_span.from_expansion()
+        && !fulfill_or_allowed(cx, USED_UNDERSCORE_BINDING, [expr.hir_id, definition_hir_id])
+    {
+        span_lint_and_then(
+            cx,
+            USED_UNDERSCORE_BINDING,
+            expr.span,
+            "used underscore-prefixed binding".to_string(),
+            |diag| {
+                diag.span_note(definition_span, "binding is defined here".to_string());
+            },
+        );
     }
 }
 
diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs
index 0b3769ecb7c..0b7d97c32c5 100644
--- a/clippy_lints/src/mixed_read_write_in_expression.rs
+++ b/clippy_lints/src/mixed_read_write_in_expression.rs
@@ -1,4 +1,5 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
+use clippy_utils::macros::root_macro_call_first_node;
 use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id};
 use rustc_hir::intravisit::{walk_expr, Visitor};
 use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, LetStmt, Node, Stmt, StmtKind};
@@ -134,6 +135,11 @@ impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> {
     }
 
     fn report_diverging_sub_expr(&mut self, e: &Expr<'_>) {
+        if let Some(macro_call) = root_macro_call_first_node(self.cx, e) {
+            if self.cx.tcx.item_name(macro_call.def_id).as_str() == "todo" {
+                return;
+            }
+        }
         span_lint(self.cx, DIVERGING_SUB_EXPRESSION, e.span, "sub-expression diverges");
     }
 }
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_maybe_sized.rs b/clippy_lints/src/needless_maybe_sized.rs
index 62e41088f37..bb44ff37b20 100644
--- a/clippy_lints/src/needless_maybe_sized.rs
+++ b/clippy_lints/src/needless_maybe_sized.rs
@@ -26,7 +26,7 @@ declare_clippy_lint! {
     ///
     /// // or choose alternative bounds for `T` so that it can be unsized
     /// ```
-    #[clippy::version = "1.79.0"]
+    #[clippy::version = "1.81.0"]
     pub NEEDLESS_MAYBE_SIZED,
     suspicious,
     "a `?Sized` bound that is unusable due to a `Sized` requirement"
diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs
index addb4b1aee8..887070bcf9a 100644
--- a/clippy_lints/src/needless_pass_by_value.rs
+++ b/clippy_lints/src/needless_pass_by_value.rs
@@ -129,7 +129,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
             })
             .collect::<Vec<_>>();
 
-        // Collect moved variables and spans which will need dereferencings from the
+        // Collect moved variables and spans which will need dereferencing from the
         // function body.
         let MovedVariablesCtxt { moved_vars } = {
             let mut ctx = MovedVariablesCtxt::default();
@@ -148,12 +148,13 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
                 return;
             }
 
-            // Ignore `self`s.
-            if idx == 0 {
-                if let PatKind::Binding(.., ident, _) = arg.pat.kind {
-                    if ident.name == kw::SelfLower {
-                        continue;
-                    }
+            // Ignore `self`s and params whose variable name starts with an underscore
+            if let PatKind::Binding(.., ident, _) = arg.pat.kind {
+                if idx == 0 && ident.name == kw::SelfLower {
+                    continue;
+                }
+                if ident.name.as_str().starts_with('_') {
+                    continue;
                 }
             }
 
@@ -207,7 +208,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
                             )
                             .is_ok()
                             {
-                                diag.span_help(span, "consider marking this type as `Copy`");
+                                diag.span_help(span, "or consider marking this type as `Copy`");
                             }
                         }
                     }
diff --git a/clippy_lints/src/non_zero_suggestions.rs b/clippy_lints/src/non_zero_suggestions.rs
new file mode 100644
index 00000000000..90a9f2e994b
--- /dev/null
+++ b/clippy_lints/src/non_zero_suggestions.rs
@@ -0,0 +1,143 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use rustc_ast::ast::BinOpKind;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::{self, Ty};
+use rustc_session::declare_lint_pass;
+use rustc_span::symbol::sym;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for conversions from `NonZero` types to regular integer types,
+    /// and suggests using `NonZero` types for the target as well.
+    ///
+    /// ### Why is this bad?
+    /// Converting from `NonZero` types to regular integer types and then back to `NonZero`
+    /// types is less efficient and loses the type-safety guarantees provided by `NonZero` types.
+    /// Using `NonZero` types consistently can lead to more optimized code and prevent
+    /// certain classes of errors related to zero values.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// use std::num::{NonZeroU32, NonZeroU64};
+    ///
+    /// fn example(x: u64, y: NonZeroU32) {
+    ///     // Bad: Converting NonZeroU32 to u64 unnecessarily
+    ///     let r1 = x / u64::from(y.get());
+    ///     let r2 = x % u64::from(y.get());
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// use std::num::{NonZeroU32, NonZeroU64};
+    ///
+    /// fn example(x: u64, y: NonZeroU32) {
+    ///     // Good: Preserving the NonZero property
+    ///     let r1 = x / NonZeroU64::from(y);
+    ///     let r2 = x % NonZeroU64::from(y);
+    /// }
+    /// ```
+    #[clippy::version = "1.81.0"]
+    pub NON_ZERO_SUGGESTIONS,
+    restriction,
+    "suggests using `NonZero#` from `u#` or `i#` for more efficient and type-safe conversions"
+}
+
+declare_lint_pass!(NonZeroSuggestions => [NON_ZERO_SUGGESTIONS]);
+
+impl<'tcx> LateLintPass<'tcx> for NonZeroSuggestions {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+        if let ExprKind::Binary(op, _, rhs) = expr.kind
+            && matches!(op.node, BinOpKind::Div | BinOpKind::Rem)
+        {
+            check_non_zero_conversion(cx, rhs, Applicability::MachineApplicable);
+        } else {
+            // Check if the parent expression is a binary operation
+            let parent_is_binary = cx.tcx.hir().parent_iter(expr.hir_id).any(|(_, node)| {
+                matches!(node, rustc_hir::Node::Expr(parent_expr) if matches!(parent_expr.kind, ExprKind::Binary(..)))
+            });
+
+            if !parent_is_binary {
+                check_non_zero_conversion(cx, expr, Applicability::MaybeIncorrect);
+            }
+        }
+    }
+}
+
+fn check_non_zero_conversion(cx: &LateContext<'_>, expr: &Expr<'_>, applicability: Applicability) {
+    // Check if the expression is a function call with one argument
+    if let ExprKind::Call(func, [arg]) = expr.kind
+        && let ExprKind::Path(qpath) = &func.kind
+        && let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id()
+        && let ExprKind::MethodCall(rcv_path, receiver, _, _) = &arg.kind
+        && rcv_path.ident.name.as_str() == "get"
+    {
+        let fn_name = cx.tcx.item_name(def_id);
+        let target_ty = cx.typeck_results().expr_ty(expr);
+        let receiver_ty = cx.typeck_results().expr_ty(receiver);
+
+        // Check if the receiver type is a NonZero type
+        if let ty::Adt(adt_def, _) = receiver_ty.kind()
+            && adt_def.is_struct()
+            && cx.tcx.get_diagnostic_name(adt_def.did()) == Some(sym::NonZero)
+        {
+            if let Some(target_non_zero_type) = get_target_non_zero_type(target_ty) {
+                let arg_snippet = get_arg_snippet(cx, arg, rcv_path);
+                suggest_non_zero_conversion(cx, expr, fn_name, target_non_zero_type, &arg_snippet, applicability);
+            }
+        }
+    }
+}
+
+fn get_arg_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, rcv_path: &rustc_hir::PathSegment<'_>) -> String {
+    let arg_snippet = snippet(cx, arg.span, "..");
+    if let Some(index) = arg_snippet.rfind(&format!(".{}", rcv_path.ident.name)) {
+        arg_snippet[..index].trim().to_string()
+    } else {
+        arg_snippet.to_string()
+    }
+}
+
+fn suggest_non_zero_conversion(
+    cx: &LateContext<'_>,
+    expr: &Expr<'_>,
+    fn_name: rustc_span::Symbol,
+    target_non_zero_type: &str,
+    arg_snippet: &str,
+    applicability: Applicability,
+) {
+    let suggestion = format!("{target_non_zero_type}::{fn_name}({arg_snippet})");
+    span_lint_and_sugg(
+        cx,
+        NON_ZERO_SUGGESTIONS,
+        expr.span,
+        format!("consider using `{target_non_zero_type}::{fn_name}()` for more efficient and type-safe conversion"),
+        "replace with",
+        suggestion,
+        applicability,
+    );
+}
+
+fn get_target_non_zero_type(ty: Ty<'_>) -> Option<&'static str> {
+    match ty.kind() {
+        ty::Uint(uint_ty) => Some(match uint_ty {
+            ty::UintTy::U8 => "NonZeroU8",
+            ty::UintTy::U16 => "NonZeroU16",
+            ty::UintTy::U32 => "NonZeroU32",
+            ty::UintTy::U64 => "NonZeroU64",
+            ty::UintTy::U128 => "NonZeroU128",
+            ty::UintTy::Usize => "NonZeroUsize",
+        }),
+        ty::Int(int_ty) => Some(match int_ty {
+            ty::IntTy::I8 => "NonZeroI8",
+            ty::IntTy::I16 => "NonZeroI16",
+            ty::IntTy::I32 => "NonZeroI32",
+            ty::IntTy::I64 => "NonZeroI64",
+            ty::IntTy::I128 => "NonZeroI128",
+            ty::IntTy::Isize => "NonZeroIsize",
+        }),
+        _ => None,
+    }
+}
diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs
index 381975199d2..fa15d5e4f9f 100644
--- a/clippy_lints/src/panic_in_result_fn.rs
+++ b/clippy_lints/src/panic_in_result_fn.rs
@@ -1,8 +1,8 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::macros::root_macro_call_first_node;
-use clippy_utils::return_ty;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::visitors::{for_each_expr, Descend};
+use clippy_utils::{is_inside_always_const_context, return_ty};
 use core::ops::ControlFlow;
 use rustc_hir as hir;
 use rustc_hir::intravisit::FnKind;
@@ -68,10 +68,12 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir
         let Some(macro_call) = root_macro_call_first_node(cx, e) else {
             return ControlFlow::Continue(Descend::Yes);
         };
-        if matches!(
-            cx.tcx.item_name(macro_call.def_id).as_str(),
-            "panic" | "assert" | "assert_eq" | "assert_ne"
-        ) {
+        if !is_inside_always_const_context(cx.tcx, e.hir_id)
+            && matches!(
+                cx.tcx.item_name(macro_call.def_id).as_str(),
+                "panic" | "assert" | "assert_eq" | "assert_ne"
+            )
+        {
             panics.push(macro_call.span);
             ControlFlow::Continue(Descend::No)
         } else {
diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs
index 4eefd0065f6..fa5b02a5a41 100644
--- a/clippy_lints/src/panic_unimplemented.rs
+++ b/clippy_lints/src/panic_unimplemented.rs
@@ -1,8 +1,9 @@
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::is_in_test;
 use clippy_utils::macros::{is_panic, root_macro_call_first_node};
-use rustc_hir::Expr;
+use clippy_utils::{is_in_test, match_def_path, paths};
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::{Expr, ExprKind, QPath};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
 
@@ -95,10 +96,49 @@ impl_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANIC])
 
 impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        let Some(macro_call) = root_macro_call_first_node(cx, expr) else {
-            return;
-        };
-        if is_panic(cx, macro_call.def_id) {
+        if let Some(macro_call) = root_macro_call_first_node(cx, expr) {
+            if is_panic(cx, macro_call.def_id) {
+                if cx.tcx.hir().is_inside_const_context(expr.hir_id)
+                    || self.allow_panic_in_tests && is_in_test(cx.tcx, expr.hir_id)
+                {
+                    return;
+                }
+
+                span_lint(
+                    cx,
+                    PANIC,
+                    macro_call.span,
+                    "`panic` should not be present in production code",
+                );
+                return;
+            }
+            match cx.tcx.item_name(macro_call.def_id).as_str() {
+                "todo" => {
+                    span_lint(
+                        cx,
+                        TODO,
+                        macro_call.span,
+                        "`todo` should not be present in production code",
+                    );
+                },
+                "unimplemented" => {
+                    span_lint(
+                        cx,
+                        UNIMPLEMENTED,
+                        macro_call.span,
+                        "`unimplemented` should not be present in production code",
+                    );
+                },
+                "unreachable" => {
+                    span_lint(cx, UNREACHABLE, macro_call.span, "usage of the `unreachable!` macro");
+                },
+                _ => {},
+            }
+        } else if let ExprKind::Call(func, [_]) = expr.kind
+            && let ExprKind::Path(QPath::Resolved(None, expr_path)) = func.kind
+            && let Res::Def(DefKind::Fn, def_id) = expr_path.res
+            && match_def_path(cx, def_id, &paths::PANIC_ANY)
+        {
             if cx.tcx.hir().is_inside_const_context(expr.hir_id)
                 || self.allow_panic_in_tests && is_in_test(cx.tcx, expr.hir_id)
             {
@@ -108,32 +148,10 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
             span_lint(
                 cx,
                 PANIC,
-                macro_call.span,
-                "`panic` should not be present in production code",
+                expr.span,
+                "`panic_any` should not be present in production code",
             );
             return;
         }
-        match cx.tcx.item_name(macro_call.def_id).as_str() {
-            "todo" => {
-                span_lint(
-                    cx,
-                    TODO,
-                    macro_call.span,
-                    "`todo` should not be present in production code",
-                );
-            },
-            "unimplemented" => {
-                span_lint(
-                    cx,
-                    UNIMPLEMENTED,
-                    macro_call.span,
-                    "`unimplemented` should not be present in production code",
-                );
-            },
-            "unreachable" => {
-                span_lint(cx, UNREACHABLE, macro_call.span, "usage of the `unreachable!` macro");
-            },
-            _ => {},
-        }
     }
 }
diff --git a/clippy_lints/src/pointers_in_nomem_asm_block.rs b/clippy_lints/src/pointers_in_nomem_asm_block.rs
new file mode 100644
index 00000000000..385f634a150
--- /dev/null
+++ b/clippy_lints/src/pointers_in_nomem_asm_block.rs
@@ -0,0 +1,88 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_ast::InlineAsmOptions;
+use rustc_hir::{Expr, ExprKind, InlineAsm, InlineAsmOperand};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::declare_lint_pass;
+use rustc_span::Span;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks if any pointer is being passed to an asm! block with `nomem` option.
+    ///
+    /// ### Why is this bad?
+    /// `nomem` forbids any reads or writes to memory and passing a pointer suggests
+    /// that either of those will happen.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// fn f(p: *mut u32) {
+    ///     unsafe { core::arch::asm!("mov [{p}], 42", p = in(reg) p, options(nomem, nostack)); }
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// fn f(p: *mut u32) {
+    ///     unsafe { core::arch::asm!("mov [{p}], 42", p = in(reg) p, options(nostack)); }
+    /// }
+    /// ```
+    #[clippy::version = "1.81.0"]
+    pub POINTERS_IN_NOMEM_ASM_BLOCK,
+    suspicious,
+    "pointers in nomem asm block"
+}
+
+declare_lint_pass!(PointersInNomemAsmBlock => [POINTERS_IN_NOMEM_ASM_BLOCK]);
+
+impl<'tcx> LateLintPass<'tcx> for PointersInNomemAsmBlock {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
+        if let ExprKind::InlineAsm(asm) = &expr.kind {
+            check_asm(cx, asm);
+        }
+    }
+}
+
+fn check_asm(cx: &LateContext<'_>, asm: &InlineAsm<'_>) {
+    if !asm.options.contains(InlineAsmOptions::NOMEM) {
+        return;
+    }
+
+    let spans = asm
+        .operands
+        .iter()
+        .filter(|(op, _span)| has_in_operand_pointer(cx, op))
+        .map(|(_op, span)| *span)
+        .collect::<Vec<Span>>();
+
+    if spans.is_empty() {
+        return;
+    }
+
+    span_lint_and_then(
+        cx,
+        POINTERS_IN_NOMEM_ASM_BLOCK,
+        spans,
+        "passing pointers to nomem asm block",
+        additional_notes,
+    );
+}
+
+fn has_in_operand_pointer(cx: &LateContext<'_>, asm_op: &InlineAsmOperand<'_>) -> bool {
+    let asm_in_expr = match asm_op {
+        InlineAsmOperand::SymStatic { .. }
+        | InlineAsmOperand::Out { .. }
+        | InlineAsmOperand::Const { .. }
+        | InlineAsmOperand::SymFn { .. }
+        | InlineAsmOperand::Label { .. } => return false,
+        InlineAsmOperand::SplitInOut { in_expr, .. } => in_expr,
+        InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => expr,
+    };
+
+    // This checks for raw ptrs, refs and function pointers - the last one
+    // also technically counts as reading memory.
+    cx.typeck_results().expr_ty(asm_in_expr).is_any_ptr()
+}
+
+fn additional_notes(diag: &mut rustc_errors::Diag<'_, ()>) {
+    diag.note("`nomem` means that no memory write or read happens inside the asm! block");
+    diag.note("if this is intentional and no pointers are read or written to, consider allowing the lint");
+}
diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs
index 125f694996c..cd8e921347c 100644
--- a/clippy_lints/src/ptr.rs
+++ b/clippy_lints/src/ptr.rs
@@ -1,11 +1,9 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
 use clippy_utils::source::SpanRangeExt;
-use clippy_utils::ty::expr_sig;
 use clippy_utils::visitors::contains_unsafe_block;
 use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local};
 use hir::LifetimeName;
 use rustc_errors::{Applicability, MultiSpan};
-use rustc_hir::def_id::DefId;
 use rustc_hir::hir_id::{HirId, HirIdMap};
 use rustc_hir::intravisit::{walk_expr, Visitor};
 use rustc_hir::{
@@ -34,7 +32,7 @@ declare_clippy_lint! {
     /// with the appropriate `.to_owned()`/`to_string()` calls.
     ///
     /// ### Why is this bad?
-    /// Requiring the argument to be of the specific size
+    /// Requiring the argument to be of the specific type
     /// makes the function less useful for no benefit; slices in the form of `&[T]`
     /// or `&str` usually suffice and can be obtained from other types, too.
     ///
@@ -323,7 +321,6 @@ struct PtrArg<'tcx> {
     idx: usize,
     emission_id: HirId,
     span: Span,
-    ty_did: DefId,
     ty_name: Symbol,
     method_renames: &'static [(&'static str, &'static str)],
     ref_prefix: RefPrefix,
@@ -411,7 +408,6 @@ impl<'tcx> DerefTy<'tcx> {
     }
 }
 
-#[expect(clippy::too_many_lines)]
 fn check_fn_args<'cx, 'tcx: 'cx>(
     cx: &'cx LateContext<'tcx>,
     fn_sig: ty::FnSig<'tcx>,
@@ -514,7 +510,6 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
                     idx: i,
                     emission_id,
                     span: hir_ty.span,
-                    ty_did: adt.did(),
                     ty_name: name.ident.name,
                     method_renames,
                     ref_prefix: RefPrefix { lt: *lt, mutability },
@@ -610,65 +605,50 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, args: &[
                         set_skip_flag();
                     }
                 },
-                Some((Node::Expr(e), child_id)) => match e.kind {
-                    ExprKind::Call(f, expr_args) => {
-                        let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0);
-                        if expr_sig(self.cx, f).and_then(|sig| sig.input(i)).map_or(true, |ty| {
-                            match *ty.skip_binder().peel_refs().kind() {
-                                ty::Dynamic(preds, _, _) => !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds),
-                                ty::Param(_) => true,
-                                ty::Adt(def, _) => def.did() == args.ty_did,
-                                _ => false,
-                            }
-                        }) {
-                            // Passed to a function taking the non-dereferenced type.
-                            set_skip_flag();
-                        }
-                    },
-                    ExprKind::MethodCall(name, self_arg, expr_args, _) => {
-                        let i = iter::once(self_arg)
-                            .chain(expr_args.iter())
-                            .position(|arg| arg.hir_id == child_id)
-                            .unwrap_or(0);
-                        if i == 0 {
-                            // Check if the method can be renamed.
-                            let name = name.ident.as_str();
-                            if let Some((_, replacement)) = args.method_renames.iter().find(|&&(x, _)| x == name) {
-                                result.replacements.push(PtrArgReplacement {
-                                    expr_span: e.span,
-                                    self_span: self_arg.span,
-                                    replacement,
-                                });
-                                return;
-                            }
+                Some((Node::Expr(use_expr), child_id)) => {
+                    if let ExprKind::Index(e, ..) = use_expr.kind
+                        && e.hir_id == child_id
+                    {
+                        // Indexing works with both owned and its dereferenced type
+                        return;
+                    }
+
+                    if let ExprKind::MethodCall(name, receiver, ..) = use_expr.kind
+                        && receiver.hir_id == child_id
+                    {
+                        let name = name.ident.as_str();
+
+                        // Check if the method can be renamed.
+                        if let Some((_, replacement)) = args.method_renames.iter().find(|&&(x, _)| x == name) {
+                            result.replacements.push(PtrArgReplacement {
+                                expr_span: use_expr.span,
+                                self_span: receiver.span,
+                                replacement,
+                            });
+                            return;
                         }
 
-                        let Some(id) = self.cx.typeck_results().type_dependent_def_id(e.hir_id) else {
-                            set_skip_flag();
+                        // Some methods exist on both `[T]` and `Vec<T>`, such as `len`, where the receiver type
+                        // doesn't coerce to a slice and our adjusted type check below isn't enough,
+                        // but it would still be valid to call with a slice
+                        if is_allowed_vec_method(self.cx, use_expr) {
                             return;
-                        };
-
-                        match *self.cx.tcx.fn_sig(id).instantiate_identity().skip_binder().inputs()[i]
-                            .peel_refs()
-                            .kind()
-                        {
-                            ty::Dynamic(preds, _, _) if !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds) => {
-                                set_skip_flag();
-                            },
-                            ty::Param(_) => {
-                                set_skip_flag();
-                            },
-                            // If the types match check for methods which exist on both types. e.g. `Vec::len` and
-                            // `slice::len`
-                            ty::Adt(def, _) if def.did() == args.ty_did && !is_allowed_vec_method(self.cx, e) => {
-                                set_skip_flag();
-                            },
-                            _ => (),
                         }
-                    },
-                    // Indexing is fine for currently supported types.
-                    ExprKind::Index(e, _, _) if e.hir_id == child_id => (),
-                    _ => set_skip_flag(),
+                    }
+
+                    let deref_ty = args.deref_ty.ty(self.cx);
+                    let adjusted_ty = self.cx.typeck_results().expr_ty_adjusted(e).peel_refs();
+                    if adjusted_ty == deref_ty {
+                        return;
+                    }
+
+                    if let ty::Dynamic(preds, ..) = adjusted_ty.kind()
+                        && matches_preds(self.cx, deref_ty, preds)
+                    {
+                        return;
+                    }
+
+                    set_skip_flag();
                 },
                 _ => set_skip_flag(),
             }
diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs
index 5ce091b7a7a..1e709649695 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_with_context, SpanRangeExt};
 use clippy_utils::sugg::has_enclosing_paren;
-use clippy_utils::visitors::{for_each_expr, Descend};
+use clippy_utils::visitors::{for_each_expr, for_each_unconsumed_temporary, 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,
@@ -389,10 +389,24 @@ fn check_final_expr<'tcx>(
                 }
             };
 
-            let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
-            if borrows {
-                return;
+            if let Some(inner) = inner {
+                if for_each_unconsumed_temporary(cx, inner, |temporary_ty| {
+                    if temporary_ty.has_significant_drop(cx.tcx, cx.param_env)
+                        && temporary_ty
+                            .walk()
+                            .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(re) if !re.is_static()))
+                    {
+                        ControlFlow::Break(())
+                    } else {
+                        ControlFlow::Continue(())
+                    }
+                })
+                .is_break()
+                {
+                    return;
+                }
             }
+
             if ret_span.from_expansion() {
                 return;
             }
diff --git a/clippy_lints/src/set_contains_or_insert.rs b/clippy_lints/src/set_contains_or_insert.rs
index e6fe7649397..bc2a71c42b9 100644
--- a/clippy_lints/src/set_contains_or_insert.rs
+++ b/clippy_lints/src/set_contains_or_insert.rs
@@ -42,7 +42,7 @@ declare_clippy_lint! {
     ///     println!("inserted {value:?}");
     /// }
     /// ```
-    #[clippy::version = "1.80.0"]
+    #[clippy::version = "1.81.0"]
     pub SET_CONTAINS_OR_INSERT,
     nursery,
     "call to `<set>::contains` followed by `<set>::insert`"
diff --git a/clippy_lints/src/string_patterns.rs b/clippy_lints/src/string_patterns.rs
index 7e211d64da1..8af50ca87d6 100644
--- a/clippy_lints/src/string_patterns.rs
+++ b/clippy_lints/src/string_patterns.rs
@@ -33,7 +33,7 @@ declare_clippy_lint! {
     /// ```no_run
     /// "Hello World!".trim_end_matches(['.', ',', '!', '?']);
     /// ```
-    #[clippy::version = "1.80.0"]
+    #[clippy::version = "1.81.0"]
     pub MANUAL_PATTERN_CHAR_COMPARISON,
     style,
     "manual char comparison in string patterns"
diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs
index da7906b1183..ced53a808f9 100644
--- a/clippy_lints/src/transmute/mod.rs
+++ b/clippy_lints/src/transmute/mod.rs
@@ -527,24 +527,44 @@ declare_clippy_lint! {
     /// Checks if transmute calls have all generics specified.
     ///
     /// ### Why is this bad?
-    /// If not set, some unexpected output type could be retrieved instead of the expected one,
-    /// potentially leading to invalid code.
+    /// If not, one or more unexpected types could be used during `transmute()`, potentially leading
+    /// to Undefined Behavior or other problems.
     ///
-    /// This is particularly dangerous in case a seemingly innocent/unrelated change can cause type
-    /// inference to start inferring a different type. E.g. the transmute is the tail expression of
-    /// an `if` branch, and a different branches type changes, causing the transmute to silently
-    /// have a different type, instead of a proper error.
+    /// This is particularly dangerous in case a seemingly innocent/unrelated change causes type
+    /// inference to result in a different type. For example, if `transmute()` is the tail
+    /// expression of an `if`-branch, and the `else`-branch type changes, the compiler may silently
+    /// infer a different type to be returned by `transmute()`. That is because the compiler is
+    /// free to change the inference of a type as long as that inference is technically correct,
+    /// regardless of the programmer's unknown expectation.
+    ///
+    /// Both type-parameters, the input- and the output-type, to any `transmute()` should
+    /// be given explicitly: Setting the input-type explicitly avoids confusion about what the
+    /// argument's type actually is. Setting the output-type explicitly avoids type-inference
+    /// to infer a technically correct yet unexpected type.
     ///
     /// ### Example
     /// ```no_run
     /// # unsafe {
+    /// // Avoid "naked" calls to `transmute()`!
     /// let x: i32 = std::mem::transmute([1u16, 2u16]);
+    ///
+    /// // `first_answers` is intended to transmute a slice of bool to a slice of u8.
+    /// // But the programmer forgot to index the first element of the outer slice,
+    /// // so we are actually transmuting from "pointers to slices" instead of
+    /// // transmuting from "a slice of bool", causing a nonsensical result.
+    /// let the_answers: &[&[bool]] = &[&[true, false, true]];
+    /// let first_answers: &[u8] = std::mem::transmute(the_answers);
     /// # }
     /// ```
     /// Use instead:
     /// ```no_run
     /// # unsafe {
     /// let x = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]);
+    ///
+    /// // The explicit type parameters on `transmute()` makes the intention clear,
+    /// // and cause a type-error if the actual types don't match our expectation.
+    /// let the_answers: &[&[bool]] = &[&[true, false, true]];
+    /// let first_answers: &[u8] = std::mem::transmute::<&[bool], &[u8]>(the_answers[0]);
     /// # }
     /// ```
     #[clippy::version = "1.79.0"]
diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs
index 3a14927802b..120d5ffbbd3 100644
--- a/clippy_lints/src/types/mod.rs
+++ b/clippy_lints/src/types/mod.rs
@@ -261,8 +261,59 @@ declare_clippy_lint! {
     /// ### Example
     /// ```no_run
     /// # use std::rc::Rc;
-    /// struct Foo {
-    ///     inner: Rc<Vec<Vec<Box<(u32, u32, u32, u32)>>>>,
+    /// struct PointMatrixContainer {
+    ///     matrix: Rc<Vec<Vec<Box<(u32, u32, u32, u32)>>>>,
+    /// }
+    ///
+    /// fn main() {
+    ///     let point_matrix: Vec<Vec<Box<(u32, u32, u32, u32)>>> = vec![
+    ///         vec![
+    ///             Box::new((1, 2, 3, 4)),
+    ///             Box::new((5, 6, 7, 8)),
+    ///         ],
+    ///         vec![
+    ///             Box::new((9, 10, 11, 12)),
+    ///         ],
+    ///     ];
+    ///
+    ///     let shared_point_matrix: Rc<Vec<Vec<Box<(u32, u32, u32, u32)>>>> = Rc::new(point_matrix);
+    ///
+    ///     let container = PointMatrixContainer {
+    ///         matrix: shared_point_matrix,
+    ///     };
+    ///
+    ///     // ...
+    /// }
+    /// ```
+    /// Use instead:
+    /// ### Example
+    /// ```no_run
+    /// # use std::rc::Rc;
+    /// type PointMatrix = Vec<Vec<Box<(u32, u32, u32, u32)>>>;
+    /// type SharedPointMatrix = Rc<PointMatrix>;
+    ///
+    /// struct PointMatrixContainer {
+    ///     matrix: SharedPointMatrix,
+    /// }
+    ///
+    /// fn main() {
+    ///     let point_matrix: PointMatrix = vec![
+    ///         vec![
+    ///             Box::new((1, 2, 3, 4)),
+    ///             Box::new((5, 6, 7, 8)),
+    ///         ],
+    ///         vec![
+    ///             Box::new((9, 10, 11, 12)),
+    ///         ],
+    ///     ];
+    ///
+    ///     let shared_point_matrix: SharedPointMatrix = Rc::new(point_matrix);
+    ///
+    ///     let container = PointMatrixContainer {
+    ///         matrix: shared_point_matrix,
+    ///     };
+    ///
+    ///     // ...
     /// }
     /// ```
     #[clippy::version = "pre 1.29.0"]
diff --git a/clippy_lints/src/unused_trait_names.rs b/clippy_lints/src/unused_trait_names.rs
new file mode 100644
index 00000000000..c1cf58dcfce
--- /dev/null
+++ b/clippy_lints/src/unused_trait_names.rs
@@ -0,0 +1,94 @@
+use clippy_config::msrvs::{self, Msrv};
+use clippy_config::Conf;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_from_proc_macro;
+use clippy_utils::source::snippet_opt;
+use rustc_errors::Applicability;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::{Item, ItemKind, UseKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext as _};
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::Visibility;
+use rustc_session::impl_lint_pass;
+use rustc_span::symbol::kw;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for `use Trait` where the Trait is only used for its methods and not referenced by a path directly.
+    ///
+    /// ### Why is this bad?
+    /// Traits imported that aren't used directly can be imported anonymously with `use Trait as _`.
+    /// It is more explicit, avoids polluting the current scope with unused names and can be useful to show which imports are required for traits.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// use std::fmt::Write;
+    ///
+    /// fn main() {
+    ///     let mut s = String::new();
+    ///     let _ = write!(s, "hello, world!");
+    ///     println!("{s}");
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// use std::fmt::Write as _;
+    ///
+    /// fn main() {
+    ///     let mut s = String::new();
+    ///     let _ = write!(s, "hello, world!");
+    ///     println!("{s}");
+    /// }
+    /// ```
+    #[clippy::version = "1.83.0"]
+    pub UNUSED_TRAIT_NAMES,
+    restriction,
+    "use items that import a trait but only use it anonymously"
+}
+
+pub struct UnusedTraitNames {
+    msrv: Msrv,
+}
+
+impl UnusedTraitNames {
+    pub fn new(conf: &'static Conf) -> Self {
+        Self {
+            msrv: conf.msrv.clone(),
+        }
+    }
+}
+
+impl_lint_pass!(UnusedTraitNames => [UNUSED_TRAIT_NAMES]);
+
+impl<'tcx> LateLintPass<'tcx> for UnusedTraitNames {
+    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
+        if self.msrv.meets(msrvs::UNDERSCORE_IMPORTS)
+            && !in_external_macro(cx.sess(), item.span)
+            && let ItemKind::Use(path, UseKind::Single) = item.kind
+            // Ignore imports that already use Underscore
+            && item.ident.name != kw::Underscore
+            // Only check traits
+            && let Some(Res::Def(DefKind::Trait, _)) = path.res.first()
+            && cx.tcx.maybe_unused_trait_imports(()).contains(&item.owner_id.def_id)
+            // Only check this import if it is visible to its module only (no pub, pub(crate), ...)
+            && let module = cx.tcx.parent_module_from_def_id(item.owner_id.def_id)
+            && cx.tcx.visibility(item.owner_id.def_id) == Visibility::Restricted(module.to_def_id())
+            && let Some(last_segment) = path.segments.last()
+            && let Some(snip) = snippet_opt(cx, last_segment.ident.span)
+            && !is_from_proc_macro(cx, &last_segment.ident)
+        {
+            let complete_span = last_segment.ident.span.to(item.ident.span);
+            span_lint_and_sugg(
+                cx,
+                UNUSED_TRAIT_NAMES,
+                complete_span,
+                "importing trait that is only used anonymously",
+                "use",
+                format!("{snip} as _"),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+
+    extract_msrv_attr!(LateContext);
+}
diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs
index 2f7d54e73ed..1890b3fdd40 100644
--- a/clippy_lints/src/useless_conversion.rs
+++ b/clippy_lints/src/useless_conversion.rs
@@ -217,12 +217,12 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
                             },
                             ExprKind::MethodCall(.., args, _) => {
                                 cx.typeck_results().type_dependent_def_id(parent.hir_id).map(|did| {
-                                    return (
+                                    (
                                         did,
                                         args,
                                         cx.typeck_results().node_args(parent.hir_id),
                                         MethodOrFunction::Method,
-                                    );
+                                    )
                                 })
                             },
                             _ => None,
diff --git a/clippy_lints/src/zombie_processes.rs b/clippy_lints/src/zombie_processes.rs
new file mode 100644
index 00000000000..eda3d7820c1
--- /dev/null
+++ b/clippy_lints/src/zombie_processes.rs
@@ -0,0 +1,334 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::{fn_def_id, get_enclosing_block, match_any_def_paths, match_def_path, path_to_local_id, paths};
+use rustc_ast::Mutability;
+use rustc_errors::Applicability;
+use rustc_hir::intravisit::{walk_block, walk_expr, walk_local, Visitor};
+use rustc_hir::{Expr, ExprKind, HirId, LetStmt, Node, PatKind, Stmt, StmtKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::declare_lint_pass;
+use rustc_span::sym;
+use std::ops::ControlFlow;
+use ControlFlow::{Break, Continue};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Looks for code that spawns a process but never calls `wait()` on the child.
+    ///
+    /// ### Why is this bad?
+    /// As explained in the [standard library documentation](https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning),
+    /// calling `wait()` is necessary on Unix platforms to properly release all OS resources associated with the process.
+    /// Not doing so will effectively leak process IDs and/or other limited global resources,
+    /// which can eventually lead to resource exhaustion, so it's recommended to call `wait()` in long-running applications.
+    /// Such processes are called "zombie processes".
+    ///
+    /// ### Example
+    /// ```rust
+    /// use std::process::Command;
+    ///
+    /// let _child = Command::new("ls").spawn().expect("failed to execute child");
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// use std::process::Command;
+    ///
+    /// let mut child = Command::new("ls").spawn().expect("failed to execute child");
+    /// child.wait().expect("failed to wait on child");
+    /// ```
+    #[clippy::version = "1.74.0"]
+    pub ZOMBIE_PROCESSES,
+    suspicious,
+    "not waiting on a spawned child process"
+}
+declare_lint_pass!(ZombieProcesses => [ZOMBIE_PROCESSES]);
+
+impl<'tcx> LateLintPass<'tcx> for ZombieProcesses {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+        if let ExprKind::Call(..) | ExprKind::MethodCall(..) = expr.kind
+            && let Some(child_adt) = cx.typeck_results().expr_ty(expr).ty_adt_def()
+            && match_def_path(cx, child_adt.did(), &paths::CHILD)
+        {
+            match cx.tcx.parent_hir_node(expr.hir_id) {
+                Node::LetStmt(local)
+                    if let PatKind::Binding(_, local_id, ..) = local.pat.kind
+                        && let Some(enclosing_block) = get_enclosing_block(cx, expr.hir_id) =>
+                {
+                    let mut vis = WaitFinder::WalkUpTo(cx, local_id);
+
+                    // If it does have a `wait()` call, we're done. Don't lint.
+                    if let Break(BreakReason::WaitFound) = walk_block(&mut vis, enclosing_block) {
+                        return;
+                    }
+
+                    // Don't emit a suggestion since the binding is used later
+                    check(cx, expr, false);
+                },
+                Node::LetStmt(&LetStmt { pat, .. }) if let PatKind::Wild = pat.kind => {
+                    // `let _ = child;`, also dropped immediately without `wait()`ing
+                    check(cx, expr, true);
+                },
+                Node::Stmt(&Stmt {
+                    kind: StmtKind::Semi(_),
+                    ..
+                }) => {
+                    // Immediately dropped. E.g. `std::process::Command::new("echo").spawn().unwrap();`
+                    check(cx, expr, true);
+                },
+                _ => {},
+            }
+        }
+    }
+}
+
+enum BreakReason {
+    WaitFound,
+    EarlyReturn,
+}
+
+/// A visitor responsible for finding a `wait()` call on a local variable.
+///
+/// Conditional `wait()` calls are assumed to not call wait:
+/// ```ignore
+/// let mut c = Command::new("").spawn().unwrap();
+/// if true {
+///     c.wait();
+/// }
+/// ```
+///
+/// Note that this visitor does NOT explicitly look for `wait()` calls directly, but rather does the
+/// inverse -- checking if all uses of the local are either:
+/// - a field access (`child.{stderr,stdin,stdout}`)
+/// - calling `id` or `kill`
+/// - no use at all (e.g. `let _x = child;`)
+/// - taking a shared reference (`&`), `wait()` can't go through that
+///
+/// None of these are sufficient to prevent zombie processes.
+/// Doing it like this means more FNs, but FNs are better than FPs.
+///
+/// `return` expressions, conditional or not, short-circuit the visitor because
+/// if a `wait()` call hadn't been found at that point, it might never reach one at a later point:
+/// ```ignore
+/// let mut c = Command::new("").spawn().unwrap();
+/// if true {
+///     return; // Break(BreakReason::EarlyReturn)
+/// }
+/// c.wait(); // this might not be reachable
+/// ```
+enum WaitFinder<'a, 'tcx> {
+    WalkUpTo(&'a LateContext<'tcx>, HirId),
+    Found(&'a LateContext<'tcx>, HirId),
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for WaitFinder<'a, 'tcx> {
+    type Result = ControlFlow<BreakReason>;
+
+    fn visit_local(&mut self, l: &'tcx LetStmt<'tcx>) -> Self::Result {
+        if let Self::WalkUpTo(cx, local_id) = *self
+            && let PatKind::Binding(_, pat_id, ..) = l.pat.kind
+            && local_id == pat_id
+        {
+            *self = Self::Found(cx, local_id);
+        }
+
+        walk_local(self, l)
+    }
+
+    fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) -> Self::Result {
+        let Self::Found(cx, local_id) = *self else {
+            return walk_expr(self, ex);
+        };
+
+        if path_to_local_id(ex, local_id) {
+            match cx.tcx.parent_hir_node(ex.hir_id) {
+                Node::Stmt(Stmt {
+                    kind: StmtKind::Semi(_),
+                    ..
+                }) => {},
+                Node::Expr(expr) if let ExprKind::Field(..) = expr.kind => {},
+                Node::Expr(expr) if let ExprKind::AddrOf(_, Mutability::Not, _) = expr.kind => {},
+                Node::Expr(expr)
+                    if let Some(fn_did) = fn_def_id(cx, expr)
+                        && match_any_def_paths(cx, fn_did, &[&paths::CHILD_ID, &paths::CHILD_KILL]).is_some() => {},
+
+                // Conservatively assume that all other kinds of nodes call `.wait()` somehow.
+                _ => return Break(BreakReason::WaitFound),
+            }
+        } else {
+            match ex.kind {
+                ExprKind::Ret(..) => return Break(BreakReason::EarlyReturn),
+                ExprKind::If(cond, then, None) => {
+                    walk_expr(self, cond)?;
+
+                    // A `wait()` call in an if expression with no else is not enough:
+                    //
+                    // let c = spawn();
+                    // if true {
+                    //   c.wait();
+                    // }
+                    //
+                    // This might not call wait(). However, early returns are propagated,
+                    // because they might lead to a later wait() not being called.
+                    if let Break(BreakReason::EarlyReturn) = walk_expr(self, then) {
+                        return Break(BreakReason::EarlyReturn);
+                    }
+
+                    return Continue(());
+                },
+
+                ExprKind::If(cond, then, Some(else_)) => {
+                    walk_expr(self, cond)?;
+
+                    #[expect(clippy::unnested_or_patterns)]
+                    match (walk_expr(self, then), walk_expr(self, else_)) {
+                        (Continue(()), Continue(()))
+
+                        // `wait()` in one branch but nothing in the other does not count
+                        | (Continue(()), Break(BreakReason::WaitFound))
+                        | (Break(BreakReason::WaitFound), Continue(())) => {},
+
+                        // `wait()` in both branches is ok
+                        (Break(BreakReason::WaitFound), Break(BreakReason::WaitFound)) => {
+                            return Break(BreakReason::WaitFound);
+                        },
+
+                        // Propagate early returns in either branch
+                        (Break(BreakReason::EarlyReturn), _) | (_, Break(BreakReason::EarlyReturn)) => {
+                            return Break(BreakReason::EarlyReturn);
+                        },
+                    }
+
+                    return Continue(());
+                },
+                _ => {},
+            }
+        }
+
+        walk_expr(self, ex)
+    }
+}
+
+/// This function has shared logic between the different kinds of nodes that can trigger the lint.
+///
+/// In particular, `let <binding> = <expr that spawns child>;` requires some custom additional logic
+/// such as checking that the binding is not used in certain ways, which isn't necessary for
+/// `let _ = <expr that spawns child>;`.
+///
+/// This checks if the program doesn't unconditionally exit after the spawn expression.
+fn check<'tcx>(cx: &LateContext<'tcx>, spawn_expr: &'tcx Expr<'tcx>, emit_suggestion: bool) {
+    let Some(block) = get_enclosing_block(cx, spawn_expr.hir_id) else {
+        return;
+    };
+
+    let mut vis = ExitPointFinder {
+        cx,
+        state: ExitPointState::WalkUpTo(spawn_expr.hir_id),
+    };
+    if let Break(ExitCallFound) = vis.visit_block(block) {
+        // Visitor found an unconditional `exit()` call, so don't lint.
+        return;
+    }
+
+    span_lint_and_then(
+        cx,
+        ZOMBIE_PROCESSES,
+        spawn_expr.span,
+        "spawned process is never `wait()`ed on",
+        |diag| {
+            if emit_suggestion {
+                diag.span_suggestion(
+                    spawn_expr.span.shrink_to_hi(),
+                    "try",
+                    ".wait()",
+                    Applicability::MaybeIncorrect,
+                );
+            } else {
+                diag.note("consider calling `.wait()`");
+            }
+
+            diag.note("not doing so might leave behind zombie processes")
+                .note("see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning");
+        },
+    );
+}
+
+/// Checks if the given expression exits the process.
+fn is_exit_expression(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    fn_def_id(cx, expr).is_some_and(|fn_did| {
+        cx.tcx.is_diagnostic_item(sym::process_exit, fn_did) || match_def_path(cx, fn_did, &paths::ABORT)
+    })
+}
+
+#[derive(Debug)]
+enum ExitPointState {
+    /// Still walking up to the expression that initiated the visitor.
+    WalkUpTo(HirId),
+    /// We're inside of a control flow construct (e.g. `if`, `match`, `loop`)
+    /// Within this, we shouldn't accept any `exit()` calls in here, but we can leave all of these
+    /// constructs later and still continue looking for an `exit()` call afterwards. Example:
+    /// ```ignore
+    /// Command::new("").spawn().unwrap();
+    ///
+    /// if true {                // depth=1
+    ///     if true {            // depth=2
+    ///         match () {       // depth=3
+    ///             () => loop { // depth=4
+    ///
+    ///                 std::process::exit();
+    ///                 ^^^^^^^^^^^^^^^^^^^^^ conditional exit call, ignored
+    ///
+    ///             }           // depth=3
+    ///         }               // depth=2
+    ///     }                   // depth=1
+    /// }                       // depth=0
+    ///
+    /// std::process::exit();
+    /// ^^^^^^^^^^^^^^^^^^^^^ this exit call is accepted because we're now unconditionally calling it
+    /// ```
+    /// We can only get into this state from `NoExit`.
+    InControlFlow { depth: u32 },
+    /// No exit call found yet, but looking for one.
+    NoExit,
+}
+
+fn expr_enters_control_flow(expr: &Expr<'_>) -> bool {
+    matches!(expr.kind, ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Loop(..))
+}
+
+struct ExitPointFinder<'a, 'tcx> {
+    state: ExitPointState,
+    cx: &'a LateContext<'tcx>,
+}
+
+struct ExitCallFound;
+
+impl<'a, 'tcx> Visitor<'tcx> for ExitPointFinder<'a, 'tcx> {
+    type Result = ControlFlow<ExitCallFound>;
+
+    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) -> Self::Result {
+        match self.state {
+            ExitPointState::WalkUpTo(id) if expr.hir_id == id => {
+                self.state = ExitPointState::NoExit;
+                walk_expr(self, expr)
+            },
+            ExitPointState::NoExit if expr_enters_control_flow(expr) => {
+                self.state = ExitPointState::InControlFlow { depth: 1 };
+                walk_expr(self, expr)?;
+                if let ExitPointState::InControlFlow { .. } = self.state {
+                    self.state = ExitPointState::NoExit;
+                }
+                Continue(())
+            },
+            ExitPointState::NoExit if is_exit_expression(self.cx, expr) => Break(ExitCallFound),
+            ExitPointState::InControlFlow { ref mut depth } if expr_enters_control_flow(expr) => {
+                *depth += 1;
+                walk_expr(self, expr)?;
+                match self.state {
+                    ExitPointState::InControlFlow { depth: 1 } => self.state = ExitPointState::NoExit,
+                    ExitPointState::InControlFlow { ref mut depth } => *depth -= 1,
+                    _ => {},
+                }
+                Continue(())
+            },
+            _ => Continue(()),
+        }
+    }
+}