about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPhilipp Krones <hello@philkrones.com>2024-09-05 17:00:37 +0200
committerPhilipp Krones <hello@philkrones.com>2024-09-05 17:00:37 +0200
commit87ce1d80693fecbf0638d3acb8aab4d31099e760 (patch)
tree0495a6bfe77495f5712cea4694133614998b2506
parent663f20086a4b48dcd6d00b3be070bdd1c9c688b2 (diff)
parentc95c767663896297c7e818d678e4ba686d55fe64 (diff)
downloadrust-87ce1d80693fecbf0638d3acb8aab4d31099e760.tar.gz
rust-87ce1d80693fecbf0638d3acb8aab4d31099e760.zip
Merge remote-tracking branch 'upstream/master' into rustup
-rw-r--r--.github/workflows/clippy_bors.yml4
-rw-r--r--CHANGELOG.md60
-rw-r--r--clippy_config/src/msrvs.rs2
-rw-r--r--clippy_dev/src/new_lint.rs4
-rw-r--r--clippy_dev/src/serve.rs3
-rw-r--r--clippy_lints/src/attrs/allow_attributes_without_reason.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/byte_char_slices.rs2
-rw-r--r--clippy_lints/src/cfg_not_test.rs2
-rw-r--r--clippy_lints/src/declared_lints.rs8
-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.rs85
-rw-r--r--clippy_lints/src/doc/suspicious_doc_comments.rs6
-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/implicit_saturating_sub.rs359
-rw-r--r--clippy_lints/src/lib.rs6
-rw-r--r--clippy_lints/src/manual_range_patterns.rs5
-rw-r--r--clippy_lints/src/methods/iter_kv_map.rs1
-rw-r--r--clippy_lints/src/methods/mod.rs17
-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/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.rs15
-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/set_contains_or_insert.rs2
-rw-r--r--clippy_lints/src/string_patterns.rs2
-rw-r--r--clippy_lints/src/zombie_processes.rs334
-rw-r--r--clippy_utils/src/attrs.rs8
-rw-r--r--clippy_utils/src/hir_utils.rs4
-rw-r--r--clippy_utils/src/lib.rs15
-rw-r--r--clippy_utils/src/paths.rs5
-rw-r--r--clippy_utils/src/source.rs60
-rw-r--r--clippy_utils/src/sugg.rs38
-rw-r--r--src/driver.rs2
-rw-r--r--tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr4
-rw-r--r--tests/ui-toml/disallowed_names_append/disallowed_names.rs2
-rw-r--r--tests/ui-toml/disallowed_names_replace/disallowed_names.rs2
-rw-r--r--tests/ui-toml/panic/panic.rs5
-rw-r--r--tests/ui-toml/panic/panic.stderr10
-rw-r--r--tests/ui/allow_attributes_without_reason.rs1
-rw-r--r--tests/ui/allow_attributes_without_reason.stderr4
-rw-r--r--tests/ui/auxiliary/external_item.rs7
-rw-r--r--tests/ui/cast_alignment.rs8
-rw-r--r--tests/ui/cmp_owned/without_suggestion.rs4
-rw-r--r--tests/ui/collapsible_else_if.fixed5
-rw-r--r--tests/ui/collapsible_else_if.rs5
-rw-r--r--tests/ui/collapsible_else_if.stderr16
-rw-r--r--tests/ui/crashes/associated-constant-ice.rs2
-rw-r--r--tests/ui/crashes/cc_seme.rs4
-rw-r--r--tests/ui/crashes/ice-11230.rs2
-rw-r--r--tests/ui/crashes/ice-1588.rs2
-rw-r--r--tests/ui/crashes/ice-1969.rs2
-rw-r--r--tests/ui/crashes/ice-2499.rs6
-rw-r--r--tests/ui/crashes/ice-2594.rs1
-rw-r--r--tests/ui/crashes/ice-2727.rs2
-rw-r--r--tests/ui/crashes/ice-2760.rs8
-rw-r--r--tests/ui/crashes/ice-2862.rs2
-rw-r--r--tests/ui/crashes/ice-2865.rs2
-rw-r--r--tests/ui/crashes/ice-3151.rs2
-rw-r--r--tests/ui/crashes/ice-3462.rs2
-rw-r--r--tests/ui/crashes/ice-3747.rs2
-rw-r--r--tests/ui/crashes/ice-700.rs2
-rw-r--r--tests/ui/crashes/ice_exact_size.rs2
-rw-r--r--tests/ui/crashes/if_same_then_else.rs2
-rw-r--r--tests/ui/crashes/inherent_impl.rs2
-rw-r--r--tests/ui/crashes/issue-825.rs2
-rw-r--r--tests/ui/crashes/match_same_arms_const.rs2
-rw-r--r--tests/ui/crashes/returns.rs2
-rw-r--r--tests/ui/derive.rs1
-rw-r--r--tests/ui/derive.stderr20
-rw-r--r--tests/ui/diverging_sub_expression.rs6
-rw-r--r--tests/ui/doc/doc_lazy_blank_line.fixed47
-rw-r--r--tests/ui/doc/doc_lazy_blank_line.rs43
-rw-r--r--tests/ui/doc/doc_lazy_blank_line.stderr56
-rw-r--r--tests/ui/doc/doc_lazy_list.fixed9
-rw-r--r--tests/ui/doc/doc_lazy_list.stderr27
-rw-r--r--tests/ui/duplicate_underscore_argument.rs1
-rw-r--r--tests/ui/duplicate_underscore_argument.stderr2
-rw-r--r--tests/ui/empty_line_after/doc_comments.1.fixed135
-rw-r--r--tests/ui/empty_line_after/doc_comments.2.fixed144
-rw-r--r--tests/ui/empty_line_after/doc_comments.rs147
-rw-r--r--tests/ui/empty_line_after/doc_comments.stderr176
-rw-r--r--tests/ui/empty_line_after/outer_attribute.1.fixed103
-rw-r--r--tests/ui/empty_line_after/outer_attribute.2.fixed106
-rw-r--r--tests/ui/empty_line_after/outer_attribute.rs112
-rw-r--r--tests/ui/empty_line_after/outer_attribute.stderr103
-rw-r--r--tests/ui/empty_line_after_doc_comments.rs132
-rw-r--r--tests/ui/empty_line_after_doc_comments.stderr37
-rw-r--r--tests/ui/empty_line_after_outer_attribute.rs120
-rw-r--r--tests/ui/empty_line_after_outer_attribute.stderr54
-rw-r--r--tests/ui/exit1.rs2
-rw-r--r--tests/ui/exit2.rs2
-rw-r--r--tests/ui/exit3.rs2
-rw-r--r--tests/ui/expect_fun_call.fixed2
-rw-r--r--tests/ui/expect_fun_call.rs2
-rw-r--r--tests/ui/expect_fun_call.stderr30
-rw-r--r--tests/ui/implicit_saturating_sub.fixed13
-rw-r--r--tests/ui/implicit_saturating_sub.rs13
-rw-r--r--tests/ui/manual_arithmetic_check-2.rs35
-rw-r--r--tests/ui/manual_arithmetic_check-2.stderr75
-rw-r--r--tests/ui/manual_arithmetic_check.fixed24
-rw-r--r--tests/ui/manual_arithmetic_check.rs24
-rw-r--r--tests/ui/manual_arithmetic_check.stderr29
-rw-r--r--tests/ui/manual_range_patterns.fixed8
-rw-r--r--tests/ui/manual_range_patterns.rs8
-rw-r--r--tests/ui/match_overlapping_arm.rs2
-rw-r--r--tests/ui/match_overlapping_arm.stderr32
-rw-r--r--tests/ui/needless_doc_main.rs6
-rw-r--r--tests/ui/needless_pass_by_value.rs12
-rw-r--r--tests/ui/needless_pass_by_value.stderr10
-rw-r--r--tests/ui/pointers_in_nomem_asm_block.rs33
-rw-r--r--tests/ui/pointers_in_nomem_asm_block.stderr34
-rw-r--r--tests/ui/string_slice.rs6
-rw-r--r--tests/ui/suspicious_command_arg_space.fixed1
-rw-r--r--tests/ui/suspicious_command_arg_space.rs1
-rw-r--r--tests/ui/suspicious_command_arg_space.stderr4
-rw-r--r--tests/ui/tabs_in_doc_comments.fixed1
-rw-r--r--tests/ui/tabs_in_doc_comments.rs1
-rw-r--r--tests/ui/tabs_in_doc_comments.stderr16
-rw-r--r--tests/ui/temporary_assignment.rs1
-rw-r--r--tests/ui/temporary_assignment.stderr8
-rw-r--r--tests/ui/to_string_in_format_args_incremental.fixed8
-rw-r--r--tests/ui/to_string_in_format_args_incremental.rs8
-rw-r--r--tests/ui/to_string_in_format_args_incremental.stderr11
-rw-r--r--tests/ui/unnecessary_min_or_max.fixed29
-rw-r--r--tests/ui/unnecessary_min_or_max.rs29
-rw-r--r--tests/ui/used_underscore_binding.stderr24
-rw-r--r--tests/ui/used_underscore_items.rs63
-rw-r--r--tests/ui/used_underscore_items.stderr112
-rw-r--r--tests/ui/zombie_processes.rs138
-rw-r--r--tests/ui/zombie_processes.stderr64
-rw-r--r--tests/ui/zombie_processes_fixable.fixed26
-rw-r--r--tests/ui/zombie_processes_fixable.rs26
-rw-r--r--tests/ui/zombie_processes_fixable.stderr40
141 files changed, 3550 insertions, 1141 deletions
diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml
index 2aa13313fa5..026771e6fcf 100644
--- a/.github/workflows/clippy_bors.yml
+++ b/.github/workflows/clippy_bors.yml
@@ -162,7 +162,7 @@ jobs:
         find $DIR ! -executable -o -type d ! -path $DIR | xargs rm -rf
 
     - name: Upload Binaries
-      uses: actions/upload-artifact@v3
+      uses: actions/upload-artifact@v4
       with:
         name: binaries
         path: target/debug
@@ -202,7 +202,7 @@ jobs:
 
     # Download
     - name: Download target dir
-      uses: actions/download-artifact@v3
+      uses: actions/download-artifact@v4
       with:
         name: binaries
         path: target/debug
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9bc4ad9698d..31fc74192ab 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,7 +6,61 @@ document.
 
 ## Unreleased / Beta / In Rust Nightly
 
-[c9139bd5...master](https://github.com/rust-lang/rust-clippy/compare/c9139bd5...master)
+[b794b8e0...master](https://github.com/rust-lang/rust-clippy/compare/b794b8e0...master)
+
+## Rust 1.81
+
+Current stable, released 2024-09-05
+
+### New Lints
+
+* Added [`cfg_not_test`] to `restriction`
+  [#11293](https://github.com/rust-lang/rust-clippy/pull/11293)
+* Added [`byte_char_slices`] to `style`
+  [#10155](https://github.com/rust-lang/rust-clippy/pull/10155)
+* Added [`set_contains_or_insert`] to `nursery`
+  [#12873](https://github.com/rust-lang/rust-clippy/pull/12873)
+* Added [`manual_rotate`] to `style`
+  [#12983](https://github.com/rust-lang/rust-clippy/pull/12983)
+* Added [`unnecessary_min_or_max`] to `complexity`
+  [#12368](https://github.com/rust-lang/rust-clippy/pull/12368)
+* Added [`manual_inspect`] to `complexity`
+  [#12287](https://github.com/rust-lang/rust-clippy/pull/12287)
+* Added [`field_scoped_visibility_modifiers`] to `restriction`
+  [#12893](https://github.com/rust-lang/rust-clippy/pull/12893)
+* Added [`manual_pattern_char_comparison`] to `style`
+  [#12849](https://github.com/rust-lang/rust-clippy/pull/12849)
+* Added [`needless_maybe_sized`] to `suspicious`
+  [#10632](https://github.com/rust-lang/rust-clippy/pull/10632)
+* Added [`needless_character_iteration`] to `suspicious`
+  [#12815](https://github.com/rust-lang/rust-clippy/pull/12815)
+
+### Moves and Deprecations
+
+* [`allow_attributes`], [`allow_attributes_without_reason`]: Now work on stable
+  [rust#120924](https://github.com/rust-lang/rust/pull/120924)
+* Renamed `overflow_check_conditional` to [`panicking_overflow_checks`]
+  [#12944](https://github.com/rust-lang/rust-clippy/pull/12944)
+* Moved [`panicking_overflow_checks`] to `correctness` (From `complexity` now deny-by-default)
+  [#12944](https://github.com/rust-lang/rust-clippy/pull/12944)
+* Renamed `thread_local_initializer_can_be_made_const` to [`missing_const_for_thread_local`]
+  [#12974](https://github.com/rust-lang/rust-clippy/pull/12974)
+* Deprecated [`maybe_misused_cfg`] and [`mismatched_target_os`] as they are now caught by cargo
+  and rustc
+  [#12875](https://github.com/rust-lang/rust-clippy/pull/12875)
+
+### Enhancements
+
+* [`significant_drop_in_scrutinee`]: Now also checks scrutinies of `while let` and `for let`
+  expressions
+  [#12870](https://github.com/rust-lang/rust-clippy/pull/12870)
+* [`std_instead_of_core`]: Now respects the `msrv` configuration
+  [#13168](https://github.com/rust-lang/rust-clippy/pull/13168)
+
+### ICE Fixes
+
+* [`suboptimal_flops`]: No longer crashes on custom `.log()` functions
+  [#12884](https://github.com/rust-lang/rust-clippy/pull/12884)
 
 ## Rust 1.80
 
@@ -5500,6 +5554,7 @@ Released 2018-09-13
 [`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
 [`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
 [`invalid_utf8_in_unchecked`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_utf8_in_unchecked
+[`inverted_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#inverted_saturating_sub
 [`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
 [`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix
 [`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
@@ -5757,6 +5812,7 @@ Released 2018-09-13
 [`pathbuf_init_then_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#pathbuf_init_then_push
 [`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
 [`permissions_set_readonly_false`]: https://rust-lang.github.io/rust-clippy/master/index.html#permissions_set_readonly_false
+[`pointers_in_nomem_asm_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#pointers_in_nomem_asm_block
 [`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters
 [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
 [`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
@@ -6013,6 +6069,7 @@ Released 2018-09-13
 [`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug
 [`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self
 [`used_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_binding
+[`used_underscore_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_items
 [`useless_asref`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_asref
 [`useless_attribute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_attribute
 [`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion
@@ -6047,6 +6104,7 @@ Released 2018-09-13
 [`zero_repeat_side_effects`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_repeat_side_effects
 [`zero_sized_map_values`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_sized_map_values
 [`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space
+[`zombie_processes`]: https://rust-lang.github.io/rust-clippy/master/index.html#zombie_processes
 [`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset
 <!-- end autogenerated links to lint list -->
 <!-- begin autogenerated links to configuration documentation -->
diff --git a/clippy_config/src/msrvs.rs b/clippy_config/src/msrvs.rs
index 0e8215aa8e3..0c673ba8046 100644
--- a/clippy_config/src/msrvs.rs
+++ b/clippy_config/src/msrvs.rs
@@ -36,7 +36,7 @@ msrv_aliases! {
     1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST }
     1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS }
     1,50,0 { BOOL_THEN, CLAMP }
-    1,47,0 { TAU, IS_ASCII_DIGIT_CONST, ARRAY_IMPL_ANY_LEN }
+    1,47,0 { TAU, IS_ASCII_DIGIT_CONST, ARRAY_IMPL_ANY_LEN, SATURATING_SUB_CONST }
     1,46,0 { CONST_IF_MATCH }
     1,45,0 { STR_STRIP_PREFIX }
     1,43,0 { LOG2_10, LOG10_2, NUMERIC_ASSOCIATED_CONSTANTS }
diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs
index 87117832fb9..de91233d196 100644
--- a/clippy_dev/src/new_lint.rs
+++ b/clippy_dev/src/new_lint.rs
@@ -470,7 +470,7 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str>
     });
 
     // Find both the last lint declaration (declare_clippy_lint!) and the lint pass impl
-    while let Some(LintDeclSearchResult { content, .. }) = iter.find(|result| result.token == TokenKind::Ident) {
+    while let Some(LintDeclSearchResult { content, .. }) = iter.find(|result| result.token_kind == TokenKind::Ident) {
         let mut iter = iter
             .by_ref()
             .filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
@@ -480,7 +480,7 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str>
                 // matches `!{`
                 match_tokens!(iter, Bang OpenBrace);
                 if let Some(LintDeclSearchResult { range, .. }) =
-                    iter.find(|result| result.token == TokenKind::CloseBrace)
+                    iter.find(|result| result.token_kind == TokenKind::CloseBrace)
                 {
                     last_decl_curly_offset = Some(range.end);
                 }
diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs
index 19560b31fd3..cc14cd8dae6 100644
--- a/clippy_dev/src/serve.rs
+++ b/clippy_dev/src/serve.rs
@@ -29,7 +29,7 @@ pub fn run(port: u16, lint: Option<String>) -> ! {
         }
         if let Some(url) = url.take() {
             thread::spawn(move || {
-                Command::new(PYTHON)
+                let mut child = Command::new(PYTHON)
                     .arg("-m")
                     .arg("http.server")
                     .arg(port.to_string())
@@ -40,6 +40,7 @@ pub fn run(port: u16, lint: Option<String>) -> ! {
                 thread::sleep(Duration::from_millis(500));
                 // Launch browser after first export.py has completed and http.server is up
                 let _result = opener::open(url);
+                child.wait().unwrap();
             });
         }
         thread::sleep(Duration::from_millis(1000));
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/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/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/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..e478ab330e8 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,
@@ -486,6 +487,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,
@@ -598,6 +600,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,
@@ -767,4 +770,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..6db63b59e02 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;
@@ -455,7 +456,82 @@ declare_clippy_lint! {
     "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/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/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..58ad9f645d6 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -287,6 +287,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;
@@ -386,6 +387,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 +625,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));
@@ -933,5 +935,7 @@ 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));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
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/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 d7126990edb..9b7cba9dafb 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -441,6 +441,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 +3975,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 +4036,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 +4110,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"
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/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..5bf390056f6 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;
                 }
             }
 
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/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/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(()),
+        }
+    }
+}
diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs
index 42c8b218d14..935a1686c0a 100644
--- a/clippy_utils/src/attrs.rs
+++ b/clippy_utils/src/attrs.rs
@@ -183,15 +183,15 @@ pub fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool {
         let mut iter = tokenize_with_text(src);
 
         // Search for the token sequence [`#`, `[`, `cfg`]
-        while iter.any(|(t, _)| matches!(t, TokenKind::Pound)) {
-            let mut iter = iter.by_ref().skip_while(|(t, _)| {
+        while iter.any(|(t, ..)| matches!(t, TokenKind::Pound)) {
+            let mut iter = iter.by_ref().skip_while(|(t, ..)| {
                 matches!(
                     t,
                     TokenKind::Whitespace | TokenKind::LineComment { .. } | TokenKind::BlockComment { .. }
                 )
             });
-            if matches!(iter.next(), Some((TokenKind::OpenBracket, _)))
-                && matches!(iter.next(), Some((TokenKind::Ident, "cfg")))
+            if matches!(iter.next(), Some((TokenKind::OpenBracket, ..)))
+                && matches!(iter.next(), Some((TokenKind::Ident, "cfg", _)))
             {
                 return true;
             }
diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs
index f61ef9ac1b0..c1e21ec4e39 100644
--- a/clippy_utils/src/hir_utils.rs
+++ b/clippy_utils/src/hir_utils.rs
@@ -1194,8 +1194,8 @@ fn eq_span_tokens(
             && let Some(rsrc) = right.get_source_range(cx)
             && let Some(rsrc) = rsrc.as_str()
         {
-            let pred = |t: &(_, _)| pred(t.0);
-            let map = |(_, x)| x;
+            let pred = |&(token, ..): &(TokenKind, _, _)| pred(token);
+            let map = |(_, source, _)| source;
 
             let ltok = tokenize_with_text(lsrc).filter(pred).map(map);
             let rtok = tokenize_with_text(rsrc).filter(pred).map(map);
diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs
index 5db14872c36..489481baf5f 100644
--- a/clippy_utils/src/lib.rs
+++ b/clippy_utils/src/lib.rs
@@ -121,7 +121,7 @@ use rustc_middle::ty::{
 use rustc_span::hygiene::{ExpnKind, MacroKind};
 use rustc_span::source_map::SourceMap;
 use rustc_span::symbol::{kw, Ident, Symbol};
-use rustc_span::{sym, Span};
+use rustc_span::{sym, InnerSpan, Span};
 use rustc_target::abi::Integer;
 use visitors::Visitable;
 
@@ -2950,13 +2950,14 @@ pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> ExprU
 }
 
 /// Tokenizes the input while keeping the text associated with each token.
-pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str)> {
+pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str, InnerSpan)> {
     let mut pos = 0;
     tokenize(s).map(move |t| {
         let end = pos + t.len;
         let range = pos as usize..end as usize;
+        let inner = InnerSpan::new(range.start, range.end);
         pos = end;
-        (t.kind, s.get(range).unwrap_or_default())
+        (t.kind, s.get(range).unwrap_or_default(), inner)
     })
 }
 
@@ -2980,8 +2981,8 @@ pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
 pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String {
     let snippet = sm.span_to_snippet(span).unwrap_or_default();
     let res = tokenize_with_text(&snippet)
-        .filter(|(t, _)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
-        .map(|(_, s)| s)
+        .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
+        .map(|(_, s, _)| s)
         .join("\n");
     res
 }
@@ -3001,7 +3002,7 @@ pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
 /// pat: Some(a)
 /// else_body: return None
 /// ```
-
+///
 /// And for this example:
 /// ```ignore
 /// let Some(FooBar { a, b }) = ex else { return None };
@@ -3011,7 +3012,7 @@ pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
 /// pat: Some(FooBar { a, b })
 /// else_body: return None
 /// ```
-
+///
 /// We output `Some(a)` in the first instance, and `Some(FooBar { a, b })` in the second, because
 /// the question mark operator is applicable here. Callers have to check whether we are in a
 /// constant or not.
diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs
index 684c645c199..930fb9fb6f2 100644
--- a/clippy_utils/src/paths.rs
+++ b/clippy_utils/src/paths.rs
@@ -4,6 +4,7 @@
 //! Whenever possible, please consider diagnostic items over hardcoded paths.
 //! See <https://github.com/rust-lang/rust-clippy/issues/5393> for more information.
 
+pub const ABORT: [&str; 3] = ["std", "process", "abort"];
 pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"];
 pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [
     ["rustc_lint_defs", "Applicability", "Unspecified"],
@@ -23,6 +24,9 @@ pub const CORE_RESULT_OK_METHOD: [&str; 4] = ["core", "result", "Result", "ok"];
 pub const CSTRING_AS_C_STR: [&str; 5] = ["alloc", "ffi", "c_str", "CString", "as_c_str"];
 pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"];
 pub const EARLY_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "EarlyLintPass"];
+pub const CHILD: [&str; 3] = ["std", "process", "Child"];
+pub const CHILD_ID: [&str; 4] = ["std", "process", "Child", "id"];
+pub const CHILD_KILL: [&str; 4] = ["std", "process", "Child", "kill"];
 pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"];
 pub const F64_EPSILON: [&str; 4] = ["core", "f64", "<impl f64>", "EPSILON"];
 pub const FILE_OPTIONS: [&str; 4] = ["std", "fs", "File", "options"];
@@ -56,6 +60,7 @@ pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to
 pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard"];
 pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockReadGuard"];
 pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockWriteGuard"];
+pub const PANIC_ANY: [&str; 3] = ["std", "panic", "panic_any"];
 pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"];
 pub const PATH_MAIN_SEPARATOR: [&str; 3] = ["std", "path", "MAIN_SEPARATOR"];
 pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs
index 482e1e0147b..f97fb4a6471 100644
--- a/clippy_utils/src/source.rs
+++ b/clippy_utils/src/source.rs
@@ -666,39 +666,6 @@ pub fn walk_span_to_context(span: Span, outer: SyntaxContext) -> Option<Span> {
     (outer_span.ctxt() == outer).then_some(outer_span)
 }
 
-/// Removes block comments from the given `Vec` of lines.
-///
-/// # Examples
-///
-/// ```rust,ignore
-/// without_block_comments(vec!["/*", "foo", "*/"]);
-/// // => vec![]
-///
-/// without_block_comments(vec!["bar", "/*", "foo", "*/"]);
-/// // => vec!["bar"]
-/// ```
-pub fn without_block_comments(lines: Vec<&str>) -> Vec<&str> {
-    let mut without = vec![];
-
-    let mut nest_level = 0;
-
-    for line in lines {
-        if line.contains("/*") {
-            nest_level += 1;
-            continue;
-        } else if line.contains("*/") {
-            nest_level -= 1;
-            continue;
-        }
-
-        if nest_level == 0 {
-            without.push(line);
-        }
-    }
-
-    without
-}
-
 /// Trims the whitespace from the start and the end of the span.
 pub fn trim_span(sm: &SourceMap, span: Span) -> Span {
     let data = span.data();
@@ -776,7 +743,7 @@ pub fn str_literal_to_char_literal(
 
 #[cfg(test)]
 mod test {
-    use super::{reindent_multiline, without_block_comments};
+    use super::reindent_multiline;
 
     #[test]
     fn test_reindent_multiline_single_line() {
@@ -844,29 +811,4 @@ mod test {
         z
     }".into(), true, Some(8)));
     }
-
-    #[test]
-    fn test_without_block_comments_lines_without_block_comments() {
-        let result = without_block_comments(vec!["/*", "", "*/"]);
-        println!("result: {result:?}");
-        assert!(result.is_empty());
-
-        let result = without_block_comments(vec!["", "/*", "", "*/", "#[crate_type = \"lib\"]", "/*", "", "*/", ""]);
-        assert_eq!(result, vec!["", "#[crate_type = \"lib\"]", ""]);
-
-        let result = without_block_comments(vec!["/* rust", "", "*/"]);
-        assert!(result.is_empty());
-
-        let result = without_block_comments(vec!["/* one-line comment */"]);
-        assert!(result.is_empty());
-
-        let result = without_block_comments(vec!["/* nested", "/* multi-line", "comment", "*/", "test", "*/"]);
-        assert!(result.is_empty());
-
-        let result = without_block_comments(vec!["/* nested /* inline /* comment */ test */ */"]);
-        assert!(result.is_empty());
-
-        let result = without_block_comments(vec!["foo", "bar", "baz"]);
-        assert_eq!(result, vec!["foo", "bar", "baz"]);
-    }
 }
diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs
index 0f86d89c980..3255c51d009 100644
--- a/clippy_utils/src/sugg.rs
+++ b/clippy_utils/src/sugg.rs
@@ -180,8 +180,10 @@ impl<'a> Sugg<'a> {
     ) -> Self {
         use rustc_ast::ast::RangeLimits;
 
+        let mut snippet = |span: Span| snippet_with_context(cx, span, ctxt, default, app).0;
+
         match expr.kind {
-            _ if expr.span.ctxt() != ctxt => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0),
+            _ if expr.span.ctxt() != ctxt => Sugg::NonParen(snippet(expr.span)),
             ast::ExprKind::AddrOf(..)
             | ast::ExprKind::Closure { .. }
             | ast::ExprKind::If(..)
@@ -224,46 +226,38 @@ impl<'a> Sugg<'a> {
             | ast::ExprKind::While(..)
             | ast::ExprKind::Await(..)
             | ast::ExprKind::Err(_)
-            | ast::ExprKind::Dummy => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0),
+            | ast::ExprKind::Dummy => Sugg::NonParen(snippet(expr.span)),
             ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp(
                 AssocOp::DotDot,
-                lhs.as_ref().map_or("".into(), |lhs| {
-                    snippet_with_context(cx, lhs.span, ctxt, default, app).0
-                }),
-                rhs.as_ref().map_or("".into(), |rhs| {
-                    snippet_with_context(cx, rhs.span, ctxt, default, app).0
-                }),
+                lhs.as_ref().map_or("".into(), |lhs| snippet(lhs.span)),
+                rhs.as_ref().map_or("".into(), |rhs| snippet(rhs.span)),
             ),
             ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::Closed) => Sugg::BinOp(
                 AssocOp::DotDotEq,
-                lhs.as_ref().map_or("".into(), |lhs| {
-                    snippet_with_context(cx, lhs.span, ctxt, default, app).0
-                }),
-                rhs.as_ref().map_or("".into(), |rhs| {
-                    snippet_with_context(cx, rhs.span, ctxt, default, app).0
-                }),
+                lhs.as_ref().map_or("".into(), |lhs| snippet(lhs.span)),
+                rhs.as_ref().map_or("".into(), |rhs| snippet(rhs.span)),
             ),
             ast::ExprKind::Assign(ref lhs, ref rhs, _) => Sugg::BinOp(
                 AssocOp::Assign,
-                snippet_with_context(cx, lhs.span, ctxt, default, app).0,
-                snippet_with_context(cx, rhs.span, ctxt, default, app).0,
+                snippet(lhs.span),
+                snippet(rhs.span),
             ),
             ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => Sugg::BinOp(
                 astbinop2assignop(op),
-                snippet_with_context(cx, lhs.span, ctxt, default, app).0,
-                snippet_with_context(cx, rhs.span, ctxt, default, app).0,
+                snippet(lhs.span),
+                snippet(rhs.span),
             ),
             ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp(
                 AssocOp::from_ast_binop(op.node),
-                snippet_with_context(cx, lhs.span, ctxt, default, app).0,
-                snippet_with_context(cx, rhs.span, ctxt, default, app).0,
+                snippet(lhs.span),
+                snippet(rhs.span),
             ),
             ast::ExprKind::Cast(ref lhs, ref ty) |
             //FIXME(chenyukang), remove this after type ascription is removed from AST
             ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp(
                 AssocOp::As,
-                snippet_with_context(cx, lhs.span, ctxt, default, app).0,
-                snippet_with_context(cx, ty.span, ctxt, default, app).0,
+                snippet(lhs.span),
+                snippet(ty.span),
             ),
         }
     }
diff --git a/src/driver.rs b/src/driver.rs
index 0ac3f35b446..414957938a4 100644
--- a/src/driver.rs
+++ b/src/driver.rs
@@ -268,8 +268,6 @@ pub fn main() {
                 },
                 _ => Some(s.to_string()),
             })
-            // FIXME: remove this line in 1.79 to only keep `--cfg clippy`.
-            .chain(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()])
             .chain(vec!["--cfg".into(), "clippy".into()])
             .collect::<Vec<String>>();
 
diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr
index 9d7c00088fe..e4575d99d03 100644
--- a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr
+++ b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr
@@ -19,8 +19,8 @@ LL |     const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]
 error: hardcoded path to a diagnostic item
   --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:12:43
    |
-LL |     const OPS_MOD: [&str; 5] = ["core", "ops"];
-   |                                ^^^^^^^^^^^^^^^
+LL |     const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
+   |                                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: convert all references to use `sym::deref_method`
 
diff --git a/tests/ui-toml/disallowed_names_append/disallowed_names.rs b/tests/ui-toml/disallowed_names_append/disallowed_names.rs
index a2e2b46c426..61ae8de8e33 100644
--- a/tests/ui-toml/disallowed_names_append/disallowed_names.rs
+++ b/tests/ui-toml/disallowed_names_append/disallowed_names.rs
@@ -1,4 +1,4 @@
-#[warn(clippy::disallowed_names)]
+#![warn(clippy::disallowed_names)]
 
 fn main() {
     // `foo` is part of the default configuration
diff --git a/tests/ui-toml/disallowed_names_replace/disallowed_names.rs b/tests/ui-toml/disallowed_names_replace/disallowed_names.rs
index a2e2b46c426..61ae8de8e33 100644
--- a/tests/ui-toml/disallowed_names_replace/disallowed_names.rs
+++ b/tests/ui-toml/disallowed_names_replace/disallowed_names.rs
@@ -1,4 +1,4 @@
-#[warn(clippy::disallowed_names)]
+#![warn(clippy::disallowed_names)]
 
 fn main() {
     // `foo` is part of the default configuration
diff --git a/tests/ui-toml/panic/panic.rs b/tests/ui-toml/panic/panic.rs
index 618a37ddfc5..b6264c950e4 100644
--- a/tests/ui-toml/panic/panic.rs
+++ b/tests/ui-toml/panic/panic.rs
@@ -1,5 +1,6 @@
 //@compile-flags: --test
 #![warn(clippy::panic)]
+use std::panic::panic_any;
 
 fn main() {
     enum Enam {
@@ -12,6 +13,10 @@ fn main() {
     }
 }
 
+fn issue_13292() {
+    panic_any("should lint")
+}
+
 #[test]
 fn lonely_test() {
     enum Enam {
diff --git a/tests/ui-toml/panic/panic.stderr b/tests/ui-toml/panic/panic.stderr
index bf7503e086c..a034207d919 100644
--- a/tests/ui-toml/panic/panic.stderr
+++ b/tests/ui-toml/panic/panic.stderr
@@ -1,5 +1,5 @@
 error: `panic` should not be present in production code
-  --> tests/ui-toml/panic/panic.rs:11:14
+  --> tests/ui-toml/panic/panic.rs:12:14
    |
 LL |         _ => panic!(""),
    |              ^^^^^^^^^^
@@ -7,5 +7,11 @@ LL |         _ => panic!(""),
    = note: `-D clippy::panic` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::panic)]`
 
-error: aborting due to 1 previous error
+error: `panic_any` should not be present in production code
+  --> tests/ui-toml/panic/panic.rs:17:5
+   |
+LL |     panic_any("should lint")
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
 
diff --git a/tests/ui/allow_attributes_without_reason.rs b/tests/ui/allow_attributes_without_reason.rs
index 86f6b2c5742..334e7ddd9d2 100644
--- a/tests/ui/allow_attributes_without_reason.rs
+++ b/tests/ui/allow_attributes_without_reason.rs
@@ -15,7 +15,6 @@ use proc_macros::{external, with_span};
 #[warn(deref_nullptr)]
 #[deny(deref_nullptr)]
 #[forbid(deref_nullptr)]
-
 fn main() {
     external! {
         #[allow(dead_code)]
diff --git a/tests/ui/allow_attributes_without_reason.stderr b/tests/ui/allow_attributes_without_reason.stderr
index 9bc3ca0f2af..86d7845df04 100644
--- a/tests/ui/allow_attributes_without_reason.stderr
+++ b/tests/ui/allow_attributes_without_reason.stderr
@@ -36,7 +36,7 @@ LL | #[expect(dead_code)]
    = help: try adding a reason at the end with `, reason = ".."`
 
 error: `allow` attribute without specifying a reason
-  --> tests/ui/allow_attributes_without_reason.rs:47:5
+  --> tests/ui/allow_attributes_without_reason.rs:46:5
    |
 LL |     #[allow(unused)]
    |     ^^^^^^^^^^^^^^^^
@@ -44,7 +44,7 @@ LL |     #[allow(unused)]
    = help: try adding a reason at the end with `, reason = ".."`
 
 error: `allow` attribute without specifying a reason
-  --> tests/ui/allow_attributes_without_reason.rs:47:5
+  --> tests/ui/allow_attributes_without_reason.rs:46:5
    |
 LL |     #[allow(unused)]
    |     ^^^^^^^^^^^^^^^^
diff --git a/tests/ui/auxiliary/external_item.rs b/tests/ui/auxiliary/external_item.rs
new file mode 100644
index 00000000000..ca4bc369e44
--- /dev/null
+++ b/tests/ui/auxiliary/external_item.rs
@@ -0,0 +1,7 @@
+pub struct _ExternalStruct {}
+
+impl _ExternalStruct {
+    pub fn _foo(self) {}
+}
+
+pub fn _exernal_foo() {}
diff --git a/tests/ui/cast_alignment.rs b/tests/ui/cast_alignment.rs
index 98ef5e36f94..72f5d4268cc 100644
--- a/tests/ui/cast_alignment.rs
+++ b/tests/ui/cast_alignment.rs
@@ -2,16 +2,16 @@
 
 #![feature(rustc_private)]
 #![feature(core_intrinsics)]
-extern crate libc;
-
-#[warn(clippy::cast_ptr_alignment)]
-#[allow(
+#![warn(clippy::cast_ptr_alignment)]
+#![allow(
     clippy::no_effect,
     clippy::unnecessary_operation,
     clippy::cast_lossless,
     clippy::borrow_as_ptr
 )]
 
+extern crate libc;
+
 fn main() {
     /* These should be warned against */
 
diff --git a/tests/ui/cmp_owned/without_suggestion.rs b/tests/ui/cmp_owned/without_suggestion.rs
index ec45d635c17..913aab72747 100644
--- a/tests/ui/cmp_owned/without_suggestion.rs
+++ b/tests/ui/cmp_owned/without_suggestion.rs
@@ -1,5 +1,5 @@
-#[allow(clippy::unnecessary_operation)]
-#[allow(clippy::implicit_clone)]
+#![allow(clippy::unnecessary_operation)]
+#![allow(clippy::implicit_clone)]
 
 fn main() {
     let x = &Baz;
diff --git a/tests/ui/collapsible_else_if.fixed b/tests/ui/collapsible_else_if.fixed
index 3b410b2f17b..c2d76146c64 100644
--- a/tests/ui/collapsible_else_if.fixed
+++ b/tests/ui/collapsible_else_if.fixed
@@ -1,9 +1,7 @@
 #![allow(clippy::assertions_on_constants, clippy::equatable_if_let, clippy::needless_if)]
+#![warn(clippy::collapsible_if, clippy::collapsible_else_if)]
 
 #[rustfmt::skip]
-#[warn(clippy::collapsible_if)]
-#[warn(clippy::collapsible_else_if)]
-
 fn main() {
     let x = "hello";
     let y = "world";
@@ -76,7 +74,6 @@ fn main() {
 }
 
 #[rustfmt::skip]
-#[allow(dead_code)]
 fn issue_7318() {
     if true { println!("I've been resolved!")
     }else if false {}
diff --git a/tests/ui/collapsible_else_if.rs b/tests/ui/collapsible_else_if.rs
index 772ef6f9fc6..3579e46cd44 100644
--- a/tests/ui/collapsible_else_if.rs
+++ b/tests/ui/collapsible_else_if.rs
@@ -1,9 +1,7 @@
 #![allow(clippy::assertions_on_constants, clippy::equatable_if_let, clippy::needless_if)]
+#![warn(clippy::collapsible_if, clippy::collapsible_else_if)]
 
 #[rustfmt::skip]
-#[warn(clippy::collapsible_if)]
-#[warn(clippy::collapsible_else_if)]
-
 fn main() {
     let x = "hello";
     let y = "world";
@@ -90,7 +88,6 @@ fn main() {
 }
 
 #[rustfmt::skip]
-#[allow(dead_code)]
 fn issue_7318() {
     if true { println!("I've been resolved!")
     }else{
diff --git a/tests/ui/collapsible_else_if.stderr b/tests/ui/collapsible_else_if.stderr
index dc19d90b4d1..395c2dcf68d 100644
--- a/tests/ui/collapsible_else_if.stderr
+++ b/tests/ui/collapsible_else_if.stderr
@@ -1,5 +1,5 @@
 error: this `else { if .. }` block can be collapsed
-  --> tests/ui/collapsible_else_if.rs:13:12
+  --> tests/ui/collapsible_else_if.rs:11:12
    |
 LL |       } else {
    |  ____________^
@@ -19,7 +19,7 @@ LL +     }
    |
 
 error: this `else { if .. }` block can be collapsed
-  --> tests/ui/collapsible_else_if.rs:21:12
+  --> tests/ui/collapsible_else_if.rs:19:12
    |
 LL |       } else {
    |  ____________^
@@ -37,7 +37,7 @@ LL +     }
    |
 
 error: this `else { if .. }` block can be collapsed
-  --> tests/ui/collapsible_else_if.rs:29:12
+  --> tests/ui/collapsible_else_if.rs:27:12
    |
 LL |       } else {
    |  ____________^
@@ -60,7 +60,7 @@ LL +     }
    |
 
 error: this `else { if .. }` block can be collapsed
-  --> tests/ui/collapsible_else_if.rs:40:12
+  --> tests/ui/collapsible_else_if.rs:38:12
    |
 LL |       } else {
    |  ____________^
@@ -83,7 +83,7 @@ LL +     }
    |
 
 error: this `else { if .. }` block can be collapsed
-  --> tests/ui/collapsible_else_if.rs:51:12
+  --> tests/ui/collapsible_else_if.rs:49:12
    |
 LL |       } else {
    |  ____________^
@@ -106,7 +106,7 @@ LL +     }
    |
 
 error: this `else { if .. }` block can be collapsed
-  --> tests/ui/collapsible_else_if.rs:62:12
+  --> tests/ui/collapsible_else_if.rs:60:12
    |
 LL |       } else {
    |  ____________^
@@ -129,7 +129,7 @@ LL +     }
    |
 
 error: this `else { if .. }` block can be collapsed
-  --> tests/ui/collapsible_else_if.rs:73:12
+  --> tests/ui/collapsible_else_if.rs:71:12
    |
 LL |       } else {
    |  ____________^
@@ -152,7 +152,7 @@ LL +     }
    |
 
 error: this `else { if .. }` block can be collapsed
-  --> tests/ui/collapsible_else_if.rs:96:10
+  --> tests/ui/collapsible_else_if.rs:93:10
    |
 LL |       }else{
    |  __________^
diff --git a/tests/ui/crashes/associated-constant-ice.rs b/tests/ui/crashes/associated-constant-ice.rs
index 948deba3ea6..fec16671eeb 100644
--- a/tests/ui/crashes/associated-constant-ice.rs
+++ b/tests/ui/crashes/associated-constant-ice.rs
@@ -1,4 +1,4 @@
-/// Test for https://github.com/rust-lang/rust-clippy/issues/1698
+// Test for https://github.com/rust-lang/rust-clippy/issues/1698
 
 pub trait Trait {
     const CONSTANT: u8;
diff --git a/tests/ui/crashes/cc_seme.rs b/tests/ui/crashes/cc_seme.rs
index 98588be9cf8..98897d6d7aa 100644
--- a/tests/ui/crashes/cc_seme.rs
+++ b/tests/ui/crashes/cc_seme.rs
@@ -1,6 +1,4 @@
-#[allow(dead_code)]
-
-/// Test for https://github.com/rust-lang/rust-clippy/issues/478
+// Test for https://github.com/rust-lang/rust-clippy/issues/478
 
 enum Baz {
     One,
diff --git a/tests/ui/crashes/ice-11230.rs b/tests/ui/crashes/ice-11230.rs
index 5761882273e..94044e9435e 100644
--- a/tests/ui/crashes/ice-11230.rs
+++ b/tests/ui/crashes/ice-11230.rs
@@ -1,4 +1,4 @@
-/// Test for https://github.com/rust-lang/rust-clippy/issues/11230
+// Test for https://github.com/rust-lang/rust-clippy/issues/11230
 
 fn main() {
     const A: &[for<'a> fn(&'a ())] = &[];
diff --git a/tests/ui/crashes/ice-1588.rs b/tests/ui/crashes/ice-1588.rs
index b0a3d11bce4..9ec093721c1 100644
--- a/tests/ui/crashes/ice-1588.rs
+++ b/tests/ui/crashes/ice-1588.rs
@@ -1,6 +1,6 @@
 #![allow(clippy::all)]
 
-/// Test for https://github.com/rust-lang/rust-clippy/issues/1588
+// Test for https://github.com/rust-lang/rust-clippy/issues/1588
 
 fn main() {
     match 1 {
diff --git a/tests/ui/crashes/ice-1969.rs b/tests/ui/crashes/ice-1969.rs
index 96a8fe6c24d..eb901c76729 100644
--- a/tests/ui/crashes/ice-1969.rs
+++ b/tests/ui/crashes/ice-1969.rs
@@ -1,6 +1,6 @@
 #![allow(clippy::all)]
 
-/// Test for https://github.com/rust-lang/rust-clippy/issues/1969
+// Test for https://github.com/rust-lang/rust-clippy/issues/1969
 
 fn main() {}
 
diff --git a/tests/ui/crashes/ice-2499.rs b/tests/ui/crashes/ice-2499.rs
index 45b3b1869dd..732f331ad14 100644
--- a/tests/ui/crashes/ice-2499.rs
+++ b/tests/ui/crashes/ice-2499.rs
@@ -1,8 +1,8 @@
 #![allow(dead_code, clippy::char_lit_as_u8, clippy::needless_bool)]
 
-/// Should not trigger an ICE in `SpanlessHash` / `consts::constant`
-///
-/// Issue: https://github.com/rust-lang/rust-clippy/issues/2499
+// Should not trigger an ICE in `SpanlessHash` / `consts::constant`
+//
+// Issue: https://github.com/rust-lang/rust-clippy/issues/2499
 
 fn f(s: &[u8]) -> bool {
     let t = s[0] as char;
diff --git a/tests/ui/crashes/ice-2594.rs b/tests/ui/crashes/ice-2594.rs
index 3f3986b6fc6..dddf860bd17 100644
--- a/tests/ui/crashes/ice-2594.rs
+++ b/tests/ui/crashes/ice-2594.rs
@@ -3,7 +3,6 @@
 /// Should not trigger an ICE in `SpanlessHash` / `consts::constant`
 ///
 /// Issue: https://github.com/rust-lang/rust-clippy/issues/2594
-
 fn spanless_hash_ice() {
     let txt = "something";
     let empty_header: [u8; 1] = [1; 1];
diff --git a/tests/ui/crashes/ice-2727.rs b/tests/ui/crashes/ice-2727.rs
index 56024abc8f5..59fb9b04b86 100644
--- a/tests/ui/crashes/ice-2727.rs
+++ b/tests/ui/crashes/ice-2727.rs
@@ -1,4 +1,4 @@
-/// Test for https://github.com/rust-lang/rust-clippy/issues/2727
+// Test for https://github.com/rust-lang/rust-clippy/issues/2727
 
 pub fn f(new: fn()) {
     new();
diff --git a/tests/ui/crashes/ice-2760.rs b/tests/ui/crashes/ice-2760.rs
index 61ef2480498..5f7d91abf99 100644
--- a/tests/ui/crashes/ice-2760.rs
+++ b/tests/ui/crashes/ice-2760.rs
@@ -5,10 +5,10 @@
     dead_code
 )]
 
-/// This should not compile-fail with:
-///
-///      error[E0277]: the trait bound `T: Foo` is not satisfied
-// See rust-lang/rust-clippy#2760.
+// This should not compile-fail with:
+//
+//      error[E0277]: the trait bound `T: Foo` is not satisfied
+// See https://github.com/rust-lang/rust-clippy/issues/2760
 
 trait Foo {
     type Bar;
diff --git a/tests/ui/crashes/ice-2862.rs b/tests/ui/crashes/ice-2862.rs
index 8326e3663b0..2573b571f55 100644
--- a/tests/ui/crashes/ice-2862.rs
+++ b/tests/ui/crashes/ice-2862.rs
@@ -1,4 +1,4 @@
-/// Test for https://github.com/rust-lang/rust-clippy/issues/2862
+// Test for https://github.com/rust-lang/rust-clippy/issues/2862
 
 pub trait FooMap {
     fn map<B, F: Fn() -> B>(&self, f: F) -> B;
diff --git a/tests/ui/crashes/ice-2865.rs b/tests/ui/crashes/ice-2865.rs
index c6298139601..28363707acc 100644
--- a/tests/ui/crashes/ice-2865.rs
+++ b/tests/ui/crashes/ice-2865.rs
@@ -1,6 +1,6 @@
 #![allow(dead_code, clippy::extra_unused_lifetimes)]
 
-/// Test for https://github.com/rust-lang/rust-clippy/issues/2865
+// Test for https://github.com/rust-lang/rust-clippy/issues/2865
 
 struct Ice {
     size: String,
diff --git a/tests/ui/crashes/ice-3151.rs b/tests/ui/crashes/ice-3151.rs
index 268ba86fc7a..f88a26cb485 100644
--- a/tests/ui/crashes/ice-3151.rs
+++ b/tests/ui/crashes/ice-3151.rs
@@ -1,4 +1,4 @@
-/// Test for https://github.com/rust-lang/rust-clippy/issues/3151
+// Test for https://github.com/rust-lang/rust-clippy/issues/3151
 
 #[derive(Clone)]
 pub struct HashMap<V, S> {
diff --git a/tests/ui/crashes/ice-3462.rs b/tests/ui/crashes/ice-3462.rs
index 21cd9d337cd..ccd617e305d 100644
--- a/tests/ui/crashes/ice-3462.rs
+++ b/tests/ui/crashes/ice-3462.rs
@@ -2,7 +2,7 @@
 #![allow(clippy::disallowed_names, clippy::equatable_if_let, clippy::needless_if)]
 #![allow(unused)]
 
-/// Test for https://github.com/rust-lang/rust-clippy/issues/3462
+// Test for https://github.com/rust-lang/rust-clippy/issues/3462
 
 enum Foo {
     Bar,
diff --git a/tests/ui/crashes/ice-3747.rs b/tests/ui/crashes/ice-3747.rs
index cdf018cbc88..44b1d7ed1b2 100644
--- a/tests/ui/crashes/ice-3747.rs
+++ b/tests/ui/crashes/ice-3747.rs
@@ -1,4 +1,4 @@
-/// Test for https://github.com/rust-lang/rust-clippy/issues/3747
+// Test for https://github.com/rust-lang/rust-clippy/issues/3747
 
 macro_rules! a {
     ( $pub:tt $($attr:tt)* ) => {
diff --git a/tests/ui/crashes/ice-700.rs b/tests/ui/crashes/ice-700.rs
index 0cbceedbd6b..5e004b94330 100644
--- a/tests/ui/crashes/ice-700.rs
+++ b/tests/ui/crashes/ice-700.rs
@@ -1,6 +1,6 @@
 #![deny(clippy::all)]
 
-/// Test for https://github.com/rust-lang/rust-clippy/issues/700
+// Test for https://github.com/rust-lang/rust-clippy/issues/700
 
 fn core() {}
 
diff --git a/tests/ui/crashes/ice_exact_size.rs b/tests/ui/crashes/ice_exact_size.rs
index 30e4b11ec0b..c0671eaff14 100644
--- a/tests/ui/crashes/ice_exact_size.rs
+++ b/tests/ui/crashes/ice_exact_size.rs
@@ -1,6 +1,6 @@
 #![deny(clippy::all)]
 
-/// Test for https://github.com/rust-lang/rust-clippy/issues/1336
+// Test for https://github.com/rust-lang/rust-clippy/issues/1336
 
 #[allow(dead_code)]
 struct Foo;
diff --git a/tests/ui/crashes/if_same_then_else.rs b/tests/ui/crashes/if_same_then_else.rs
index 2f913292995..a900fe5e6bc 100644
--- a/tests/ui/crashes/if_same_then_else.rs
+++ b/tests/ui/crashes/if_same_then_else.rs
@@ -1,7 +1,7 @@
 #![allow(clippy::comparison_chain)]
 #![deny(clippy::if_same_then_else)]
 
-/// Test for https://github.com/rust-lang/rust-clippy/issues/2426
+// Test for https://github.com/rust-lang/rust-clippy/issues/2426
 
 fn main() {}
 
diff --git a/tests/ui/crashes/inherent_impl.rs b/tests/ui/crashes/inherent_impl.rs
index aeb27b5ba8c..800a5a383f6 100644
--- a/tests/ui/crashes/inherent_impl.rs
+++ b/tests/ui/crashes/inherent_impl.rs
@@ -1,6 +1,6 @@
 #![deny(clippy::multiple_inherent_impl)]
 
-/// Test for https://github.com/rust-lang/rust-clippy/issues/4578
+// Test for https://github.com/rust-lang/rust-clippy/issues/4578
 
 macro_rules! impl_foo {
     ($struct:ident) => {
diff --git a/tests/ui/crashes/issue-825.rs b/tests/ui/crashes/issue-825.rs
index 05696e3d7d5..e8b455a0ec6 100644
--- a/tests/ui/crashes/issue-825.rs
+++ b/tests/ui/crashes/issue-825.rs
@@ -1,6 +1,6 @@
 #![allow(warnings)]
 
-/// Test for https://github.com/rust-lang/rust-clippy/issues/825
+// Test for https://github.com/rust-lang/rust-clippy/issues/825
 
 // this should compile in a reasonable amount of time
 fn rust_type_id(name: &str) {
diff --git a/tests/ui/crashes/match_same_arms_const.rs b/tests/ui/crashes/match_same_arms_const.rs
index 94c939665e6..626179c0015 100644
--- a/tests/ui/crashes/match_same_arms_const.rs
+++ b/tests/ui/crashes/match_same_arms_const.rs
@@ -1,6 +1,6 @@
 #![deny(clippy::match_same_arms)]
 
-/// Test for https://github.com/rust-lang/rust-clippy/issues/2427
+// Test for https://github.com/rust-lang/rust-clippy/issues/2427
 
 const PRICE_OF_SWEETS: u32 = 5;
 const PRICE_OF_KINDNESS: u32 = 0;
diff --git a/tests/ui/crashes/returns.rs b/tests/ui/crashes/returns.rs
index 8021ed4607d..91cdb5306c8 100644
--- a/tests/ui/crashes/returns.rs
+++ b/tests/ui/crashes/returns.rs
@@ -1,4 +1,4 @@
-/// Test for https://github.com/rust-lang/rust-clippy/issues/1346
+// Test for https://github.com/rust-lang/rust-clippy/issues/1346
 
 #[deny(warnings)]
 fn cfg_return() -> i32 {
diff --git a/tests/ui/derive.rs b/tests/ui/derive.rs
index 20ac8a6e6be..4ac21f2cb4b 100644
--- a/tests/ui/derive.rs
+++ b/tests/ui/derive.rs
@@ -1,7 +1,6 @@
 #![allow(clippy::non_canonical_clone_impl, clippy::non_canonical_partial_ord_impl, dead_code)]
 #![warn(clippy::expl_impl_clone_on_copy)]
 
-
 #[derive(Copy)]
 struct Qux;
 
diff --git a/tests/ui/derive.stderr b/tests/ui/derive.stderr
index d5f9d7cf2a2..486e6dc1b6b 100644
--- a/tests/ui/derive.stderr
+++ b/tests/ui/derive.stderr
@@ -1,5 +1,5 @@
 error: you are implementing `Clone` explicitly on a `Copy` type
-  --> tests/ui/derive.rs:8:1
+  --> tests/ui/derive.rs:7:1
    |
 LL | / impl Clone for Qux {
 LL | |
@@ -10,7 +10,7 @@ LL | | }
    | |_^
    |
 note: consider deriving `Clone` or removing `Copy`
-  --> tests/ui/derive.rs:8:1
+  --> tests/ui/derive.rs:7:1
    |
 LL | / impl Clone for Qux {
 LL | |
@@ -23,7 +23,7 @@ LL | | }
    = help: to override `-D warnings` add `#[allow(clippy::expl_impl_clone_on_copy)]`
 
 error: you are implementing `Clone` explicitly on a `Copy` type
-  --> tests/ui/derive.rs:33:1
+  --> tests/ui/derive.rs:32:1
    |
 LL | / impl<'a> Clone for Lt<'a> {
 LL | |
@@ -34,7 +34,7 @@ LL | | }
    | |_^
    |
 note: consider deriving `Clone` or removing `Copy`
-  --> tests/ui/derive.rs:33:1
+  --> tests/ui/derive.rs:32:1
    |
 LL | / impl<'a> Clone for Lt<'a> {
 LL | |
@@ -45,7 +45,7 @@ LL | | }
    | |_^
 
 error: you are implementing `Clone` explicitly on a `Copy` type
-  --> tests/ui/derive.rs:45:1
+  --> tests/ui/derive.rs:44:1
    |
 LL | / impl Clone for BigArray {
 LL | |
@@ -56,7 +56,7 @@ LL | | }
    | |_^
    |
 note: consider deriving `Clone` or removing `Copy`
-  --> tests/ui/derive.rs:45:1
+  --> tests/ui/derive.rs:44:1
    |
 LL | / impl Clone for BigArray {
 LL | |
@@ -67,7 +67,7 @@ LL | | }
    | |_^
 
 error: you are implementing `Clone` explicitly on a `Copy` type
-  --> tests/ui/derive.rs:57:1
+  --> tests/ui/derive.rs:56:1
    |
 LL | / impl Clone for FnPtr {
 LL | |
@@ -78,7 +78,7 @@ LL | | }
    | |_^
    |
 note: consider deriving `Clone` or removing `Copy`
-  --> tests/ui/derive.rs:57:1
+  --> tests/ui/derive.rs:56:1
    |
 LL | / impl Clone for FnPtr {
 LL | |
@@ -89,7 +89,7 @@ LL | | }
    | |_^
 
 error: you are implementing `Clone` explicitly on a `Copy` type
-  --> tests/ui/derive.rs:78:1
+  --> tests/ui/derive.rs:77:1
    |
 LL | / impl<T: Clone> Clone for Generic2<T> {
 LL | |
@@ -100,7 +100,7 @@ LL | | }
    | |_^
    |
 note: consider deriving `Clone` or removing `Copy`
-  --> tests/ui/derive.rs:78:1
+  --> tests/ui/derive.rs:77:1
    |
 LL | / impl<T: Clone> Clone for Generic2<T> {
 LL | |
diff --git a/tests/ui/diverging_sub_expression.rs b/tests/ui/diverging_sub_expression.rs
index e0acf050949..1abba60fd34 100644
--- a/tests/ui/diverging_sub_expression.rs
+++ b/tests/ui/diverging_sub_expression.rs
@@ -67,3 +67,9 @@ fn foobar() {
         };
     }
 }
+
+#[allow(unused)]
+fn ignore_todo() {
+    let x: u32 = todo!();
+    println!("{x}");
+}
diff --git a/tests/ui/doc/doc_lazy_blank_line.fixed b/tests/ui/doc/doc_lazy_blank_line.fixed
deleted file mode 100644
index 1aaa26afe7f..00000000000
--- a/tests/ui/doc/doc_lazy_blank_line.fixed
+++ /dev/null
@@ -1,47 +0,0 @@
-// https://github.com/rust-lang/rust-clippy/issues/12917
-#![warn(clippy::doc_lazy_continuation)]
-
-/// This is a constant.
-///
-/// The meaning of which should not be explained.
-pub const A: i32 = 42;
-
-/// This is another constant, no longer used.
-///
-/// This block of documentation has a long
-/// explanation and derivation to explain
-/// why it is what it is, and how it's used.
-///
-/// It is left here for historical reasons, and
-/// for reference.
-///
-/// Reasons it's great:
-///  - First reason
-///  - Second reason
-///
-//pub const B: i32 = 1337;
-
-/// This is yet another constant.
-///
-/// This has a similar fate as `B`.
-///
-/// Reasons it's useful:
-///  1. First reason
-///  2. Second reason
-///
-//pub const C: i32 = 8008;
-
-/// This is still in use.
-pub const D: i32 = 20;
-
-/// > blockquote code path
-///
-
-/// bottom text
-pub const E: i32 = 20;
-
-/// > blockquote code path
-///
-#[repr(C)]
-/// bottom text
-pub struct Foo(i32);
diff --git a/tests/ui/doc/doc_lazy_blank_line.rs b/tests/ui/doc/doc_lazy_blank_line.rs
deleted file mode 100644
index e1ab8fc8389..00000000000
--- a/tests/ui/doc/doc_lazy_blank_line.rs
+++ /dev/null
@@ -1,43 +0,0 @@
-// https://github.com/rust-lang/rust-clippy/issues/12917
-#![warn(clippy::doc_lazy_continuation)]
-
-/// This is a constant.
-///
-/// The meaning of which should not be explained.
-pub const A: i32 = 42;
-
-/// This is another constant, no longer used.
-///
-/// This block of documentation has a long
-/// explanation and derivation to explain
-/// why it is what it is, and how it's used.
-///
-/// It is left here for historical reasons, and
-/// for reference.
-///
-/// Reasons it's great:
-///  - First reason
-///  - Second reason
-//pub const B: i32 = 1337;
-
-/// This is yet another constant.
-///
-/// This has a similar fate as `B`.
-///
-/// Reasons it's useful:
-///  1. First reason
-///  2. Second reason
-//pub const C: i32 = 8008;
-
-/// This is still in use.
-pub const D: i32 = 20;
-
-/// > blockquote code path
-
-/// bottom text
-pub const E: i32 = 20;
-
-/// > blockquote code path
-#[repr(C)]
-/// bottom text
-pub struct Foo(i32);
diff --git a/tests/ui/doc/doc_lazy_blank_line.stderr b/tests/ui/doc/doc_lazy_blank_line.stderr
deleted file mode 100644
index 854906a7474..00000000000
--- a/tests/ui/doc/doc_lazy_blank_line.stderr
+++ /dev/null
@@ -1,56 +0,0 @@
-error: doc list item without indentation
-  --> tests/ui/doc/doc_lazy_blank_line.rs:23:5
-   |
-LL | /// This is yet another constant.
-   |     ^
-   |
-   = help: if this is intended to be part of the list, indent 3 spaces
-   = note: `-D clippy::doc-lazy-continuation` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::doc_lazy_continuation)]`
-help: if this should be its own paragraph, add a blank doc comment line
-   |
-LL ~ ///  - Second reason
-LL + ///
-   |
-
-error: doc list item without indentation
-  --> tests/ui/doc/doc_lazy_blank_line.rs:32:5
-   |
-LL | /// This is still in use.
-   |     ^
-   |
-   = help: if this is intended to be part of the list, indent 4 spaces
-help: if this should be its own paragraph, add a blank doc comment line
-   |
-LL ~ ///  2. Second reason
-LL + ///
-   |
-
-error: doc quote line without `>` marker
-  --> tests/ui/doc/doc_lazy_blank_line.rs:37:5
-   |
-LL | /// bottom text
-   |     ^
-   |
-   = help: if this not intended to be a quote at all, escape it with `\>`
-help: if this should be its own paragraph, add a blank doc comment line
-   |
-LL ~ /// > blockquote code path
-LL + ///
-   |
-
-error: doc quote line without `>` marker
-  --> tests/ui/doc/doc_lazy_blank_line.rs:42:5
-   |
-LL | /// bottom text
-   |     ^
-   |
-   = help: if this not intended to be a quote at all, escape it with `\>`
-help: if this should be its own paragraph, add a blank doc comment line
-   |
-LL ~ /// > blockquote code path
-LL + ///
-   |
-
-error: aborting due to 4 previous errors
-
diff --git a/tests/ui/doc/doc_lazy_list.fixed b/tests/ui/doc/doc_lazy_list.fixed
index ea59ae4c01c..da537518a2b 100644
--- a/tests/ui/doc/doc_lazy_list.fixed
+++ b/tests/ui/doc/doc_lazy_list.fixed
@@ -7,9 +7,8 @@ fn one() {}
 
 /// 1. first line
 ///    lazy list continuations don't make warnings with this lint
-///
 //~^ ERROR: doc list item without indentation
-/// because they don't have the
+///    because they don't have the
 //~^ ERROR: doc list item without indentation
 fn two() {}
 
@@ -20,9 +19,8 @@ fn three() {}
 
 ///   - first line
 ///     lazy list continuations don't make warnings with this lint
-///
 //~^ ERROR: doc list item without indentation
-/// because they don't have the
+///     because they don't have the
 //~^ ERROR: doc list item without indentation
 fn four() {}
 
@@ -33,9 +31,8 @@ fn five() {}
 
 ///   - - first line
 ///       this will warn on the lazy continuation
-///
 //~^ ERROR: doc list item without indentation
-///     and so should this
+///       and so should this
 //~^ ERROR: doc list item without indentation
 fn six() {}
 
diff --git a/tests/ui/doc/doc_lazy_list.stderr b/tests/ui/doc/doc_lazy_list.stderr
index 52aa74df894..b38f43b7555 100644
--- a/tests/ui/doc/doc_lazy_list.stderr
+++ b/tests/ui/doc/doc_lazy_list.stderr
@@ -30,12 +30,11 @@ error: doc list item without indentation
 LL | /// because they don't have the
    |     ^
    |
-   = help: if this is intended to be part of the list, indent 3 spaces
-help: if this should be its own paragraph, add a blank doc comment line
-   |
-LL ~ /// lazy list continuations don't make warnings with this lint
-LL + ///
+   = help: if this is supposed to be its own paragraph, add a blank line
+help: indent this line
    |
+LL | ///    because they don't have the
+   |     +++
 
 error: doc list item without indentation
   --> tests/ui/doc/doc_lazy_list.rs:16:5
@@ -67,12 +66,11 @@ error: doc list item without indentation
 LL | /// because they don't have the
    |     ^
    |
-   = help: if this is intended to be part of the list, indent 4 spaces
-help: if this should be its own paragraph, add a blank doc comment line
-   |
-LL ~ /// lazy list continuations don't make warnings with this lint
-LL + ///
+   = help: if this is supposed to be its own paragraph, add a blank line
+help: indent this line
    |
+LL | ///     because they don't have the
+   |     ++++
 
 error: doc list item without indentation
   --> tests/ui/doc/doc_lazy_list.rs:28:5
@@ -104,12 +102,11 @@ error: doc list item without indentation
 LL | ///     and so should this
    |     ^^^^
    |
-   = help: if this is intended to be part of the list, indent 2 spaces
-help: if this should be its own paragraph, add a blank doc comment line
-   |
-LL ~ /// this will warn on the lazy continuation
-LL + ///
+   = help: if this is supposed to be its own paragraph, add a blank line
+help: indent this line
    |
+LL | ///       and so should this
+   |         ++
 
 error: doc list item without indentation
   --> tests/ui/doc/doc_lazy_list.rs:56:5
diff --git a/tests/ui/duplicate_underscore_argument.rs b/tests/ui/duplicate_underscore_argument.rs
index 118f6e4a34c..a725538436c 100644
--- a/tests/ui/duplicate_underscore_argument.rs
+++ b/tests/ui/duplicate_underscore_argument.rs
@@ -1,5 +1,4 @@
 #![warn(clippy::duplicate_underscore_argument)]
-#[allow(dead_code, unused)]
 
 fn join_the_dark_side(darth: i32, _darth: i32) {}
 //~^ ERROR: `darth` already exists, having another argument having almost the same name ma
diff --git a/tests/ui/duplicate_underscore_argument.stderr b/tests/ui/duplicate_underscore_argument.stderr
index 40a24b823d1..74979b15788 100644
--- a/tests/ui/duplicate_underscore_argument.stderr
+++ b/tests/ui/duplicate_underscore_argument.stderr
@@ -1,5 +1,5 @@
 error: `darth` already exists, having another argument having almost the same name makes code comprehension and documentation more difficult
-  --> tests/ui/duplicate_underscore_argument.rs:4:23
+  --> tests/ui/duplicate_underscore_argument.rs:3:23
    |
 LL | fn join_the_dark_side(darth: i32, _darth: i32) {}
    |                       ^^^^^
diff --git a/tests/ui/empty_line_after/doc_comments.1.fixed b/tests/ui/empty_line_after/doc_comments.1.fixed
new file mode 100644
index 00000000000..fd6a94b6a80
--- /dev/null
+++ b/tests/ui/empty_line_after/doc_comments.1.fixed
@@ -0,0 +1,135 @@
+#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)]
+
+//~vvv empty_line_after_doc_comments
+/// Meant to be an
+/// inner doc comment
+/// for the crate
+fn first_in_crate() {}
+
+mod m {
+    //~vvv empty_line_after_doc_comments
+    /// Meant to be an
+    /// inner doc comment
+    /// for the module
+    fn first_in_module() {}
+}
+
+mod some_mod {
+    //! This doc comment should *NOT* produce a warning
+
+    mod some_inner_mod {
+        fn some_noop() {}
+    }
+
+    //~v empty_line_after_doc_comments
+    /// # Indented
+    /// Blank line
+    fn indented() {}
+}
+
+//~v empty_line_after_doc_comments
+/// This should produce a warning
+fn with_doc_and_newline() {}
+
+// This should *NOT* produce a warning
+#[crate_type = "lib"]
+/// some comment
+fn with_no_newline_and_comment() {}
+
+//~v empty_line_after_doc_comments
+/// This doc comment should produce a warning
+/** This is also a doc comment and is part of the warning
+ */
+#[allow(non_camel_case_types)]
+#[allow(missing_docs)]
+#[allow(dead_code)]
+fn three_attributes() {}
+
+mod misattributed {
+    //~v empty_line_after_doc_comments
+    /// docs for `old_code`
+    // fn old_code() {}
+    fn new_code() {}
+
+    //~vv empty_line_after_doc_comments
+    /// Docs
+    /// for OldA
+    // struct OldA;
+    /// Docs
+    /// for OldB
+    // struct OldB;
+    /// Docs
+    /// for Multiple
+    #[allow(dead_code)]
+    struct Multiple;
+}
+
+mod block_comments {
+    //~v empty_line_after_doc_comments
+    /**
+     * Meant to be inner doc comment
+     */
+    fn first_in_module() {}
+
+    //~v empty_line_after_doc_comments
+    /**
+     * Docs for `old_code`
+     */
+    /* fn old_code() {} */
+    /**
+     * Docs for `new_code`
+     */
+    fn new_code() {}
+
+    //~v empty_line_after_doc_comments
+    /// Docs for `old_code2`
+    /* fn old_code2() {} */
+    /// Docs for `new_code2`
+    fn new_code2() {}
+}
+
+// This should *NOT* produce a warning
+#[doc = "
+Returns the escaped value of the textual representation of
+
+"]
+pub fn function() -> bool {
+    true
+}
+
+// This should *NOT* produce a warning
+#[derive(Clone, Copy)]
+pub enum FooFighter {
+    Bar1,
+
+    Bar2,
+
+    Bar3,
+
+    Bar4,
+}
+
+/// Should not lint
+// some line comment
+/// gaps without an empty line
+struct LineComment;
+
+/// This should *NOT* produce a warning because the empty line is inside a block comment
+/*
+
+*/
+pub struct EmptyInBlockComment;
+
+/// This should *NOT* produce a warning
+/* test */
+pub struct BlockComment;
+
+/// Ignore the empty line inside a cfg_attr'd out attribute
+#[cfg_attr(any(), multiline(
+    foo = 1
+
+    bar = 2
+))]
+fn empty_line_in_cfg_attr() {}
+
+fn main() {}
diff --git a/tests/ui/empty_line_after/doc_comments.2.fixed b/tests/ui/empty_line_after/doc_comments.2.fixed
new file mode 100644
index 00000000000..7a57dcd9233
--- /dev/null
+++ b/tests/ui/empty_line_after/doc_comments.2.fixed
@@ -0,0 +1,144 @@
+#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)]
+
+//~vvv empty_line_after_doc_comments
+//! Meant to be an
+//! inner doc comment
+//! for the crate
+
+fn first_in_crate() {}
+
+mod m {
+    //~vvv empty_line_after_doc_comments
+    //! Meant to be an
+    //! inner doc comment
+    //! for the module
+
+    fn first_in_module() {}
+}
+
+mod some_mod {
+    //! This doc comment should *NOT* produce a warning
+
+    mod some_inner_mod {
+        fn some_noop() {}
+    }
+
+    //~v empty_line_after_doc_comments
+    /// # Indented
+    ///
+    /// Blank line
+    fn indented() {}
+}
+
+//~v empty_line_after_doc_comments
+/// This should produce a warning
+fn with_doc_and_newline() {}
+
+// This should *NOT* produce a warning
+#[crate_type = "lib"]
+/// some comment
+fn with_no_newline_and_comment() {}
+
+//~v empty_line_after_doc_comments
+/// This doc comment should produce a warning
+/** This is also a doc comment and is part of the warning
+ */
+#[allow(non_camel_case_types)]
+#[allow(missing_docs)]
+#[allow(dead_code)]
+fn three_attributes() {}
+
+mod misattributed {
+    //~v empty_line_after_doc_comments
+    // /// docs for `old_code`
+    // fn old_code() {}
+
+    fn new_code() {}
+
+    //~vv empty_line_after_doc_comments
+    // /// Docs
+    // /// for OldA
+    // struct OldA;
+
+    // /// Docs
+    // /// for OldB
+    // struct OldB;
+
+    /// Docs
+    /// for Multiple
+    #[allow(dead_code)]
+    struct Multiple;
+}
+
+mod block_comments {
+    //~v empty_line_after_doc_comments
+    /*!
+     * Meant to be inner doc comment
+     */
+
+    fn first_in_module() {}
+
+    //~v empty_line_after_doc_comments
+    /*
+     * Docs for `old_code`
+     */
+    /* fn old_code() {} */
+
+    /**
+     * Docs for `new_code`
+     */
+    fn new_code() {}
+
+    //~v empty_line_after_doc_comments
+    // /// Docs for `old_code2`
+    /* fn old_code2() {} */
+
+    /// Docs for `new_code2`
+    fn new_code2() {}
+}
+
+// This should *NOT* produce a warning
+#[doc = "
+Returns the escaped value of the textual representation of
+
+"]
+pub fn function() -> bool {
+    true
+}
+
+// This should *NOT* produce a warning
+#[derive(Clone, Copy)]
+pub enum FooFighter {
+    Bar1,
+
+    Bar2,
+
+    Bar3,
+
+    Bar4,
+}
+
+/// Should not lint
+// some line comment
+/// gaps without an empty line
+struct LineComment;
+
+/// This should *NOT* produce a warning because the empty line is inside a block comment
+/*
+
+*/
+pub struct EmptyInBlockComment;
+
+/// This should *NOT* produce a warning
+/* test */
+pub struct BlockComment;
+
+/// Ignore the empty line inside a cfg_attr'd out attribute
+#[cfg_attr(any(), multiline(
+    foo = 1
+
+    bar = 2
+))]
+fn empty_line_in_cfg_attr() {}
+
+fn main() {}
diff --git a/tests/ui/empty_line_after/doc_comments.rs b/tests/ui/empty_line_after/doc_comments.rs
new file mode 100644
index 00000000000..1da761a5c3d
--- /dev/null
+++ b/tests/ui/empty_line_after/doc_comments.rs
@@ -0,0 +1,147 @@
+#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)]
+
+//~vvv empty_line_after_doc_comments
+/// Meant to be an
+/// inner doc comment
+/// for the crate
+
+fn first_in_crate() {}
+
+mod m {
+    //~vvv empty_line_after_doc_comments
+    /// Meant to be an
+    /// inner doc comment
+    /// for the module
+
+    fn first_in_module() {}
+}
+
+mod some_mod {
+    //! This doc comment should *NOT* produce a warning
+
+    mod some_inner_mod {
+        fn some_noop() {}
+    }
+
+    //~v empty_line_after_doc_comments
+    /// # Indented
+
+    /// Blank line
+    fn indented() {}
+}
+
+//~v empty_line_after_doc_comments
+/// This should produce a warning
+
+fn with_doc_and_newline() {}
+
+// This should *NOT* produce a warning
+#[crate_type = "lib"]
+/// some comment
+fn with_no_newline_and_comment() {}
+
+//~v empty_line_after_doc_comments
+/// This doc comment should produce a warning
+
+/** This is also a doc comment and is part of the warning
+ */
+
+#[allow(non_camel_case_types)]
+#[allow(missing_docs)]
+#[allow(dead_code)]
+fn three_attributes() {}
+
+mod misattributed {
+    //~v empty_line_after_doc_comments
+    /// docs for `old_code`
+    // fn old_code() {}
+
+    fn new_code() {}
+
+    //~vv empty_line_after_doc_comments
+    /// Docs
+    /// for OldA
+    // struct OldA;
+
+    /// Docs
+    /// for OldB
+    // struct OldB;
+
+    /// Docs
+    /// for Multiple
+    #[allow(dead_code)]
+    struct Multiple;
+}
+
+mod block_comments {
+    //~v empty_line_after_doc_comments
+    /**
+     * Meant to be inner doc comment
+     */
+
+    fn first_in_module() {}
+
+    //~v empty_line_after_doc_comments
+    /**
+     * Docs for `old_code`
+     */
+    /* fn old_code() {} */
+
+    /**
+     * Docs for `new_code`
+     */
+    fn new_code() {}
+
+    //~v empty_line_after_doc_comments
+    /// Docs for `old_code2`
+    /* fn old_code2() {} */
+
+    /// Docs for `new_code2`
+    fn new_code2() {}
+}
+
+// This should *NOT* produce a warning
+#[doc = "
+Returns the escaped value of the textual representation of
+
+"]
+pub fn function() -> bool {
+    true
+}
+
+// This should *NOT* produce a warning
+#[derive(Clone, Copy)]
+pub enum FooFighter {
+    Bar1,
+
+    Bar2,
+
+    Bar3,
+
+    Bar4,
+}
+
+/// Should not lint
+// some line comment
+/// gaps without an empty line
+struct LineComment;
+
+/// This should *NOT* produce a warning because the empty line is inside a block comment
+/*
+
+*/
+pub struct EmptyInBlockComment;
+
+/// This should *NOT* produce a warning
+/* test */
+pub struct BlockComment;
+
+/// Ignore the empty line inside a cfg_attr'd out attribute
+#[cfg_attr(any(), multiline(
+    foo = 1
+
+    bar = 2
+))]
+fn empty_line_in_cfg_attr() {}
+
+fn main() {}
diff --git a/tests/ui/empty_line_after/doc_comments.stderr b/tests/ui/empty_line_after/doc_comments.stderr
new file mode 100644
index 00000000000..c238b4c9a17
--- /dev/null
+++ b/tests/ui/empty_line_after/doc_comments.stderr
@@ -0,0 +1,176 @@
+error: empty line after doc comment
+  --> tests/ui/empty_line_after/doc_comments.rs:6:1
+   |
+LL | / /// for the crate
+LL | |
+   | |_
+LL |   fn first_in_crate() {}
+   |   ------------------- the comment documents this function
+   |
+   = note: `-D clippy::empty-line-after-doc-comments` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::empty_line_after_doc_comments)]`
+   = help: if the empty line is unintentional remove it
+help: if the comment should document the crate use an inner doc comment
+   |
+LL ~ //! Meant to be an
+LL ~ //! inner doc comment
+LL ~ //! for the crate
+   |
+
+error: empty line after doc comment
+  --> tests/ui/empty_line_after/doc_comments.rs:14:5
+   |
+LL | /     /// for the module
+LL | |
+   | |_
+LL |       fn first_in_module() {}
+   |       -------------------- the comment documents this function
+   |
+   = help: if the empty line is unintentional remove it
+help: if the comment should document the parent module use an inner doc comment
+   |
+LL ~     //! Meant to be an
+LL ~     //! inner doc comment
+LL ~     //! for the module
+   |
+
+error: empty line after doc comment
+  --> tests/ui/empty_line_after/doc_comments.rs:27:5
+   |
+LL | /     /// # Indented
+LL | |
+   | |_
+LL |       /// Blank line
+LL |       fn indented() {}
+   |       ------------- the comment documents this function
+   |
+   = help: if the empty line is unintentional remove it
+help: if the documentation should include the empty line include it in the comment
+   |
+LL |     ///
+   |
+
+error: empty line after doc comment
+  --> tests/ui/empty_line_after/doc_comments.rs:34:1
+   |
+LL | / /// This should produce a warning
+LL | |
+   | |_
+LL |   fn with_doc_and_newline() {}
+   |   ------------------------- the comment documents this function
+   |
+   = help: if the empty line is unintentional remove it
+
+error: empty lines after doc comment
+  --> tests/ui/empty_line_after/doc_comments.rs:44:1
+   |
+LL | / /// This doc comment should produce a warning
+LL | |
+LL | | /** This is also a doc comment and is part of the warning
+LL | |  */
+LL | |
+   | |_
+...
+LL |   fn three_attributes() {}
+   |   --------------------- the comment documents this function
+   |
+   = help: if the empty lines are unintentional remove them
+
+error: empty line after doc comment
+  --> tests/ui/empty_line_after/doc_comments.rs:56:5
+   |
+LL | /     /// docs for `old_code`
+LL | |     // fn old_code() {}
+LL | |
+   | |_
+LL |       fn new_code() {}
+   |       ------------- the comment documents this function
+   |
+   = help: if the empty line is unintentional remove it
+help: if the doc comment should not document `new_code` comment it out
+   |
+LL |     // /// docs for `old_code`
+   |     ++
+
+error: empty lines after doc comment
+  --> tests/ui/empty_line_after/doc_comments.rs:63:5
+   |
+LL | /     /// for OldA
+LL | |     // struct OldA;
+LL | |
+LL | |     /// Docs
+LL | |     /// for OldB
+LL | |     // struct OldB;
+LL | |
+   | |_
+...
+LL |       struct Multiple;
+   |       --------------- the comment documents this struct
+   |
+   = help: if the empty lines are unintentional remove them
+help: if the doc comment should not document `Multiple` comment it out
+   |
+LL ~     // /// Docs
+LL ~     // /// for OldA
+LL |     // struct OldA;
+LL |
+LL ~     // /// Docs
+LL ~     // /// for OldB
+   |
+
+error: empty line after doc comment
+  --> tests/ui/empty_line_after/doc_comments.rs:78:5
+   |
+LL | /     /**
+LL | |      * Meant to be inner doc comment
+LL | |      */
+LL | |
+   | |_
+LL |       fn first_in_module() {}
+   |       -------------------- the comment documents this function
+   |
+   = help: if the empty line is unintentional remove it
+help: if the comment should document the parent module use an inner doc comment
+   |
+LL |     /*!
+   |       ~
+
+error: empty line after doc comment
+  --> tests/ui/empty_line_after/doc_comments.rs:85:5
+   |
+LL | /     /**
+LL | |      * Docs for `old_code`
+LL | |      */
+LL | |     /* fn old_code() {} */
+LL | |
+   | |_
+...
+LL |       fn new_code() {}
+   |       ------------- the comment documents this function
+   |
+   = help: if the empty line is unintentional remove it
+help: if the doc comment should not document `new_code` comment it out
+   |
+LL -     /**
+LL +     /*
+   |
+
+error: empty line after doc comment
+  --> tests/ui/empty_line_after/doc_comments.rs:96:5
+   |
+LL | /     /// Docs for `old_code2`
+LL | |     /* fn old_code2() {} */
+LL | |
+   | |_
+LL |       /// Docs for `new_code2`
+LL |       fn new_code2() {}
+   |       -------------- the comment documents this function
+   |
+   = help: if the empty line is unintentional remove it
+help: if the doc comment should not document `new_code2` comment it out
+   |
+LL |     // /// Docs for `old_code2`
+   |     ++
+
+error: aborting due to 10 previous errors
+
diff --git a/tests/ui/empty_line_after/outer_attribute.1.fixed b/tests/ui/empty_line_after/outer_attribute.1.fixed
new file mode 100644
index 00000000000..cd7ea24b6be
--- /dev/null
+++ b/tests/ui/empty_line_after/outer_attribute.1.fixed
@@ -0,0 +1,103 @@
+//@aux-build:../auxiliary/proc_macro_attr.rs
+#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)]
+
+//~v empty_line_after_outer_attr
+#[crate_type = "lib"]
+fn first_in_crate() {}
+
+#[macro_use]
+extern crate proc_macro_attr;
+
+//~v empty_line_after_outer_attr
+#[inline]
+/// some comment
+fn with_one_newline_and_comment() {}
+
+#[inline]
+/// some comment
+fn with_no_newline_and_comment() {}
+
+//~v empty_line_after_outer_attr
+#[inline]
+fn with_one_newline() {}
+
+#[rustfmt::skip]
+mod two_lines {
+    //~v empty_line_after_outer_attr
+    #[crate_type = "lib"]
+    fn with_two_newlines() {}
+}
+
+//~v empty_line_after_outer_attr
+#[doc = "doc attributes should be considered attributes"]
+enum Baz {
+    One,
+    Two,
+}
+
+//~v empty_line_after_outer_attr
+#[repr(C)]
+struct Foo {
+    one: isize,
+    two: isize,
+}
+
+//~v empty_line_after_outer_attr
+#[allow(dead_code)]
+mod foo {}
+
+//~v empty_line_after_outer_attr
+#[inline]
+// Still lint cases where the empty line does not immediately follow the attribute
+fn comment_before_empty_line() {}
+
+#[doc = "
+Returns the escaped value of the textual representation of
+
+"]
+pub fn function() -> bool {
+    true
+}
+
+#[derive(Clone, Copy)]
+pub enum FooFighter {
+    Bar1,
+
+    Bar2,
+
+    Bar3,
+
+    Bar4,
+}
+
+#[crate_type = "lib"]
+/*
+
+*/
+pub struct EmptyLineInBlockComment;
+
+#[crate_type = "lib"]
+/* test */
+pub struct BlockComment;
+
+// See https://github.com/rust-lang/rust-clippy/issues/5567
+#[rustfmt::skip]
+#[fake_async_trait]
+pub trait Bazz {
+    fn foo() -> Vec<u8> {
+        let _i = "";
+
+
+
+        vec![]
+    }
+}
+
+#[derive(Clone, Copy)]
+#[dummy(string = "first line
+
+second line
+")]
+pub struct Args;
+
+fn main() {}
diff --git a/tests/ui/empty_line_after/outer_attribute.2.fixed b/tests/ui/empty_line_after/outer_attribute.2.fixed
new file mode 100644
index 00000000000..1b044d2fcde
--- /dev/null
+++ b/tests/ui/empty_line_after/outer_attribute.2.fixed
@@ -0,0 +1,106 @@
+//@aux-build:../auxiliary/proc_macro_attr.rs
+#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)]
+
+//~v empty_line_after_outer_attr
+#![crate_type = "lib"]
+
+fn first_in_crate() {}
+
+#[macro_use]
+extern crate proc_macro_attr;
+
+//~v empty_line_after_outer_attr
+#[inline]
+/// some comment
+fn with_one_newline_and_comment() {}
+
+#[inline]
+/// some comment
+fn with_no_newline_and_comment() {}
+
+//~v empty_line_after_outer_attr
+#[inline]
+fn with_one_newline() {}
+
+#[rustfmt::skip]
+mod two_lines {
+    //~v empty_line_after_outer_attr
+    #![crate_type = "lib"]
+
+
+    fn with_two_newlines() {}
+}
+
+//~v empty_line_after_outer_attr
+#[doc = "doc attributes should be considered attributes"]
+enum Baz {
+    One,
+    Two,
+}
+
+//~v empty_line_after_outer_attr
+#[repr(C)]
+struct Foo {
+    one: isize,
+    two: isize,
+}
+
+//~v empty_line_after_outer_attr
+#[allow(dead_code)]
+mod foo {}
+
+//~v empty_line_after_outer_attr
+#[inline]
+// Still lint cases where the empty line does not immediately follow the attribute
+fn comment_before_empty_line() {}
+
+#[doc = "
+Returns the escaped value of the textual representation of
+
+"]
+pub fn function() -> bool {
+    true
+}
+
+#[derive(Clone, Copy)]
+pub enum FooFighter {
+    Bar1,
+
+    Bar2,
+
+    Bar3,
+
+    Bar4,
+}
+
+#[crate_type = "lib"]
+/*
+
+*/
+pub struct EmptyLineInBlockComment;
+
+#[crate_type = "lib"]
+/* test */
+pub struct BlockComment;
+
+// See https://github.com/rust-lang/rust-clippy/issues/5567
+#[rustfmt::skip]
+#[fake_async_trait]
+pub trait Bazz {
+    fn foo() -> Vec<u8> {
+        let _i = "";
+
+
+
+        vec![]
+    }
+}
+
+#[derive(Clone, Copy)]
+#[dummy(string = "first line
+
+second line
+")]
+pub struct Args;
+
+fn main() {}
diff --git a/tests/ui/empty_line_after/outer_attribute.rs b/tests/ui/empty_line_after/outer_attribute.rs
new file mode 100644
index 00000000000..81e1a7ab8ed
--- /dev/null
+++ b/tests/ui/empty_line_after/outer_attribute.rs
@@ -0,0 +1,112 @@
+//@aux-build:../auxiliary/proc_macro_attr.rs
+#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)]
+
+//~v empty_line_after_outer_attr
+#[crate_type = "lib"]
+
+fn first_in_crate() {}
+
+#[macro_use]
+extern crate proc_macro_attr;
+
+//~v empty_line_after_outer_attr
+#[inline]
+
+/// some comment
+fn with_one_newline_and_comment() {}
+
+#[inline]
+/// some comment
+fn with_no_newline_and_comment() {}
+
+//~v empty_line_after_outer_attr
+#[inline]
+
+fn with_one_newline() {}
+
+#[rustfmt::skip]
+mod two_lines {
+    //~v empty_line_after_outer_attr
+    #[crate_type = "lib"]
+
+
+    fn with_two_newlines() {}
+}
+
+//~v empty_line_after_outer_attr
+#[doc = "doc attributes should be considered attributes"]
+
+enum Baz {
+    One,
+    Two,
+}
+
+//~v empty_line_after_outer_attr
+#[repr(C)]
+
+struct Foo {
+    one: isize,
+    two: isize,
+}
+
+//~v empty_line_after_outer_attr
+#[allow(dead_code)]
+
+mod foo {}
+
+//~v empty_line_after_outer_attr
+#[inline]
+// Still lint cases where the empty line does not immediately follow the attribute
+
+fn comment_before_empty_line() {}
+
+#[doc = "
+Returns the escaped value of the textual representation of
+
+"]
+pub fn function() -> bool {
+    true
+}
+
+#[derive(Clone, Copy)]
+pub enum FooFighter {
+    Bar1,
+
+    Bar2,
+
+    Bar3,
+
+    Bar4,
+}
+
+#[crate_type = "lib"]
+/*
+
+*/
+pub struct EmptyLineInBlockComment;
+
+#[crate_type = "lib"]
+/* test */
+pub struct BlockComment;
+
+// See https://github.com/rust-lang/rust-clippy/issues/5567
+#[rustfmt::skip]
+#[fake_async_trait]
+pub trait Bazz {
+    fn foo() -> Vec<u8> {
+        let _i = "";
+
+
+
+        vec![]
+    }
+}
+
+#[derive(Clone, Copy)]
+#[dummy(string = "first line
+
+second line
+")]
+pub struct Args;
+
+fn main() {}
diff --git a/tests/ui/empty_line_after/outer_attribute.stderr b/tests/ui/empty_line_after/outer_attribute.stderr
new file mode 100644
index 00000000000..b73ebb4f662
--- /dev/null
+++ b/tests/ui/empty_line_after/outer_attribute.stderr
@@ -0,0 +1,103 @@
+error: empty line after outer attribute
+  --> tests/ui/empty_line_after/outer_attribute.rs:5:1
+   |
+LL | / #[crate_type = "lib"]
+LL | |
+   | |_
+LL |   fn first_in_crate() {}
+   |   ------------------- the attribute applies to this function
+   |
+   = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::empty_line_after_outer_attr)]`
+   = help: if the empty line is unintentional remove it
+help: if the attribute should apply to the crate use an inner attribute
+   |
+LL | #![crate_type = "lib"]
+   |  +
+
+error: empty line after outer attribute
+  --> tests/ui/empty_line_after/outer_attribute.rs:13:1
+   |
+LL | / #[inline]
+LL | |
+   | |_
+LL |   /// some comment
+LL |   fn with_one_newline_and_comment() {}
+   |   --------------------------------- the attribute applies to this function
+   |
+   = help: if the empty line is unintentional remove it
+
+error: empty line after outer attribute
+  --> tests/ui/empty_line_after/outer_attribute.rs:23:1
+   |
+LL | / #[inline]
+LL | |
+   | |_
+LL |   fn with_one_newline() {}
+   |   --------------------- the attribute applies to this function
+   |
+   = help: if the empty line is unintentional remove it
+
+error: empty lines after outer attribute
+  --> tests/ui/empty_line_after/outer_attribute.rs:30:5
+   |
+LL | /     #[crate_type = "lib"]
+LL | |
+LL | |
+   | |_
+LL |       fn with_two_newlines() {}
+   |       ---------------------- the attribute applies to this function
+   |
+   = help: if the empty lines are unintentional remove them
+help: if the attribute should apply to the parent module use an inner attribute
+   |
+LL |     #![crate_type = "lib"]
+   |      +
+
+error: empty line after outer attribute
+  --> tests/ui/empty_line_after/outer_attribute.rs:37:1
+   |
+LL | / #[doc = "doc attributes should be considered attributes"]
+LL | |
+   | |_
+LL |   enum Baz {
+   |   -------- the attribute applies to this enum
+   |
+   = help: if the empty line is unintentional remove it
+
+error: empty line after outer attribute
+  --> tests/ui/empty_line_after/outer_attribute.rs:45:1
+   |
+LL | / #[repr(C)]
+LL | |
+   | |_
+LL |   struct Foo {
+   |   ---------- the attribute applies to this struct
+   |
+   = help: if the empty line is unintentional remove it
+
+error: empty line after outer attribute
+  --> tests/ui/empty_line_after/outer_attribute.rs:53:1
+   |
+LL | / #[allow(dead_code)]
+LL | |
+   | |_
+LL |   mod foo {}
+   |   ------- the attribute applies to this module
+   |
+   = help: if the empty line is unintentional remove it
+
+error: empty line after outer attribute
+  --> tests/ui/empty_line_after/outer_attribute.rs:58:1
+   |
+LL | / #[inline]
+LL | | // Still lint cases where the empty line does not immediately follow the attribute
+LL | |
+   | |_
+LL |   fn comment_before_empty_line() {}
+   |   ------------------------------ the attribute applies to this function
+   |
+   = help: if the empty line is unintentional remove it
+
+error: aborting due to 8 previous errors
+
diff --git a/tests/ui/empty_line_after_doc_comments.rs b/tests/ui/empty_line_after_doc_comments.rs
deleted file mode 100644
index dd78491749c..00000000000
--- a/tests/ui/empty_line_after_doc_comments.rs
+++ /dev/null
@@ -1,132 +0,0 @@
-//@aux-build:proc_macro_attr.rs
-#![warn(clippy::empty_line_after_doc_comments)]
-#![allow(clippy::assertions_on_constants, clippy::duplicated_attributes)]
-#![feature(custom_inner_attributes)]
-#![rustfmt::skip]
-
-#[macro_use]
-extern crate proc_macro_attr;
-
-mod some_mod {
-    //! This doc comment should *NOT* produce a warning
-
-    mod some_inner_mod {
-        fn some_noop() {}
-    }
-}
-
-/// This should produce a warning
-
-fn with_doc_and_newline() { assert!(true)}
-
-// This should *NOT* produce a warning
-#[crate_type = "lib"]
-
-/// some comment
-fn with_one_newline_and_comment() { assert!(true) }
-
-// This should *NOT* produce a warning
-#[crate_type = "lib"]
-/// some comment
-fn with_no_newline_and_comment() { assert!(true) }
-
-
-// This should *NOT* produce a warning
-#[crate_type = "lib"]
-
-fn with_one_newline() { assert!(true) }
-
-// This should *NOT* produce a warning
-#[crate_type = "lib"]
-
-
-fn with_two_newlines() { assert!(true) }
-
-
-// This should *NOT* produce a warning
-#[crate_type = "lib"]
-
-enum Baz {
-    One,
-    Two
-}
-
-// This should *NOT* produce a warning
-#[crate_type = "lib"]
-
-struct Foo {
-    one: isize,
-    two: isize
-}
-
-// This should *NOT* produce a warning
-#[crate_type = "lib"]
-
-mod foo {
-}
-
-/// This doc comment should produce a warning
-
-/** This is also a doc comment and should produce a warning
- */
-
-// This should *NOT* produce a warning
-#[allow(non_camel_case_types)]
-#[allow(missing_docs)]
-#[allow(missing_docs)]
-fn three_attributes() { assert!(true) }
-
-// This should *NOT* produce a warning
-#[doc = "
-Returns the escaped value of the textual representation of
-
-"]
-pub fn function() -> bool {
-    true
-}
-
-// This should *NOT* produce a warning
-#[derive(Clone, Copy)]
-pub enum FooFighter {
-    Bar1,
-
-    Bar2,
-
-    Bar3,
-
-    Bar4
-}
-
-// This should *NOT* produce a warning because the empty line is inside a block comment
-#[crate_type = "lib"]
-/*
-
-*/
-pub struct S;
-
-// This should *NOT* produce a warning
-#[crate_type = "lib"]
-/* test */
-pub struct T;
-
-// This should *NOT* produce a warning
-// See https://github.com/rust-lang/rust-clippy/issues/5567
-#[fake_async_trait]
-pub trait Bazz {
-    fn foo() -> Vec<u8> {
-        let _i = "";
-
-
-
-        vec![]
-    }
-}
-
-#[derive(Clone, Copy)]
-#[dummy(string = "first line
-
-second line
-")]
-pub struct Args;
-
-fn main() {}
diff --git a/tests/ui/empty_line_after_doc_comments.stderr b/tests/ui/empty_line_after_doc_comments.stderr
deleted file mode 100644
index 889ccf6ba19..00000000000
--- a/tests/ui/empty_line_after_doc_comments.stderr
+++ /dev/null
@@ -1,37 +0,0 @@
-error: 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 `//`?
-  --> tests/ui/empty_line_after_doc_comments.rs:18:1
-   |
-LL | / /// This should produce a warning
-LL | |
-LL | | fn with_doc_and_newline() { assert!(true)}
-   | |_
-   |
-   = note: `-D clippy::empty-line-after-doc-comments` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::empty_line_after_doc_comments)]`
-
-error: 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 `//`?
-  --> tests/ui/empty_line_after_doc_comments.rs:68:1
-   |
-LL | / /// This doc comment should produce a warning
-LL | |
-LL | | /** This is also a doc comment and should produce a warning
-LL | |  */
-...  |
-LL | | #[allow(missing_docs)]
-LL | | fn three_attributes() { assert!(true) }
-   | |_
-
-error: 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 `//`?
-  --> tests/ui/empty_line_after_doc_comments.rs:70:1
-   |
-LL | / /** This is also a doc comment and should produce a warning
-LL | |  */
-LL | |
-LL | | // This should *NOT* produce a warning
-...  |
-LL | | #[allow(missing_docs)]
-LL | | fn three_attributes() { assert!(true) }
-   | |_
-
-error: aborting due to 3 previous errors
-
diff --git a/tests/ui/empty_line_after_outer_attribute.rs b/tests/ui/empty_line_after_outer_attribute.rs
deleted file mode 100644
index f147cf2cd5d..00000000000
--- a/tests/ui/empty_line_after_outer_attribute.rs
+++ /dev/null
@@ -1,120 +0,0 @@
-//@aux-build:proc_macro_attr.rs
-#![warn(clippy::empty_line_after_outer_attr)]
-#![allow(clippy::assertions_on_constants, clippy::duplicated_attributes)]
-#![feature(custom_inner_attributes)]
-#![rustfmt::skip]
-
-#[macro_use]
-extern crate proc_macro_attr;
-
-// This should produce a warning
-#[crate_type = "lib"]
-
-/// some comment
-fn with_one_newline_and_comment() { assert!(true) }
-
-// This should not produce a warning
-#[crate_type = "lib"]
-/// some comment
-fn with_no_newline_and_comment() { assert!(true) }
-
-
-// This should produce a warning
-#[crate_type = "lib"]
-
-fn with_one_newline() { assert!(true) }
-
-// This should produce a warning, too
-#[crate_type = "lib"]
-
-
-fn with_two_newlines() { assert!(true) }
-
-
-// This should produce a warning
-#[crate_type = "lib"]
-
-enum Baz {
-    One,
-    Two
-}
-
-// This should produce a warning
-#[crate_type = "lib"]
-
-struct Foo {
-    one: isize,
-    two: isize
-}
-
-// This should produce a warning
-#[crate_type = "lib"]
-
-mod foo {
-}
-
-/// This doc comment should not produce a warning
-
-/** This is also a doc comment and should not produce a warning
- */
-
-// This should not produce a warning
-#[allow(non_camel_case_types)]
-#[allow(missing_docs)]
-#[allow(missing_docs)]
-fn three_attributes() { assert!(true) }
-
-// This should not produce a warning
-#[doc = "
-Returns the escaped value of the textual representation of
-
-"]
-pub fn function() -> bool {
-    true
-}
-
-// This should not produce a warning
-#[derive(Clone, Copy)]
-pub enum FooFighter {
-    Bar1,
-
-    Bar2,
-
-    Bar3,
-
-    Bar4
-}
-
-// This should not produce a warning because the empty line is inside a block comment
-#[crate_type = "lib"]
-/*
-
-*/
-pub struct S;
-
-// This should not produce a warning
-#[crate_type = "lib"]
-/* test */
-pub struct T;
-
-// This should not produce a warning
-// See https://github.com/rust-lang/rust-clippy/issues/5567
-#[fake_async_trait]
-pub trait Bazz {
-    fn foo() -> Vec<u8> {
-        let _i = "";
-
-
-
-        vec![]
-    }
-}
-
-#[derive(Clone, Copy)]
-#[dummy(string = "first line
-
-second line
-")]
-pub struct Args;
-
-fn main() {}
diff --git a/tests/ui/empty_line_after_outer_attribute.stderr b/tests/ui/empty_line_after_outer_attribute.stderr
deleted file mode 100644
index b43e6e30da2..00000000000
--- a/tests/ui/empty_line_after_outer_attribute.stderr
+++ /dev/null
@@ -1,54 +0,0 @@
-error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
-  --> tests/ui/empty_line_after_outer_attribute.rs:11:1
-   |
-LL | / #[crate_type = "lib"]
-LL | |
-LL | | /// some comment
-LL | | fn with_one_newline_and_comment() { assert!(true) }
-   | |_
-   |
-   = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::empty_line_after_outer_attr)]`
-
-error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
-  --> tests/ui/empty_line_after_outer_attribute.rs:23:1
-   |
-LL | / #[crate_type = "lib"]
-LL | |
-LL | | fn with_one_newline() { assert!(true) }
-   | |_
-
-error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
-  --> tests/ui/empty_line_after_outer_attribute.rs:28:1
-   |
-LL | / #[crate_type = "lib"]
-...  |
-LL | | fn with_two_newlines() { assert!(true) }
-   | |_
-
-error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
-  --> tests/ui/empty_line_after_outer_attribute.rs:35:1
-   |
-LL | / #[crate_type = "lib"]
-LL | |
-LL | | enum Baz {
-   | |_
-
-error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
-  --> tests/ui/empty_line_after_outer_attribute.rs:43:1
-   |
-LL | / #[crate_type = "lib"]
-LL | |
-LL | | struct Foo {
-   | |_
-
-error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
-  --> tests/ui/empty_line_after_outer_attribute.rs:51:1
-   |
-LL | / #[crate_type = "lib"]
-LL | |
-LL | | mod foo {
-   | |_
-
-error: aborting due to 6 previous errors
-
diff --git a/tests/ui/exit1.rs b/tests/ui/exit1.rs
index a89f6dd4ca0..36b3c42fd99 100644
--- a/tests/ui/exit1.rs
+++ b/tests/ui/exit1.rs
@@ -1,4 +1,4 @@
-#[warn(clippy::exit)]
+#![warn(clippy::exit)]
 
 fn not_main() {
     if true {
diff --git a/tests/ui/exit2.rs b/tests/ui/exit2.rs
index d5ff93fb9cc..9bbb7b073a4 100644
--- a/tests/ui/exit2.rs
+++ b/tests/ui/exit2.rs
@@ -1,4 +1,4 @@
-#[warn(clippy::exit)]
+#![warn(clippy::exit)]
 
 fn also_not_main() {
     std::process::exit(3);
diff --git a/tests/ui/exit3.rs b/tests/ui/exit3.rs
index 9dc0e1015a4..cab908aafd3 100644
--- a/tests/ui/exit3.rs
+++ b/tests/ui/exit3.rs
@@ -1,4 +1,4 @@
-#[warn(clippy::exit)]
+#![warn(clippy::exit)]
 
 fn main() {
     if true {
diff --git a/tests/ui/expect_fun_call.fixed b/tests/ui/expect_fun_call.fixed
index 6ac3c43ad29..8f800c71941 100644
--- a/tests/ui/expect_fun_call.fixed
+++ b/tests/ui/expect_fun_call.fixed
@@ -5,8 +5,6 @@
     clippy::unnecessary_literal_unwrap
 )]
 
-/// Checks implementation of the `EXPECT_FUN_CALL` lint
-
 macro_rules! one {
     () => {
         1
diff --git a/tests/ui/expect_fun_call.rs b/tests/ui/expect_fun_call.rs
index 22ea2db504f..b5cfafb2993 100644
--- a/tests/ui/expect_fun_call.rs
+++ b/tests/ui/expect_fun_call.rs
@@ -5,8 +5,6 @@
     clippy::unnecessary_literal_unwrap
 )]
 
-/// Checks implementation of the `EXPECT_FUN_CALL` lint
-
 macro_rules! one {
     () => {
         1
diff --git a/tests/ui/expect_fun_call.stderr b/tests/ui/expect_fun_call.stderr
index b41904d04fa..bae853ac5c1 100644
--- a/tests/ui/expect_fun_call.stderr
+++ b/tests/ui/expect_fun_call.stderr
@@ -1,5 +1,5 @@
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:37:26
+  --> tests/ui/expect_fun_call.rs:35:26
    |
 LL |     with_none_and_format.expect(&format!("Error {}: fake error", error_code));
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))`
@@ -8,85 +8,85 @@ LL |     with_none_and_format.expect(&format!("Error {}: fake error", error_code
    = help: to override `-D warnings` add `#[allow(clippy::expect_fun_call)]`
 
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:40:26
+  --> tests/ui/expect_fun_call.rs:38:26
    |
 LL |     with_none_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))`
 
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:43:37
+  --> tests/ui/expect_fun_call.rs:41:37
    |
 LL |     with_none_and_format_with_macro.expect(format!("Error {}: fake error", one!()).as_str());
    |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("Error {}: fake error", one!()))`
 
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:53:25
+  --> tests/ui/expect_fun_call.rs:51:25
    |
 LL |     with_err_and_format.expect(&format!("Error {}: fake error", error_code));
    |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))`
 
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:56:25
+  --> tests/ui/expect_fun_call.rs:54:25
    |
 LL |     with_err_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
    |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))`
 
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:68:17
+  --> tests/ui/expect_fun_call.rs:66:17
    |
 LL |     Some("foo").expect(format!("{} {}", 1, 2).as_ref());
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("{} {}", 1, 2))`
 
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:89:21
+  --> tests/ui/expect_fun_call.rs:87:21
    |
 LL |         Some("foo").expect(&get_string());
    |                     ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_string()) })`
 
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:90:21
+  --> tests/ui/expect_fun_call.rs:88:21
    |
 LL |         Some("foo").expect(get_string().as_ref());
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_string()) })`
 
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:91:21
+  --> tests/ui/expect_fun_call.rs:89:21
    |
 LL |         Some("foo").expect(get_string().as_str());
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_string()) })`
 
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:93:21
+  --> tests/ui/expect_fun_call.rs:91:21
    |
 LL |         Some("foo").expect(get_static_str());
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_static_str()) })`
 
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:94:21
+  --> tests/ui/expect_fun_call.rs:92:21
    |
 LL |         Some("foo").expect(get_non_static_str(&0));
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_non_static_str(&0).to_string()) })`
 
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:98:16
+  --> tests/ui/expect_fun_call.rs:96:16
    |
 LL |     Some(true).expect(&format!("key {}, {}", 1, 2));
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("key {}, {}", 1, 2))`
 
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:104:17
+  --> tests/ui/expect_fun_call.rs:102:17
    |
 LL |         opt_ref.expect(&format!("{:?}", opt_ref));
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("{:?}", opt_ref))`
 
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:108:20
+  --> tests/ui/expect_fun_call.rs:106:20
    |
 LL |     format_capture.expect(&format!("{error_code}"));
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("{error_code}"))`
 
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:111:30
+  --> tests/ui/expect_fun_call.rs:109:30
    |
 LL |     format_capture_and_value.expect(&format!("{error_code}, {}", 1));
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("{error_code}, {}", 1))`
diff --git a/tests/ui/implicit_saturating_sub.fixed b/tests/ui/implicit_saturating_sub.fixed
index 27f679797dd..81cc1494914 100644
--- a/tests/ui/implicit_saturating_sub.fixed
+++ b/tests/ui/implicit_saturating_sub.fixed
@@ -184,18 +184,18 @@ fn main() {
     let mut m = Mock;
     let mut u_32 = 3000;
     let a = 200;
-    let mut _b = 8;
+    let mut b = 8;
 
     if m != 0 {
         m -= 1;
     }
 
     if a > 0 {
-        _b -= 1;
+        b -= 1;
     }
 
     if 0 > a {
-        _b -= 1;
+        b -= 1;
     }
 
     if u_32 > 0 {
@@ -214,4 +214,11 @@ fn main() {
     } else if u_32 > 0 {
         u_32 -= 1;
     }
+
+    let result = if a < b {
+        println!("we shouldn't remove this");
+        0
+    } else {
+        a - b
+    };
 }
diff --git a/tests/ui/implicit_saturating_sub.rs b/tests/ui/implicit_saturating_sub.rs
index 5d7b95d2c65..f73396ebd27 100644
--- a/tests/ui/implicit_saturating_sub.rs
+++ b/tests/ui/implicit_saturating_sub.rs
@@ -230,18 +230,18 @@ fn main() {
     let mut m = Mock;
     let mut u_32 = 3000;
     let a = 200;
-    let mut _b = 8;
+    let mut b = 8;
 
     if m != 0 {
         m -= 1;
     }
 
     if a > 0 {
-        _b -= 1;
+        b -= 1;
     }
 
     if 0 > a {
-        _b -= 1;
+        b -= 1;
     }
 
     if u_32 > 0 {
@@ -260,4 +260,11 @@ fn main() {
     } else if u_32 > 0 {
         u_32 -= 1;
     }
+
+    let result = if a < b {
+        println!("we shouldn't remove this");
+        0
+    } else {
+        a - b
+    };
 }
diff --git a/tests/ui/manual_arithmetic_check-2.rs b/tests/ui/manual_arithmetic_check-2.rs
new file mode 100644
index 00000000000..e97e3bdfef7
--- /dev/null
+++ b/tests/ui/manual_arithmetic_check-2.rs
@@ -0,0 +1,35 @@
+//@no-rustfix
+#![warn(clippy::implicit_saturating_sub)]
+#![allow(arithmetic_overflow)]
+
+fn main() {
+    let a = 12u32;
+    let b = 13u32;
+
+    let result = if a > b { b - a } else { 0 };
+    //~^ ERROR: inverted arithmetic check before subtraction
+    let result = if b < a { b - a } else { 0 };
+    //~^ ERROR: inverted arithmetic check before subtraction
+
+    let result = if a > b { 0 } else { a - b };
+    //~^ ERROR: inverted arithmetic check before subtraction
+    let result = if a >= b { 0 } else { a - b };
+    //~^ ERROR: inverted arithmetic check before subtraction
+    let result = if b < a { 0 } else { a - b };
+    //~^ ERROR: inverted arithmetic check before subtraction
+    let result = if b <= a { 0 } else { a - b };
+    //~^ ERROR: inverted arithmetic check before subtraction
+
+    let af = 12f32;
+    let bf = 13f32;
+    // Should not lint!
+    let result = if bf < af { 0. } else { af - bf };
+
+    // Should not lint!
+    let result = if a < b {
+        println!("we shouldn't remove this");
+        0
+    } else {
+        a - b
+    };
+}
diff --git a/tests/ui/manual_arithmetic_check-2.stderr b/tests/ui/manual_arithmetic_check-2.stderr
new file mode 100644
index 00000000000..4121aa7464f
--- /dev/null
+++ b/tests/ui/manual_arithmetic_check-2.stderr
@@ -0,0 +1,75 @@
+error: inverted arithmetic check before subtraction
+  --> tests/ui/manual_arithmetic_check-2.rs:9:23
+   |
+LL |     let result = if a > b { b - a } else { 0 };
+   |                       ^     ----- help: try replacing it with: `a - b`
+   |
+note: this subtraction underflows when `b < a`
+  --> tests/ui/manual_arithmetic_check-2.rs:9:29
+   |
+LL |     let result = if a > b { b - a } else { 0 };
+   |                             ^^^^^
+   = note: `#[deny(clippy::inverted_saturating_sub)]` on by default
+
+error: inverted arithmetic check before subtraction
+  --> tests/ui/manual_arithmetic_check-2.rs:11:23
+   |
+LL |     let result = if b < a { b - a } else { 0 };
+   |                       ^     ----- help: try replacing it with: `a - b`
+   |
+note: this subtraction underflows when `b < a`
+  --> tests/ui/manual_arithmetic_check-2.rs:11:29
+   |
+LL |     let result = if b < a { b - a } else { 0 };
+   |                             ^^^^^
+
+error: inverted arithmetic check before subtraction
+  --> tests/ui/manual_arithmetic_check-2.rs:14:23
+   |
+LL |     let result = if a > b { 0 } else { a - b };
+   |                       ^                ----- help: try replacing it with: `b - a`
+   |
+note: this subtraction underflows when `a < b`
+  --> tests/ui/manual_arithmetic_check-2.rs:14:40
+   |
+LL |     let result = if a > b { 0 } else { a - b };
+   |                                        ^^^^^
+
+error: inverted arithmetic check before subtraction
+  --> tests/ui/manual_arithmetic_check-2.rs:16:23
+   |
+LL |     let result = if a >= b { 0 } else { a - b };
+   |                       ^^                ----- help: try replacing it with: `b - a`
+   |
+note: this subtraction underflows when `a < b`
+  --> tests/ui/manual_arithmetic_check-2.rs:16:41
+   |
+LL |     let result = if a >= b { 0 } else { a - b };
+   |                                         ^^^^^
+
+error: inverted arithmetic check before subtraction
+  --> tests/ui/manual_arithmetic_check-2.rs:18:23
+   |
+LL |     let result = if b < a { 0 } else { a - b };
+   |                       ^                ----- help: try replacing it with: `b - a`
+   |
+note: this subtraction underflows when `a < b`
+  --> tests/ui/manual_arithmetic_check-2.rs:18:40
+   |
+LL |     let result = if b < a { 0 } else { a - b };
+   |                                        ^^^^^
+
+error: inverted arithmetic check before subtraction
+  --> tests/ui/manual_arithmetic_check-2.rs:20:23
+   |
+LL |     let result = if b <= a { 0 } else { a - b };
+   |                       ^^                ----- help: try replacing it with: `b - a`
+   |
+note: this subtraction underflows when `a < b`
+  --> tests/ui/manual_arithmetic_check-2.rs:20:41
+   |
+LL |     let result = if b <= a { 0 } else { a - b };
+   |                                         ^^^^^
+
+error: aborting due to 6 previous errors
+
diff --git a/tests/ui/manual_arithmetic_check.fixed b/tests/ui/manual_arithmetic_check.fixed
new file mode 100644
index 00000000000..29ecbb9ad2a
--- /dev/null
+++ b/tests/ui/manual_arithmetic_check.fixed
@@ -0,0 +1,24 @@
+#![warn(clippy::implicit_saturating_sub, clippy::inverted_saturating_sub)]
+#![allow(clippy::if_same_then_else)]
+
+fn main() {
+    let a = 12u32;
+    let b = 13u32;
+    let c = 8u32;
+
+    let result = a.saturating_sub(b);
+    //~^ ERROR: manual arithmetic check found
+    let result = a.saturating_sub(b);
+    //~^ ERROR: manual arithmetic check found
+
+    let result = a.saturating_sub(b);
+    //~^ ERROR: manual arithmetic check found
+    let result = a.saturating_sub(b);
+    //~^ ERROR: manual arithmetic check found
+
+    // Should not warn!
+    let result = if a > b { a - b } else { a - c };
+
+    // Just to check it won't break clippy.
+    let result = if b > a { 0 } else { 0 };
+}
diff --git a/tests/ui/manual_arithmetic_check.rs b/tests/ui/manual_arithmetic_check.rs
new file mode 100644
index 00000000000..69554c6b61c
--- /dev/null
+++ b/tests/ui/manual_arithmetic_check.rs
@@ -0,0 +1,24 @@
+#![warn(clippy::implicit_saturating_sub, clippy::inverted_saturating_sub)]
+#![allow(clippy::if_same_then_else)]
+
+fn main() {
+    let a = 12u32;
+    let b = 13u32;
+    let c = 8u32;
+
+    let result = if a > b { a - b } else { 0 };
+    //~^ ERROR: manual arithmetic check found
+    let result = if b < a { a - b } else { 0 };
+    //~^ ERROR: manual arithmetic check found
+
+    let result = if a < b { 0 } else { a - b };
+    //~^ ERROR: manual arithmetic check found
+    let result = if b > a { 0 } else { a - b };
+    //~^ ERROR: manual arithmetic check found
+
+    // Should not warn!
+    let result = if a > b { a - b } else { a - c };
+
+    // Just to check it won't break clippy.
+    let result = if b > a { 0 } else { 0 };
+}
diff --git a/tests/ui/manual_arithmetic_check.stderr b/tests/ui/manual_arithmetic_check.stderr
new file mode 100644
index 00000000000..b0cf73cd915
--- /dev/null
+++ b/tests/ui/manual_arithmetic_check.stderr
@@ -0,0 +1,29 @@
+error: manual arithmetic check found
+  --> tests/ui/manual_arithmetic_check.rs:9:18
+   |
+LL |     let result = if a > b { a - b } else { 0 };
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b)`
+   |
+   = note: `-D clippy::implicit-saturating-sub` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::implicit_saturating_sub)]`
+
+error: manual arithmetic check found
+  --> tests/ui/manual_arithmetic_check.rs:11:18
+   |
+LL |     let result = if b < a { a - b } else { 0 };
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b)`
+
+error: manual arithmetic check found
+  --> tests/ui/manual_arithmetic_check.rs:14:18
+   |
+LL |     let result = if a < b { 0 } else { a - b };
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b)`
+
+error: manual arithmetic check found
+  --> tests/ui/manual_arithmetic_check.rs:16:18
+   |
+LL |     let result = if b > a { 0 } else { a - b };
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b)`
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/manual_range_patterns.fixed b/tests/ui/manual_range_patterns.fixed
index f1b99637afd..60467bf9e88 100644
--- a/tests/ui/manual_range_patterns.fixed
+++ b/tests/ui/manual_range_patterns.fixed
@@ -45,4 +45,12 @@ fn main() {
         };
     }
     mac!(f);
+
+    #[rustfmt::skip]
+    let _ = match f {
+        | 2..=15 => 4,
+        | 241..=254 => 5,
+        | 255 => 6,
+        | _ => 7,
+    };
 }
diff --git a/tests/ui/manual_range_patterns.rs b/tests/ui/manual_range_patterns.rs
index 869ffbe80b9..9cd80333449 100644
--- a/tests/ui/manual_range_patterns.rs
+++ b/tests/ui/manual_range_patterns.rs
@@ -45,4 +45,12 @@ fn main() {
         };
     }
     mac!(f);
+
+    #[rustfmt::skip]
+    let _ = match f {
+        | 2..=15 => 4,
+        | 241..=254 => 5,
+        | 255 => 6,
+        | _ => 7,
+    };
 }
diff --git a/tests/ui/match_overlapping_arm.rs b/tests/ui/match_overlapping_arm.rs
index 4457ae73da2..a056fdeaa5d 100644
--- a/tests/ui/match_overlapping_arm.rs
+++ b/tests/ui/match_overlapping_arm.rs
@@ -2,8 +2,6 @@
 #![allow(clippy::redundant_pattern_matching)]
 #![allow(clippy::if_same_then_else, clippy::equatable_if_let, clippy::needless_if)]
 
-/// Tests for match_overlapping_arm
-
 fn overlapping() {
     const FOO: u64 = 2;
 
diff --git a/tests/ui/match_overlapping_arm.stderr b/tests/ui/match_overlapping_arm.stderr
index 65092ffbb55..a60a09a0799 100644
--- a/tests/ui/match_overlapping_arm.stderr
+++ b/tests/ui/match_overlapping_arm.stderr
@@ -1,11 +1,11 @@
 error: some ranges overlap
-  --> tests/ui/match_overlapping_arm.rs:11:9
+  --> tests/ui/match_overlapping_arm.rs:9:9
    |
 LL |         0..=10 => println!("0..=10"),
    |         ^^^^^^
    |
 note: overlaps with this
-  --> tests/ui/match_overlapping_arm.rs:13:9
+  --> tests/ui/match_overlapping_arm.rs:11:9
    |
 LL |         0..=11 => println!("0..=11"),
    |         ^^^^^^
@@ -13,85 +13,85 @@ LL |         0..=11 => println!("0..=11"),
    = help: to override `-D warnings` add `#[allow(clippy::match_overlapping_arm)]`
 
 error: some ranges overlap
-  --> tests/ui/match_overlapping_arm.rs:18:9
+  --> tests/ui/match_overlapping_arm.rs:16:9
    |
 LL |         0..=5 => println!("0..=5"),
    |         ^^^^^
    |
 note: overlaps with this
-  --> tests/ui/match_overlapping_arm.rs:21:9
+  --> tests/ui/match_overlapping_arm.rs:19:9
    |
 LL |         FOO..=11 => println!("FOO..=11"),
    |         ^^^^^^^^
 
 error: some ranges overlap
-  --> tests/ui/match_overlapping_arm.rs:56:9
+  --> tests/ui/match_overlapping_arm.rs:54:9
    |
 LL |         0..11 => println!("0..11"),
    |         ^^^^^
    |
 note: overlaps with this
-  --> tests/ui/match_overlapping_arm.rs:58:9
+  --> tests/ui/match_overlapping_arm.rs:56:9
    |
 LL |         0..=11 => println!("0..=11"),
    |         ^^^^^^
 
 error: some ranges overlap
-  --> tests/ui/match_overlapping_arm.rs:82:9
+  --> tests/ui/match_overlapping_arm.rs:80:9
    |
 LL |         0..=10 => println!("0..=10"),
    |         ^^^^^^
    |
 note: overlaps with this
-  --> tests/ui/match_overlapping_arm.rs:81:9
+  --> tests/ui/match_overlapping_arm.rs:79:9
    |
 LL |         5..14 => println!("5..14"),
    |         ^^^^^
 
 error: some ranges overlap
-  --> tests/ui/match_overlapping_arm.rs:88:9
+  --> tests/ui/match_overlapping_arm.rs:86:9
    |
 LL |         0..7 => println!("0..7"),
    |         ^^^^
    |
 note: overlaps with this
-  --> tests/ui/match_overlapping_arm.rs:90:9
+  --> tests/ui/match_overlapping_arm.rs:88:9
    |
 LL |         0..=10 => println!("0..=10"),
    |         ^^^^^^
 
 error: some ranges overlap
-  --> tests/ui/match_overlapping_arm.rs:101:9
+  --> tests/ui/match_overlapping_arm.rs:99:9
    |
 LL |         ..=23 => println!("..=23"),
    |         ^^^^^
    |
 note: overlaps with this
-  --> tests/ui/match_overlapping_arm.rs:103:9
+  --> tests/ui/match_overlapping_arm.rs:101:9
    |
 LL |         ..26 => println!("..26"),
    |         ^^^^
 
 error: some ranges overlap
-  --> tests/ui/match_overlapping_arm.rs:111:9
+  --> tests/ui/match_overlapping_arm.rs:109:9
    |
 LL |         21..=30 => (),
    |         ^^^^^^^
    |
 note: overlaps with this
-  --> tests/ui/match_overlapping_arm.rs:113:9
+  --> tests/ui/match_overlapping_arm.rs:111:9
    |
 LL |         21..=40 => (),
    |         ^^^^^^^
 
 error: some ranges overlap
-  --> tests/ui/match_overlapping_arm.rs:126:9
+  --> tests/ui/match_overlapping_arm.rs:124:9
    |
 LL |         0..=0x0000_0000_0000_00ff => (),
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: overlaps with this
-  --> tests/ui/match_overlapping_arm.rs:128:9
+  --> tests/ui/match_overlapping_arm.rs:126:9
    |
 LL |         0..=0x0000_0000_0000_ffff => (),
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/needless_doc_main.rs b/tests/ui/needless_doc_main.rs
index fee05926ce4..18adfff8beb 100644
--- a/tests/ui/needless_doc_main.rs
+++ b/tests/ui/needless_doc_main.rs
@@ -10,7 +10,7 @@
 ///     unimplemented!();
 /// }
 /// ```
-///
+/// 
 /// With an explicit return type it should lint too
 /// ```edition2015
 /// fn main() -> () {
@@ -18,7 +18,7 @@
 ///     unimplemented!();
 /// }
 /// ```
-///
+/// 
 /// This should, too.
 /// ```rust
 /// fn main() {
@@ -26,7 +26,7 @@
 ///     unimplemented!();
 /// }
 /// ```
-///
+/// 
 /// This one too.
 /// ```no_run
 /// // the fn is not always the first line
diff --git a/tests/ui/needless_pass_by_value.rs b/tests/ui/needless_pass_by_value.rs
index 14cba5a7eb5..9408b8c948f 100644
--- a/tests/ui/needless_pass_by_value.rs
+++ b/tests/ui/needless_pass_by_value.rs
@@ -84,7 +84,7 @@ trait Serialize {}
 impl<'a, T> Serialize for &'a T where T: Serialize {}
 impl Serialize for i32 {}
 
-fn test_blanket_ref<T: Foo, S: Serialize>(_foo: T, _serializable: S) {}
+fn test_blanket_ref<T: Foo, S: Serialize>(vals: T, serializable: S) {}
 //~^ ERROR: this argument is passed by value, but not consumed in the function body
 
 fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
@@ -116,7 +116,7 @@ impl<T: Serialize, U> S<T, U> {
     ) {
     }
 
-    fn baz(&self, _u: U, _s: Self) {}
+    fn baz(&self, uu: U, ss: Self) {}
     //~^ ERROR: this argument is passed by value, but not consumed in the function body
     //~| ERROR: this argument is passed by value, but not consumed in the function body
 }
@@ -162,13 +162,13 @@ fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) {
 // The following 3 lines should not cause an ICE. See #2831
 trait Bar<'a, A> {}
 impl<'b, T> Bar<'b, T> for T {}
-fn some_fun<'b, S: Bar<'b, ()>>(_item: S) {}
+fn some_fun<'b, S: Bar<'b, ()>>(items: S) {}
 //~^ ERROR: this argument is passed by value, but not consumed in the function body
 
 // Also this should not cause an ICE. See #2831
 trait Club<'a, A> {}
 impl<T> Club<'static, T> for T {}
-fn more_fun(_item: impl Club<'static, i32>) {}
+fn more_fun(items: impl Club<'static, i32>) {}
 //~^ ERROR: this argument is passed by value, but not consumed in the function body
 
 fn is_sync<T>(_: T)
@@ -177,6 +177,10 @@ where
 {
 }
 
+struct Obj(String);
+
+fn prefix_test(_unused_with_prefix: Obj) {}
+
 fn main() {
     // This should not cause an ICE either
     // https://github.com/rust-lang/rust-clippy/issues/3144
diff --git a/tests/ui/needless_pass_by_value.stderr b/tests/ui/needless_pass_by_value.stderr
index 827a200ba68..dce28186ff8 100644
--- a/tests/ui/needless_pass_by_value.stderr
+++ b/tests/ui/needless_pass_by_value.stderr
@@ -46,7 +46,7 @@ LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) {
 error: this argument is passed by value, but not consumed in the function body
   --> tests/ui/needless_pass_by_value.rs:87:49
    |
-LL | fn test_blanket_ref<T: Foo, S: Serialize>(_foo: T, _serializable: S) {}
+LL | fn test_blanket_ref<T: Foo, S: Serialize>(vals: T, serializable: S) {}
    |                                                 ^ help: consider taking a reference instead: `&T`
 
 error: this argument is passed by value, but not consumed in the function body
@@ -106,13 +106,13 @@ LL |         t: String,
 error: this argument is passed by value, but not consumed in the function body
   --> tests/ui/needless_pass_by_value.rs:119:23
    |
-LL |     fn baz(&self, _u: U, _s: Self) {}
+LL |     fn baz(&self, uu: U, ss: Self) {}
    |                       ^ help: consider taking a reference instead: `&U`
 
 error: this argument is passed by value, but not consumed in the function body
   --> tests/ui/needless_pass_by_value.rs:119:30
    |
-LL |     fn baz(&self, _u: U, _s: Self) {}
+LL |     fn baz(&self, uu: U, ss: Self) {}
    |                              ^^^^ help: consider taking a reference instead: `&Self`
 
 error: this argument is passed by value, but not consumed in the function body
@@ -166,13 +166,13 @@ LL | struct CopyWrapper(u32);
 error: this argument is passed by value, but not consumed in the function body
   --> tests/ui/needless_pass_by_value.rs:165:40
    |
-LL | fn some_fun<'b, S: Bar<'b, ()>>(_item: S) {}
+LL | fn some_fun<'b, S: Bar<'b, ()>>(items: S) {}
    |                                        ^ help: consider taking a reference instead: `&S`
 
 error: this argument is passed by value, but not consumed in the function body
   --> tests/ui/needless_pass_by_value.rs:171:20
    |
-LL | fn more_fun(_item: impl Club<'static, i32>) {}
+LL | fn more_fun(items: impl Club<'static, i32>) {}
    |                    ^^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&impl Club<'static, i32>`
 
 error: aborting due to 22 previous errors
diff --git a/tests/ui/pointers_in_nomem_asm_block.rs b/tests/ui/pointers_in_nomem_asm_block.rs
new file mode 100644
index 00000000000..b5abcbb3474
--- /dev/null
+++ b/tests/ui/pointers_in_nomem_asm_block.rs
@@ -0,0 +1,33 @@
+//@ needs-asm-support
+#![warn(clippy::pointers_in_nomem_asm_block)]
+#![crate_type = "lib"]
+#![no_std]
+
+use core::arch::asm;
+
+unsafe fn nomem_bad(p: &i32) {
+    asm!(
+        "asdf {p1}, {p2}, {p3}",
+        p1 = in(reg) p,
+        //~^ ERROR: passing pointers to nomem asm block
+        p2 = in(reg) p as *const _ as usize,
+        p3 = in(reg) p,
+        options(nomem, nostack, preserves_flags)
+    );
+}
+
+unsafe fn nomem_good(p: &i32) {
+    asm!("asdf {p}", p = in(reg) p, options(readonly, nostack, preserves_flags));
+    let p = p as *const i32 as usize;
+    asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags));
+}
+
+unsafe fn nomem_bad2(p: &mut i32) {
+    asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags));
+    //~^ ERROR: passing pointers to nomem asm block
+}
+
+unsafe fn nomem_fn(p: extern "C" fn()) {
+    asm!("call {p}", p = in(reg) p, options(nomem));
+    //~^ ERROR: passing pointers to nomem asm block
+}
diff --git a/tests/ui/pointers_in_nomem_asm_block.stderr b/tests/ui/pointers_in_nomem_asm_block.stderr
new file mode 100644
index 00000000000..cabeb37344f
--- /dev/null
+++ b/tests/ui/pointers_in_nomem_asm_block.stderr
@@ -0,0 +1,34 @@
+error: passing pointers to nomem asm block
+  --> tests/ui/pointers_in_nomem_asm_block.rs:11:9
+   |
+LL |         p1 = in(reg) p,
+   |         ^^^^^^^^^^^^^^
+...
+LL |         p3 = in(reg) p,
+   |         ^^^^^^^^^^^^^^
+   |
+   = note: `nomem` means that no memory write or read happens inside the asm! block
+   = note: if this is intentional and no pointers are read or written to, consider allowing the lint
+   = note: `-D clippy::pointers-in-nomem-asm-block` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::pointers_in_nomem_asm_block)]`
+
+error: passing pointers to nomem asm block
+  --> tests/ui/pointers_in_nomem_asm_block.rs:26:22
+   |
+LL |     asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags));
+   |                      ^^^^^^^^^^^^^
+   |
+   = note: `nomem` means that no memory write or read happens inside the asm! block
+   = note: if this is intentional and no pointers are read or written to, consider allowing the lint
+
+error: passing pointers to nomem asm block
+  --> tests/ui/pointers_in_nomem_asm_block.rs:31:22
+   |
+LL |     asm!("call {p}", p = in(reg) p, options(nomem));
+   |                      ^^^^^^^^^^^^^
+   |
+   = note: `nomem` means that no memory write or read happens inside the asm! block
+   = note: if this is intentional and no pointers are read or written to, consider allowing the lint
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/string_slice.rs b/tests/ui/string_slice.rs
index 1d1911aaa1d..dc519493a4d 100644
--- a/tests/ui/string_slice.rs
+++ b/tests/ui/string_slice.rs
@@ -1,7 +1,7 @@
-use std::borrow::Cow;
+#![warn(clippy::string_slice)]
+#![allow(clippy::no_effect)]
 
-#[warn(clippy::string_slice)]
-#[allow(clippy::no_effect)]
+use std::borrow::Cow;
 
 fn main() {
     &"Ölkanne"[1..];
diff --git a/tests/ui/suspicious_command_arg_space.fixed b/tests/ui/suspicious_command_arg_space.fixed
index 5d7b1e0c17f..704d6ea1bb8 100644
--- a/tests/ui/suspicious_command_arg_space.fixed
+++ b/tests/ui/suspicious_command_arg_space.fixed
@@ -1,3 +1,4 @@
+#![allow(clippy::zombie_processes)]
 fn main() {
     // Things it should warn about:
     std::process::Command::new("echo").args(["-n", "hello"]).spawn().unwrap();
diff --git a/tests/ui/suspicious_command_arg_space.rs b/tests/ui/suspicious_command_arg_space.rs
index 8abd9803a0c..2a2a7557381 100644
--- a/tests/ui/suspicious_command_arg_space.rs
+++ b/tests/ui/suspicious_command_arg_space.rs
@@ -1,3 +1,4 @@
+#![allow(clippy::zombie_processes)]
 fn main() {
     // Things it should warn about:
     std::process::Command::new("echo").arg("-n hello").spawn().unwrap();
diff --git a/tests/ui/suspicious_command_arg_space.stderr b/tests/ui/suspicious_command_arg_space.stderr
index d2517b66b56..6fd07d07d7b 100644
--- a/tests/ui/suspicious_command_arg_space.stderr
+++ b/tests/ui/suspicious_command_arg_space.stderr
@@ -1,5 +1,5 @@
 error: single argument that looks like it should be multiple arguments
-  --> tests/ui/suspicious_command_arg_space.rs:3:44
+  --> tests/ui/suspicious_command_arg_space.rs:4:44
    |
 LL |     std::process::Command::new("echo").arg("-n hello").spawn().unwrap();
    |                                            ^^^^^^^^^^
@@ -12,7 +12,7 @@ LL |     std::process::Command::new("echo").args(["-n", "hello"]).spawn().unwrap
    |                                        ~~~~ ~~~~~~~~~~~~~~~
 
 error: single argument that looks like it should be multiple arguments
-  --> tests/ui/suspicious_command_arg_space.rs:6:43
+  --> tests/ui/suspicious_command_arg_space.rs:7:43
    |
 LL |     std::process::Command::new("cat").arg("--number file").spawn().unwrap();
    |                                           ^^^^^^^^^^^^^^^
diff --git a/tests/ui/tabs_in_doc_comments.fixed b/tests/ui/tabs_in_doc_comments.fixed
index 26cc5c27e88..3536c1746df 100644
--- a/tests/ui/tabs_in_doc_comments.fixed
+++ b/tests/ui/tabs_in_doc_comments.fixed
@@ -1,5 +1,4 @@
 #![warn(clippy::tabs_in_doc_comments)]
-#[allow(dead_code)]
 
 ///
 /// Struct to hold two strings:
diff --git a/tests/ui/tabs_in_doc_comments.rs b/tests/ui/tabs_in_doc_comments.rs
index 14b06966ecc..033a685066e 100644
--- a/tests/ui/tabs_in_doc_comments.rs
+++ b/tests/ui/tabs_in_doc_comments.rs
@@ -1,5 +1,4 @@
 #![warn(clippy::tabs_in_doc_comments)]
-#[allow(dead_code)]
 
 ///
 /// Struct to hold two strings:
diff --git a/tests/ui/tabs_in_doc_comments.stderr b/tests/ui/tabs_in_doc_comments.stderr
index aef6c391452..f8d30b728e5 100644
--- a/tests/ui/tabs_in_doc_comments.stderr
+++ b/tests/ui/tabs_in_doc_comments.stderr
@@ -1,5 +1,5 @@
 error: using tabs in doc comments is not recommended
-  --> tests/ui/tabs_in_doc_comments.rs:6:5
+  --> tests/ui/tabs_in_doc_comments.rs:5:5
    |
 LL | ///     - first        one
    |     ^^^^ help: consider using four spaces per tab
@@ -8,43 +8,43 @@ LL | ///     - first        one
    = help: to override `-D warnings` add `#[allow(clippy::tabs_in_doc_comments)]`
 
 error: using tabs in doc comments is not recommended
-  --> tests/ui/tabs_in_doc_comments.rs:6:13
+  --> tests/ui/tabs_in_doc_comments.rs:5:13
    |
 LL | ///     - first        one
    |                ^^^^^^^^ help: consider using four spaces per tab
 
 error: using tabs in doc comments is not recommended
-  --> tests/ui/tabs_in_doc_comments.rs:7:5
+  --> tests/ui/tabs_in_doc_comments.rs:6:5
    |
 LL | ///     - second    one
    |     ^^^^ help: consider using four spaces per tab
 
 error: using tabs in doc comments is not recommended
-  --> tests/ui/tabs_in_doc_comments.rs:7:14
+  --> tests/ui/tabs_in_doc_comments.rs:6:14
    |
 LL | ///     - second    one
    |                 ^^^^ help: consider using four spaces per tab
 
 error: using tabs in doc comments is not recommended
-  --> tests/ui/tabs_in_doc_comments.rs:10:9
+  --> tests/ui/tabs_in_doc_comments.rs:9:9
    |
 LL |     ///     - First String:
    |         ^^^^ help: consider using four spaces per tab
 
 error: using tabs in doc comments is not recommended
-  --> tests/ui/tabs_in_doc_comments.rs:11:9
+  --> tests/ui/tabs_in_doc_comments.rs:10:9
    |
 LL |     ///         - needs to be inside here
    |         ^^^^^^^^ help: consider using four spaces per tab
 
 error: using tabs in doc comments is not recommended
-  --> tests/ui/tabs_in_doc_comments.rs:14:9
+  --> tests/ui/tabs_in_doc_comments.rs:13:9
    |
 LL |     ///     - Second String:
    |         ^^^^ help: consider using four spaces per tab
 
 error: using tabs in doc comments is not recommended
-  --> tests/ui/tabs_in_doc_comments.rs:15:9
+  --> tests/ui/tabs_in_doc_comments.rs:14:9
    |
 LL |     ///         - needs to be inside here
    |         ^^^^^^^^ help: consider using four spaces per tab
diff --git a/tests/ui/temporary_assignment.rs b/tests/ui/temporary_assignment.rs
index 383e70be925..e2b982aeaf5 100644
--- a/tests/ui/temporary_assignment.rs
+++ b/tests/ui/temporary_assignment.rs
@@ -1,5 +1,4 @@
 #![warn(clippy::temporary_assignment)]
-#![allow(const_item_mutation)]
 
 use std::ops::{Deref, DerefMut};
 
diff --git a/tests/ui/temporary_assignment.stderr b/tests/ui/temporary_assignment.stderr
index 8c284594075..1b6007f0b98 100644
--- a/tests/ui/temporary_assignment.stderr
+++ b/tests/ui/temporary_assignment.stderr
@@ -1,5 +1,5 @@
 error: assignment to temporary
-  --> tests/ui/temporary_assignment.rs:48:5
+  --> tests/ui/temporary_assignment.rs:47:5
    |
 LL |     Struct { field: 0 }.field = 1;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL |     Struct { field: 0 }.field = 1;
    = help: to override `-D warnings` add `#[allow(clippy::temporary_assignment)]`
 
 error: assignment to temporary
-  --> tests/ui/temporary_assignment.rs:51:5
+  --> tests/ui/temporary_assignment.rs:50:5
    |
 LL | /     MultiStruct {
 LL | |
@@ -19,13 +19,13 @@ LL | |     .field = 1;
    | |______________^
 
 error: assignment to temporary
-  --> tests/ui/temporary_assignment.rs:57:5
+  --> tests/ui/temporary_assignment.rs:56:5
    |
 LL |     ArrayStruct { array: [0] }.array[0] = 1;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: assignment to temporary
-  --> tests/ui/temporary_assignment.rs:59:5
+  --> tests/ui/temporary_assignment.rs:58:5
    |
 LL |     (0, 0).0 = 1;
    |     ^^^^^^^^^^^^
diff --git a/tests/ui/to_string_in_format_args_incremental.fixed b/tests/ui/to_string_in_format_args_incremental.fixed
new file mode 100644
index 00000000000..1f789579636
--- /dev/null
+++ b/tests/ui/to_string_in_format_args_incremental.fixed
@@ -0,0 +1,8 @@
+//@compile-flags: -C incremental=target/debug/test/incr
+
+// see https://github.com/rust-lang/rust-clippy/issues/10969
+
+fn main() {
+    let s = "Hello, world!";
+    println!("{}", s);
+}
diff --git a/tests/ui/to_string_in_format_args_incremental.rs b/tests/ui/to_string_in_format_args_incremental.rs
new file mode 100644
index 00000000000..514febe8c92
--- /dev/null
+++ b/tests/ui/to_string_in_format_args_incremental.rs
@@ -0,0 +1,8 @@
+//@compile-flags: -C incremental=target/debug/test/incr
+
+// see https://github.com/rust-lang/rust-clippy/issues/10969
+
+fn main() {
+    let s = "Hello, world!";
+    println!("{}", s.to_string());
+}
diff --git a/tests/ui/to_string_in_format_args_incremental.stderr b/tests/ui/to_string_in_format_args_incremental.stderr
new file mode 100644
index 00000000000..535dd21ea58
--- /dev/null
+++ b/tests/ui/to_string_in_format_args_incremental.stderr
@@ -0,0 +1,11 @@
+error: `to_string` applied to a type that implements `Display` in `println!` args
+  --> tests/ui/to_string_in_format_args_incremental.rs:7:21
+   |
+LL |     println!("{}", s.to_string());
+   |                     ^^^^^^^^^^^^ help: remove this
+   |
+   = note: `-D clippy::to-string-in-format-args` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::to_string_in_format_args)]`
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/unnecessary_min_or_max.fixed b/tests/ui/unnecessary_min_or_max.fixed
index 392f6dd1fee..1f3e131516c 100644
--- a/tests/ui/unnecessary_min_or_max.fixed
+++ b/tests/ui/unnecessary_min_or_max.fixed
@@ -65,3 +65,32 @@ fn random_u32() -> u32 {
     // random number generator
     0
 }
+
+struct Issue13191 {
+    min: u16,
+    max: u16,
+}
+
+impl Issue13191 {
+    fn new() -> Self {
+        Self { min: 0, max: 0 }
+    }
+
+    fn min(mut self, value: u16) -> Self {
+        self.min = value;
+        self
+    }
+
+    fn max(mut self, value: u16) -> Self {
+        self.max = value;
+        self
+    }
+}
+
+fn issue_13191() {
+    // should not fixed
+    Issue13191::new().min(0);
+
+    // should not fixed
+    Issue13191::new().max(0);
+}
diff --git a/tests/ui/unnecessary_min_or_max.rs b/tests/ui/unnecessary_min_or_max.rs
index b03755e6d23..58356b9d49e 100644
--- a/tests/ui/unnecessary_min_or_max.rs
+++ b/tests/ui/unnecessary_min_or_max.rs
@@ -65,3 +65,32 @@ fn random_u32() -> u32 {
     // random number generator
     0
 }
+
+struct Issue13191 {
+    min: u16,
+    max: u16,
+}
+
+impl Issue13191 {
+    fn new() -> Self {
+        Self { min: 0, max: 0 }
+    }
+
+    fn min(mut self, value: u16) -> Self {
+        self.min = value;
+        self
+    }
+
+    fn max(mut self, value: u16) -> Self {
+        self.max = value;
+        self
+    }
+}
+
+fn issue_13191() {
+    // should not fixed
+    Issue13191::new().min(0);
+
+    // should not fixed
+    Issue13191::new().max(0);
+}
diff --git a/tests/ui/used_underscore_binding.stderr b/tests/ui/used_underscore_binding.stderr
index 556e1792b3e..f9e8013d3ad 100644
--- a/tests/ui/used_underscore_binding.stderr
+++ b/tests/ui/used_underscore_binding.stderr
@@ -1,10 +1,10 @@
-error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
+error: used underscore-prefixed binding
   --> tests/ui/used_underscore_binding.rs:23:5
    |
 LL |     _foo + 1
    |     ^^^^
    |
-note: `_foo` is defined here
+note: binding is defined here
   --> tests/ui/used_underscore_binding.rs:22:22
    |
 LL | fn prefix_underscore(_foo: u32) -> u32 {
@@ -12,61 +12,61 @@ LL | fn prefix_underscore(_foo: u32) -> u32 {
    = note: `-D clippy::used-underscore-binding` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::used_underscore_binding)]`
 
-error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
+error: used underscore-prefixed binding
   --> tests/ui/used_underscore_binding.rs:28:20
    |
 LL |     println!("{}", _foo);
    |                    ^^^^
    |
-note: `_foo` is defined here
+note: binding is defined here
   --> tests/ui/used_underscore_binding.rs:27:24
    |
 LL | fn in_macro_or_desugar(_foo: u32) {
    |                        ^^^^
 
-error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
+error: used underscore-prefixed binding
   --> tests/ui/used_underscore_binding.rs:29:16
    |
 LL |     assert_eq!(_foo, _foo);
    |                ^^^^
    |
-note: `_foo` is defined here
+note: binding is defined here
   --> tests/ui/used_underscore_binding.rs:27:24
    |
 LL | fn in_macro_or_desugar(_foo: u32) {
    |                        ^^^^
 
-error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
+error: used underscore-prefixed binding
   --> tests/ui/used_underscore_binding.rs:29:22
    |
 LL |     assert_eq!(_foo, _foo);
    |                      ^^^^
    |
-note: `_foo` is defined here
+note: binding is defined here
   --> tests/ui/used_underscore_binding.rs:27:24
    |
 LL | fn in_macro_or_desugar(_foo: u32) {
    |                        ^^^^
 
-error: used binding `_underscore_field` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
+error: used underscore-prefixed binding
   --> tests/ui/used_underscore_binding.rs:42:5
    |
 LL |     s._underscore_field += 1;
    |     ^^^^^^^^^^^^^^^^^^^
    |
-note: `_underscore_field` is defined here
+note: binding is defined here
   --> tests/ui/used_underscore_binding.rs:36:5
    |
 LL |     _underscore_field: u32,
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
-error: used binding `_i` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
+error: used underscore-prefixed binding
   --> tests/ui/used_underscore_binding.rs:103:16
    |
 LL |         uses_i(_i);
    |                ^^
    |
-note: `_i` is defined here
+note: binding is defined here
   --> tests/ui/used_underscore_binding.rs:102:13
    |
 LL |         let _i = 5;
diff --git a/tests/ui/used_underscore_items.rs b/tests/ui/used_underscore_items.rs
new file mode 100644
index 00000000000..223016a5c96
--- /dev/null
+++ b/tests/ui/used_underscore_items.rs
@@ -0,0 +1,63 @@
+//@aux-build:external_item.rs
+#![allow(unused)]
+#![warn(clippy::used_underscore_items)]
+
+extern crate external_item;
+
+// should not lint macro
+macro_rules! macro_wrap_func {
+    () => {
+        fn _marco_foo() {}
+    };
+}
+
+macro_wrap_func!();
+
+struct _FooStruct {}
+
+impl _FooStruct {
+    fn _method_call(self) {}
+}
+
+fn _foo1() {}
+
+fn _foo2() -> i32 {
+    0
+}
+
+mod a {
+    pub mod b {
+        pub mod c {
+            pub fn _foo3() {}
+
+            pub struct _FooStruct2 {}
+
+            impl _FooStruct2 {
+                pub fn _method_call(self) {}
+            }
+        }
+    }
+}
+
+fn main() {
+    _foo1();
+    let _ = _foo2();
+    a::b::c::_foo3();
+    let _ = &_FooStruct {};
+    let _ = _FooStruct {};
+
+    let foo_struct = _FooStruct {};
+    foo_struct._method_call();
+
+    let foo_struct2 = a::b::c::_FooStruct2 {};
+    foo_struct2._method_call();
+}
+
+// should not lint exteranl crate.
+// user cannot control how others name their items
+fn external_item_call() {
+    let foo_struct3 = external_item::_ExternalStruct {};
+    foo_struct3._foo();
+
+    external_item::_exernal_foo();
+}
diff --git a/tests/ui/used_underscore_items.stderr b/tests/ui/used_underscore_items.stderr
new file mode 100644
index 00000000000..93ac3a6fec6
--- /dev/null
+++ b/tests/ui/used_underscore_items.stderr
@@ -0,0 +1,112 @@
+error: used underscore-prefixed item
+  --> tests/ui/used_underscore_items.rs:43:5
+   |
+LL |     _foo1();
+   |     ^^^^^^^
+   |
+note: item is defined here
+  --> tests/ui/used_underscore_items.rs:22:1
+   |
+LL | fn _foo1() {}
+   | ^^^^^^^^^^
+   = note: `-D clippy::used-underscore-items` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::used_underscore_items)]`
+
+error: used underscore-prefixed item
+  --> tests/ui/used_underscore_items.rs:44:13
+   |
+LL |     let _ = _foo2();
+   |             ^^^^^^^
+   |
+note: item is defined here
+  --> tests/ui/used_underscore_items.rs:24:1
+   |
+LL | fn _foo2() -> i32 {
+   | ^^^^^^^^^^^^^^^^^
+
+error: used underscore-prefixed item
+  --> tests/ui/used_underscore_items.rs:45:5
+   |
+LL |     a::b::c::_foo3();
+   |     ^^^^^^^^^^^^^^^^
+   |
+note: item is defined here
+  --> tests/ui/used_underscore_items.rs:31:13
+   |
+LL |             pub fn _foo3() {}
+   |             ^^^^^^^^^^^^^^
+
+error: used underscore-prefixed item
+  --> tests/ui/used_underscore_items.rs:46:14
+   |
+LL |     let _ = &_FooStruct {};
+   |              ^^^^^^^^^^^^^
+   |
+note: item is defined here
+  --> tests/ui/used_underscore_items.rs:16:1
+   |
+LL | struct _FooStruct {}
+   | ^^^^^^^^^^^^^^^^^
+
+error: used underscore-prefixed item
+  --> tests/ui/used_underscore_items.rs:47:13
+   |
+LL |     let _ = _FooStruct {};
+   |             ^^^^^^^^^^^^^
+   |
+note: item is defined here
+  --> tests/ui/used_underscore_items.rs:16:1
+   |
+LL | struct _FooStruct {}
+   | ^^^^^^^^^^^^^^^^^
+
+error: used underscore-prefixed item
+  --> tests/ui/used_underscore_items.rs:49:22
+   |
+LL |     let foo_struct = _FooStruct {};
+   |                      ^^^^^^^^^^^^^
+   |
+note: item is defined here
+  --> tests/ui/used_underscore_items.rs:16:1
+   |
+LL | struct _FooStruct {}
+   | ^^^^^^^^^^^^^^^^^
+
+error: used underscore-prefixed item
+  --> tests/ui/used_underscore_items.rs:50:5
+   |
+LL |     foo_struct._method_call();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: item is defined here
+  --> tests/ui/used_underscore_items.rs:19:5
+   |
+LL |     fn _method_call(self) {}
+   |     ^^^^^^^^^^^^^^^^^^^^^
+
+error: used underscore-prefixed item
+  --> tests/ui/used_underscore_items.rs:52:23
+   |
+LL |     let foo_struct2 = a::b::c::_FooStruct2 {};
+   |                       ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: item is defined here
+  --> tests/ui/used_underscore_items.rs:33:13
+   |
+LL |             pub struct _FooStruct2 {}
+   |             ^^^^^^^^^^^^^^^^^^^^^^
+
+error: used underscore-prefixed item
+  --> tests/ui/used_underscore_items.rs:53:5
+   |
+LL |     foo_struct2._method_call();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: item is defined here
+  --> tests/ui/used_underscore_items.rs:36:17
+   |
+LL |                 pub fn _method_call(self) {}
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 9 previous errors
+
diff --git a/tests/ui/zombie_processes.rs b/tests/ui/zombie_processes.rs
new file mode 100644
index 00000000000..a2abc7fc3a1
--- /dev/null
+++ b/tests/ui/zombie_processes.rs
@@ -0,0 +1,138 @@
+#![warn(clippy::zombie_processes)]
+#![allow(clippy::if_same_then_else, clippy::ifs_same_cond)]
+
+use std::process::{Child, Command};
+
+fn main() {
+    {
+        // Check that #[expect] works
+        #[expect(clippy::zombie_processes)]
+        let mut x = Command::new("").spawn().unwrap();
+    }
+
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        //~^ ERROR: spawned process is never `wait()`ed on
+        x.kill();
+        x.id();
+    }
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        x.wait().unwrap(); // OK
+    }
+    {
+        let x = Command::new("").spawn().unwrap();
+        x.wait_with_output().unwrap(); // OK
+    }
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        x.try_wait().unwrap(); // OK
+    }
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        let mut r = &mut x;
+        r.wait().unwrap(); // OK, not calling `.wait()` directly on `x` but through `r` -> `x`
+    }
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        process_child(x); // OK, other function might call `.wait()` so assume it does
+    }
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        //~^ ERROR: spawned process is never `wait()`ed on
+        let v = &x;
+        // (allow shared refs is fine because one cannot call `.wait()` through that)
+    }
+
+    // https://github.com/rust-lang/rust-clippy/pull/11476#issuecomment-1718456033
+    // Unconditionally exiting the process in various ways (should not lint)
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        std::process::exit(0);
+    }
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        std::process::abort(); // same as above, but abort instead of exit
+    }
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        if true { /* nothing */ }
+        std::process::abort(); // still unconditionally exits
+    }
+
+    // Conditionally exiting
+    // It should assume that it might not exit and still lint
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        //~^ ERROR: spawned process is never `wait()`ed on
+        if true {
+            std::process::exit(0);
+        }
+    }
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        //~^ ERROR: spawned process is never `wait()`ed on
+        if true {
+            while false {}
+            // Calling `exit()` after leaving a while loop should still be linted.
+            std::process::exit(0);
+        }
+    }
+
+    {
+        let mut x = { Command::new("").spawn().unwrap() };
+        x.wait().unwrap();
+    }
+
+    {
+        struct S {
+            c: Child,
+        }
+
+        let mut s = S {
+            c: Command::new("").spawn().unwrap(),
+        };
+        s.c.wait().unwrap();
+    }
+
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        //~^ ERROR: spawned process is never `wait()`ed on
+        if true {
+            return;
+        }
+        x.wait().unwrap();
+    }
+
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        //~^ ERROR: spawned process is never `wait()`ed on
+        if true {
+            x.wait().unwrap();
+        }
+    }
+
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        if true {
+            x.wait().unwrap();
+        } else if true {
+            x.wait().unwrap();
+        } else {
+            x.wait().unwrap();
+        }
+    }
+
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        if true {
+            x.wait().unwrap();
+            return;
+        }
+        x.wait().unwrap();
+    }
+}
+
+fn process_child(c: Child) {
+    todo!()
+}
diff --git a/tests/ui/zombie_processes.stderr b/tests/ui/zombie_processes.stderr
new file mode 100644
index 00000000000..eec821a4c8f
--- /dev/null
+++ b/tests/ui/zombie_processes.stderr
@@ -0,0 +1,64 @@
+error: spawned process is never `wait()`ed on
+  --> tests/ui/zombie_processes.rs:14:21
+   |
+LL |         let mut x = Command::new("").spawn().unwrap();
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: consider calling `.wait()`
+   = note: not doing so might leave behind zombie processes
+   = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning
+   = note: `-D clippy::zombie-processes` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::zombie_processes)]`
+
+error: spawned process is never `wait()`ed on
+  --> tests/ui/zombie_processes.rs:41:21
+   |
+LL |         let mut x = Command::new("").spawn().unwrap();
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: consider calling `.wait()`
+   = note: not doing so might leave behind zombie processes
+   = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning
+
+error: spawned process is never `wait()`ed on
+  --> tests/ui/zombie_processes.rs:66:21
+   |
+LL |         let mut x = Command::new("").spawn().unwrap();
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: consider calling `.wait()`
+   = note: not doing so might leave behind zombie processes
+   = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning
+
+error: spawned process is never `wait()`ed on
+  --> tests/ui/zombie_processes.rs:73:21
+   |
+LL |         let mut x = Command::new("").spawn().unwrap();
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: consider calling `.wait()`
+   = note: not doing so might leave behind zombie processes
+   = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning
+
+error: spawned process is never `wait()`ed on
+  --> tests/ui/zombie_processes.rs:99:21
+   |
+LL |         let mut x = Command::new("").spawn().unwrap();
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: consider calling `.wait()`
+   = note: not doing so might leave behind zombie processes
+   = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning
+
+error: spawned process is never `wait()`ed on
+  --> tests/ui/zombie_processes.rs:108:21
+   |
+LL |         let mut x = Command::new("").spawn().unwrap();
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: consider calling `.wait()`
+   = note: not doing so might leave behind zombie processes
+   = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning
+
+error: aborting due to 6 previous errors
+
diff --git a/tests/ui/zombie_processes_fixable.fixed b/tests/ui/zombie_processes_fixable.fixed
new file mode 100644
index 00000000000..6045262f519
--- /dev/null
+++ b/tests/ui/zombie_processes_fixable.fixed
@@ -0,0 +1,26 @@
+#![warn(clippy::zombie_processes)]
+#![allow(clippy::needless_return)]
+
+use std::process::{Child, Command};
+
+fn main() {
+    let _ = Command::new("").spawn().unwrap().wait();
+    //~^ ERROR: spawned process is never `wait()`ed on
+    Command::new("").spawn().unwrap().wait();
+    //~^ ERROR: spawned process is never `wait()`ed on
+    spawn_proc().wait();
+    //~^ ERROR: spawned process is never `wait()`ed on
+    spawn_proc().wait().unwrap(); // OK
+}
+
+fn not_main() {
+    Command::new("").spawn().unwrap().wait();
+}
+
+fn spawn_proc() -> Child {
+    Command::new("").spawn().unwrap()
+}
+
+fn spawn_proc_2() -> Child {
+    return Command::new("").spawn().unwrap();
+}
diff --git a/tests/ui/zombie_processes_fixable.rs b/tests/ui/zombie_processes_fixable.rs
new file mode 100644
index 00000000000..e1ecb771641
--- /dev/null
+++ b/tests/ui/zombie_processes_fixable.rs
@@ -0,0 +1,26 @@
+#![warn(clippy::zombie_processes)]
+#![allow(clippy::needless_return)]
+
+use std::process::{Child, Command};
+
+fn main() {
+    let _ = Command::new("").spawn().unwrap();
+    //~^ ERROR: spawned process is never `wait()`ed on
+    Command::new("").spawn().unwrap();
+    //~^ ERROR: spawned process is never `wait()`ed on
+    spawn_proc();
+    //~^ ERROR: spawned process is never `wait()`ed on
+    spawn_proc().wait().unwrap(); // OK
+}
+
+fn not_main() {
+    Command::new("").spawn().unwrap();
+}
+
+fn spawn_proc() -> Child {
+    Command::new("").spawn().unwrap()
+}
+
+fn spawn_proc_2() -> Child {
+    return Command::new("").spawn().unwrap();
+}
diff --git a/tests/ui/zombie_processes_fixable.stderr b/tests/ui/zombie_processes_fixable.stderr
new file mode 100644
index 00000000000..e1c40472c32
--- /dev/null
+++ b/tests/ui/zombie_processes_fixable.stderr
@@ -0,0 +1,40 @@
+error: spawned process is never `wait()`ed on
+  --> tests/ui/zombie_processes_fixable.rs:7:13
+   |
+LL |     let _ = Command::new("").spawn().unwrap();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- help: try: `.wait()`
+   |
+   = note: not doing so might leave behind zombie processes
+   = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning
+   = note: `-D clippy::zombie-processes` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::zombie_processes)]`
+
+error: spawned process is never `wait()`ed on
+  --> tests/ui/zombie_processes_fixable.rs:9:5
+   |
+LL |     Command::new("").spawn().unwrap();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- help: try: `.wait()`
+   |
+   = note: not doing so might leave behind zombie processes
+   = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning
+
+error: spawned process is never `wait()`ed on
+  --> tests/ui/zombie_processes_fixable.rs:11:5
+   |
+LL |     spawn_proc();
+   |     ^^^^^^^^^^^^- help: try: `.wait()`
+   |
+   = note: not doing so might leave behind zombie processes
+   = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning
+
+error: spawned process is never `wait()`ed on
+  --> tests/ui/zombie_processes_fixable.rs:17:5
+   |
+LL |     Command::new("").spawn().unwrap();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- help: try: `.wait()`
+   |
+   = note: not doing so might leave behind zombie processes
+   = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning
+
+error: aborting due to 4 previous errors
+