about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bootstrap/bootstrap.py1
-rw-r--r--src/bootstrap/compile.rs6
-rw-r--r--src/bootstrap/lib.rs3
-rw-r--r--src/bootstrap/llvm.rs2
-rw-r--r--src/bootstrap/test.rs5
-rw-r--r--src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version2
m---------src/doc/book0
m---------src/doc/reference0
m---------src/doc/rust-by-example0
m---------src/doc/rustc-dev-guide0
-rw-r--r--src/doc/rustc/src/SUMMARY.md1
-rw-r--r--src/doc/rustc/src/platform-support.md1
-rw-r--r--src/doc/rustc/src/platform-support/loongarch-linux.md92
-rwxr-xr-xsrc/etc/rust-gdb4
-rwxr-xr-xsrc/etc/rust-gdbgui6
-rw-r--r--src/librustdoc/clean/cfg.rs1
-rw-r--r--src/librustdoc/clean/mod.rs3
-rw-r--r--src/librustdoc/clean/types.rs2
m---------src/tools/cargo0
-rw-r--r--src/tools/clippy/.cargo/config.toml3
-rw-r--r--src/tools/clippy/.editorconfig1
-rw-r--r--src/tools/clippy/.github/workflows/clippy_bors.yml2
-rw-r--r--src/tools/clippy/.github/workflows/remark.yml2
-rw-r--r--src/tools/clippy/CHANGELOG.md7
-rw-r--r--src/tools/clippy/README.md6
-rw-r--r--src/tools/clippy/book/src/README.md2
-rw-r--r--src/tools/clippy/book/src/SUMMARY.md1
-rw-r--r--src/tools/clippy/book/src/configuration.md29
-rw-r--r--src/tools/clippy/book/src/development/README.md2
-rw-r--r--src/tools/clippy/book/src/development/adding_lints.md32
-rw-r--r--src/tools/clippy/book/src/development/basics.md6
-rw-r--r--src/tools/clippy/book/src/development/common_tools_writing_lints.md14
-rw-r--r--src/tools/clippy/book/src/development/infrastructure/book.md4
-rw-r--r--src/tools/clippy/book/src/development/infrastructure/changelog_update.md2
-rw-r--r--src/tools/clippy/book/src/development/infrastructure/release.md2
-rw-r--r--src/tools/clippy/book/src/development/infrastructure/sync.md13
-rw-r--r--src/tools/clippy/book/src/development/proposals/README.md2
-rw-r--r--src/tools/clippy/book/src/development/proposals/roadmap-2021.md6
-rw-r--r--src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md28
-rw-r--r--src/tools/clippy/book/src/development/type_checking.md144
-rw-r--r--src/tools/clippy/book/src/installation.md4
-rw-r--r--src/tools/clippy/book/src/lint_configuration.md18
-rw-r--r--src/tools/clippy/book/src/lints.md10
-rw-r--r--src/tools/clippy/book/src/usage.md4
-rw-r--r--src/tools/clippy/clippy_dev/src/lib.rs1
-rw-r--r--src/tools/clippy/clippy_dev/src/new_lint.rs8
-rw-r--r--src/tools/clippy/clippy_dev/src/update_lints.rs14
-rw-r--r--src/tools/clippy/clippy_lints/Cargo.toml1
-rw-r--r--src/tools/clippy/clippy_lints/src/booleans.rs39
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs42
-rw-r--r--src/tools/clippy/clippy_lints/src/collection_is_never_read.rs39
-rw-r--r--src/tools/clippy/clippy_lints/src/declared_lints.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/explicit_write.rs29
-rw-r--r--src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs177
-rw-r--r--src/tools/clippy/clippy_lints/src/format.rs91
-rw-r--r--src/tools/clippy/clippy_lints/src/format_args.rs224
-rw-r--r--src/tools/clippy/clippy_lints/src/format_impl.rs60
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/must_use.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/items_after_statements.rs31
-rw-r--r--src/tools/clippy/clippy_lints/src/large_futures.rs87
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs36
-rw-r--r--src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs100
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs93
-rw-r--r--src/tools/clippy/clippy_lints/src/mem_replace.rs71
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs53
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs27
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs24
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs40
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_async_block.rs145
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/returns.rs32
-rw-r--r--src/tools/clippy/clippy_lints/src/single_component_path_imports.rs59
-rw-r--r--src/tools/clippy/clippy_lints/src/suspicious_doc_comments.rs94
-rw-r--r--src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs71
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs120
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/use_self.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/conf.rs36
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs79
-rw-r--r--src/tools/clippy/clippy_lints/src/write.rs14
-rw-r--r--src/tools/clippy/clippy_utils/src/ast_utils.rs39
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs72
-rw-r--r--src/tools/clippy/clippy_utils/src/macros.rs800
-rw-r--r--src/tools/clippy/clippy_utils/src/paths.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs22
-rw-r--r--src/tools/clippy/etc/relicense/RELICENSE_DOCUMENTATION.md4
-rw-r--r--src/tools/clippy/lintcheck/README.md2
-rw-r--r--src/tools/clippy/rust-toolchain2
-rw-r--r--src/tools/clippy/src/driver.rs2
-rw-r--r--src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/src/main.stderr6
-rw-r--r--src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr22
-rw-r--r--src/tools/clippy/tests/ui-toml/extra_unused_type_parameters/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/extra_unused_type_parameters/extra_unused_type_parameters.rs9
-rw-r--r--src/tools/clippy/tests/ui-toml/large_futures/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/large_futures/large_futures.rs27
-rw-r--r--src/tools/clippy/tests/ui-toml/large_futures/large_futures.stderr10
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr1
-rw-r--r--src/tools/clippy/tests/ui/arithmetic_side_effects.rs4
-rw-r--r--src/tools/clippy/tests/ui/arithmetic_side_effects.stderr14
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/proc_macros.rs2
-rw-r--r--src/tools/clippy/tests/ui/cast.rs6
-rw-r--r--src/tools/clippy/tests/ui/cast.stderr112
-rw-r--r--src/tools/clippy/tests/ui/clear_with_drain.fixed358
-rw-r--r--src/tools/clippy/tests/ui/clear_with_drain.rs358
-rw-r--r--src/tools/clippy/tests/ui/clear_with_drain.stderr130
-rw-r--r--src/tools/clippy/tests/ui/collection_is_never_read.rs31
-rw-r--r--src/tools/clippy/tests/ui/collection_is_never_read.stderr34
-rw-r--r--src/tools/clippy/tests/ui/double_must_use.rs11
-rw-r--r--src/tools/clippy/tests/ui/double_must_use.stderr10
-rw-r--r--src/tools/clippy/tests/ui/extra_unused_type_parameters.fixed105
-rw-r--r--src/tools/clippy/tests/ui/extra_unused_type_parameters.rs11
-rw-r--r--src/tools/clippy/tests/ui/extra_unused_type_parameters.stderr79
-rw-r--r--src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.rs24
-rw-r--r--src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.stderr27
-rw-r--r--src/tools/clippy/tests/ui/format_args_unfixable.rs44
-rw-r--r--src/tools/clippy/tests/ui/format_args_unfixable.stderr36
-rw-r--r--src/tools/clippy/tests/ui/items_after_statement.rs (renamed from src/tools/clippy/tests/ui/item_after_statement.rs)17
-rw-r--r--src/tools/clippy/tests/ui/items_after_statement.stderr (renamed from src/tools/clippy/tests/ui/item_after_statement.stderr)6
-rw-r--r--src/tools/clippy/tests/ui/large_futures.rs61
-rw-r--r--src/tools/clippy/tests/ui/large_futures.stderr82
-rw-r--r--src/tools/clippy/tests/ui/lines_filter_map_ok.fixed29
-rw-r--r--src/tools/clippy/tests/ui/lines_filter_map_ok.rs29
-rw-r--r--src/tools/clippy/tests/ui/lines_filter_map_ok.stderr51
-rw-r--r--src/tools/clippy/tests/ui/manual_slice_size_calculation.rs36
-rw-r--r--src/tools/clippy/tests/ui/manual_slice_size_calculation.stderr51
-rw-r--r--src/tools/clippy/tests/ui/mem_replace.fixed34
-rw-r--r--src/tools/clippy/tests/ui/mem_replace.rs34
-rw-r--r--src/tools/clippy/tests/ui/mem_replace.stderr26
-rw-r--r--src/tools/clippy/tests/ui/needless_return.fixed9
-rw-r--r--src/tools/clippy/tests/ui/needless_return.rs9
-rw-r--r--src/tools/clippy/tests/ui/nonminimal_bool.rs18
-rw-r--r--src/tools/clippy/tests/ui/print_literal.rs4
-rw-r--r--src/tools/clippy/tests/ui/redundant_async_block.fixed148
-rw-r--r--src/tools/clippy/tests/ui/redundant_async_block.rs148
-rw-r--r--src/tools/clippy/tests/ui/redundant_async_block.stderr46
-rw-r--r--src/tools/clippy/tests/ui/single_component_path_imports.fixed6
-rw-r--r--src/tools/clippy/tests/ui/single_component_path_imports.rs6
-rw-r--r--src/tools/clippy/tests/ui/single_component_path_imports.stderr4
-rw-r--r--src/tools/clippy/tests/ui/suspicious_doc_comments.fixed81
-rw-r--r--src/tools/clippy/tests/ui/suspicious_doc_comments.rs81
-rw-r--r--src/tools/clippy/tests/ui/suspicious_doc_comments.stderr114
-rw-r--r--src/tools/clippy/tests/ui/suspicious_doc_comments_unfixable.rs16
-rw-r--r--src/tools/clippy/tests/ui/suspicious_doc_comments_unfixable.stderr37
-rw-r--r--src/tools/clippy/tests/ui/tests_outside_test_module.rs18
-rw-r--r--src/tools/clippy/tests/ui/tests_outside_test_module.stderr11
-rw-r--r--src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed8
-rw-r--r--src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs8
-rw-r--r--src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr8
-rw-r--r--src/tools/clippy/tests/ui/uninit.rs18
-rw-r--r--src/tools/clippy/tests/ui/uninit.stderr10
-rw-r--r--src/tools/clippy/tests/ui/uninit_vec.rs8
-rw-r--r--src/tools/clippy/tests/ui/uninlined_format_args.fixed88
-rw-r--r--src/tools/clippy/tests/ui/uninlined_format_args.rs86
-rw-r--r--src/tools/clippy/tests/ui/uninlined_format_args.stderr14
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_box_returns.rs60
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_box_returns.stderr35
-rw-r--r--src/tools/clippy/tests/ui/unused_format_specs.fixed18
-rw-r--r--src/tools/clippy/tests/ui/unused_format_specs.rs18
-rw-r--r--src/tools/clippy/tests/ui/unused_format_specs.stderr54
-rw-r--r--src/tools/clippy/tests/ui/unused_format_specs_unfixable.stderr12
-rw-r--r--src/tools/compiletest/src/common.rs3
-rw-r--r--src/tools/compiletest/src/main.rs9
-rw-r--r--src/tools/compiletest/src/runtest.rs11
-rw-r--r--src/tools/miri/.github/workflows/ci.yml7
-rw-r--r--src/tools/miri/Cargo.lock8
-rw-r--r--src/tools/miri/README.md9
-rwxr-xr-xsrc/tools/miri/ci.sh4
-rw-r--r--src/tools/miri/rust-version2
-rw-r--r--src/tools/miri/src/bin/miri.rs19
-rw-r--r--src/tools/miri/src/clock.rs5
-rw-r--r--src/tools/miri/src/lib.rs8
-rw-r--r--src/tools/miri/src/shims/panic.rs20
-rw-r--r--src/tools/miri/src/shims/unix/fs.rs14
-rw-r--r--src/tools/miri/tests/fail/terminate-terminator.stderr4
-rw-r--r--src/tools/miri/tests/panic/alignment-assertion.rs9
-rw-r--r--src/tools/miri/tests/panic/alignment-assertion.stderr2
-rw-r--r--src/tools/miri/tests/pass-dep/shims/fcntl_f-fullfsync_apple.rs12
-rw-r--r--src/tools/miri/tests/pass-dep/shims/fcntl_f-fullfsync_apple.stderr2
-rw-r--r--src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.rs5
-rw-r--r--src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.stderr2
-rw-r--r--src/tools/miri/tests/pass-dep/tokio/sleep.rs6
-rw-r--r--src/tools/miri/tests/pass-dep/tokio/tokio_mvp.rs2
-rw-r--r--src/tools/miri/tests/pass/concurrency/thread_park_isolated.rs4
-rw-r--r--src/tools/miri/tests/pass/shims/time-with-isolation.rs15
-rw-r--r--src/tools/miri/tests/pass/shims/time-with-isolation2.rs8
-rw-r--r--src/tools/miri/tests/pass/shims/time-with-isolation2.stdout1
190 files changed, 4968 insertions, 1775 deletions
diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py
index d12781cc33a..025145244c4 100644
--- a/src/bootstrap/bootstrap.py
+++ b/src/bootstrap/bootstrap.py
@@ -304,6 +304,7 @@ def default_build_triple(verbose):
         'i486': 'i686',
         'i686': 'i686',
         'i786': 'i686',
+        'loongarch64': 'loongarch64',
         'm68k': 'm68k',
         'powerpc': 'powerpc',
         'powerpc64': 'powerpc64',
diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs
index e3581943f2c..85d1c12cc6a 100644
--- a/src/bootstrap/compile.rs
+++ b/src/bootstrap/compile.rs
@@ -83,11 +83,11 @@ impl Step for Std {
         let target = self.target;
         let compiler = self.compiler;
 
-        // These artifacts were already copied (in `impl Step for Sysroot`).
-        // Don't recompile them.
+        // When using `download-rustc`, we already have artifacts for the host available
+        // (they were copied in `impl Step for Sysroot`). Don't recompile them.
         // NOTE: the ABI of the beta compiler is different from the ABI of the downloaded compiler,
         // so its artifacts can't be reused.
-        if builder.download_rustc() && compiler.stage != 0 {
+        if builder.download_rustc() && compiler.stage != 0 && target == builder.build.build {
             return;
         }
 
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index 5ee18cf6411..eaa3afa4b7b 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -129,7 +129,8 @@ const EXTRA_CHECK_CFGS: &[(Option<Mode>, &'static str, Option<&[&'static str]>)]
     /* Extra values not defined in the built-in targets yet, but used in std */
     (Some(Mode::Std), "target_env", Some(&["libnx"])),
     // (Some(Mode::Std), "target_os", Some(&[])),
-    (Some(Mode::Std), "target_arch", Some(&["asmjs", "spirv", "nvptx", "xtensa"])),
+    // #[cfg(bootstrap)] loongarch64
+    (Some(Mode::Std), "target_arch", Some(&["asmjs", "spirv", "nvptx", "xtensa", "loongarch64"])),
     /* Extra names used by dependencies */
     // FIXME: Used by serde_json, but we should not be triggering on external dependencies.
     (Some(Mode::Rustc), "no_btreemap_remove_entry", None),
diff --git a/src/bootstrap/llvm.rs b/src/bootstrap/llvm.rs
index cc2b45a9bdb..d123deec354 100644
--- a/src/bootstrap/llvm.rs
+++ b/src/bootstrap/llvm.rs
@@ -291,7 +291,7 @@ impl Step for Llvm {
         let llvm_targets = match &builder.config.llvm_targets {
             Some(s) => s,
             None => {
-                "AArch64;ARM;BPF;Hexagon;MSP430;Mips;NVPTX;PowerPC;RISCV;\
+                "AArch64;ARM;BPF;Hexagon;LoongArch;MSP430;Mips;NVPTX;PowerPC;RISCV;\
                      Sparc;SystemZ;WebAssembly;X86"
             }
         };
diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs
index 058ff429e80..f9c5837b7d6 100644
--- a/src/bootstrap/test.rs
+++ b/src/bootstrap/test.rs
@@ -1535,7 +1535,10 @@ note: if you're sure you want to do this, please open an issue as to why. In the
         flags.extend(builder.config.cmd.rustc_args().iter().map(|s| s.to_string()));
 
         if let Some(linker) = builder.linker(target) {
-            cmd.arg("--linker").arg(linker);
+            cmd.arg("--target-linker").arg(linker);
+        }
+        if let Some(linker) = builder.linker(compiler.host) {
+            cmd.arg("--host-linker").arg(linker);
         }
 
         let mut hostflags = flags.clone();
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version
index 66fb941ea37..7092c7c46f8 100644
--- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version
@@ -1 +1 @@
-0.14.6
\ No newline at end of file
+0.15.0
\ No newline at end of file
diff --git a/src/doc/book b/src/doc/book
-Subproject 0510ca84c2ce6bf93c4ccf9248756e9e4fd00b1
+Subproject c06006157b14b3d47b5c716fc392b77f3b2e21c
diff --git a/src/doc/reference b/src/doc/reference
-Subproject 3c47807a3131b3c7cacb508f52632078d253cd0
+Subproject 1f8dc727e94ae4ef92adf70df979521a1ea1143
diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example
-Subproject ba84bf35d0f17d404003349309201654d25f61a
+Subproject 31961fe22521a779070a44a8f30a2b00a20b621
diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide
-Subproject fca8af6c154c6cde2512f1331cf2704f214a818
+Subproject 6337ed17fb8dcd918d78b7d97d213e923530337
diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md
index 0452126cc37..8ded2ee59dd 100644
--- a/src/doc/rustc/src/SUMMARY.md
+++ b/src/doc/rustc/src/SUMMARY.md
@@ -29,6 +29,7 @@
     - [\*-linux-ohos](platform-support/openharmony.md)
     - [\*-unknown-fuchsia](platform-support/fuchsia.md)
     - [\*-kmc-solid_\*](platform-support/kmc-solid.md)
+    - [loongarch\*-unknown-linux-\*](platform-support/loongarch-linux.md)
     - [m68k-unknown-linux-gnu](platform-support/m68k-unknown-linux-gnu.md)
     - [mips64-openwrt-linux-musl](platform-support/mips64-openwrt-linux-musl.md)
     - [mipsel-sony-psx](platform-support/mipsel-sony-psx.md)
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index 67fe2a610c2..c378532dbf6 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -266,6 +266,7 @@ target | std | host | notes
 `i686-uwp-windows-gnu` | ? |  |
 `i686-uwp-windows-msvc` | ? |  |
 `i686-wrs-vxworks` | ? |  |
+[`loongarch64-unknown-linux-gnu`](platform-support/loongarch-linux.md) | ? |  | LoongArch64 Linux (LP64D ABI)
 [`m68k-unknown-linux-gnu`](platform-support/m68k-unknown-linux-gnu.md) | ? |  | Motorola 680x0 Linux
 `mips-unknown-linux-uclibc` | ✓ |  | MIPS Linux with uClibc
 [`mips64-openwrt-linux-musl`](platform-support/mips64-openwrt-linux-musl.md) | ? |  | MIPS64 for OpenWrt Linux MUSL
diff --git a/src/doc/rustc/src/platform-support/loongarch-linux.md b/src/doc/rustc/src/platform-support/loongarch-linux.md
new file mode 100644
index 00000000000..e046ec244ec
--- /dev/null
+++ b/src/doc/rustc/src/platform-support/loongarch-linux.md
@@ -0,0 +1,92 @@
+# loongarch\*-unknown-linux-\*
+
+**Tier: 3**
+
+[LoongArch] is a new RISC ISA developed by Loongson Technology Corporation Limited.
+
+[LoongArch]: https://loongson.github.io/LoongArch-Documentation/README-EN.html
+
+The target name follow this format: `<machine>-<vendor>-<os><fabi_suffix>, where `<machine>` specifies the CPU family/model, `<vendor>` specifies the vendor and `<os>` the operating system name.
+While the integer base ABI is implied by the machine field, the floating point base ABI type is encoded into the os field of the specifier using the string suffix `<fabi-suffix>`.
+
+|    `<fabi-suffix>`     |                           `Description`                            |
+|------------------------|--------------------------------------------------------------------|
+|          f64           | The base ABI use 64-bits FPRs for parameter passing.(lp64d)|
+|          f32           | The base ABI uses 32-bit FPRs for parameter passing. (lp64f)|
+|          sf            | The base ABI uses no FPR for parameter passing. (lp64s)     |
+
+|`ABI type(Base ABI/ABI extension)`| `C library` | `kernel` |          `target tuple`          |
+|----------------------------------|-------------|----------|----------------------------------|
+|           lp64d/base             |   glibc     |  linux   | loongarch64-unknown-linux-gnu |
+|           lp64f/base             |   glibc     |  linux   | loongarch64-unknown-linux-gnuf32 |
+|           lp64s/base             |   glibc     |  linux   | loongarch64-unknown-linux-gnusf  |
+|           lp64d/base             |  musl libc  |  linux   | loongarch64-unknown-linux-musl|
+|           lp64f/base             |  musl libc  |  linux   | loongarch64-unknown-linux-muslf32|
+|           lp64s/base             |  musl libc  |  linux   | loongarch64-unknown-linux-muslsf |
+
+## Target maintainers
+
+- [ZHAI xiaojuan](https://github.com/zhaixiaojuan) `zhaixiaojuan@loongson.cn`
+- [WANG rui](https://github.com/heiher) `wangrui@loongson.cn`
+- [ZHAI xiang](https://github.com/xiangzhai) `zhaixiang@loongson.cn`
+- [WANG Xuerui](https://github.com/xen0n) `git@xen0n.name`
+
+## Requirements
+
+This target is cross-compiled.
+A GNU toolchain for LoongArch target is required.  It can be downloaded from https://github.com/loongson/build-tools/releases, or built from the source code of GCC (12.1.0 or later) and Binutils (2.40 or later).
+
+## Building the target
+
+The target can be built by enabling it for a `rustc` build.
+
+```toml
+[build]
+target = ["loongarch64-unknown-linux-gnu"]
+```
+
+Make sure `loongarch64-unknown-linux-gnu-gcc` can be searched from the directories specified in`$PATH`. Alternatively, you can use GNU LoongArch Toolchain by adding the following to `config.toml`:
+
+```toml
+[target.loongarch64-unknown-linux-gnu]
+# ADJUST THIS PATH TO POINT AT YOUR TOOLCHAIN
+cc = "/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-gcc"
+cxx = "/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-g++"
+ar = "/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-ar"
+ranlib = "/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-ranlib"
+linker = "/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-gcc"
+```
+
+## Cross-compilation
+
+This target can be cross-compiled on a `x86_64-unknown-linux-gnu` host. Cross-compilation on other hosts may work but is not tested.
+
+## Testing
+To test a cross-compiled binary on your build system, install the qemu binary that supports the LoongArch architecture and execute the following commands.
+```text
+CC_loongarch64_unknown_linux_gnu=/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-gcc \
+CXX_loongarch64_unknown_linux_gnu=/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-g++ \
+AR_loongarch64_unknown_linux_gnu=/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-gcc-ar \
+CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNUN_LINKER=/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-gcc \
+# SET TARGET SYSTEM LIBRARY PATH
+CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNUN_RUNNER="qemu-loongarch64 -L /TOOLCHAIN_PATH/TARGET_LIBRAY_PATH" \
+cargo run --target loongarch64-unknown-linux-gnu --release
+```
+Tested on x86 architecture, other architectures not tested.
+
+## Building Rust programs
+
+Rust does not yet ship pre-compiled artifacts for this target. To compile for this target, you will either need to build Rust with the target enabled (see "Building the target" above), or build your own copy of `std` by using `build-std` or similar.
+
+If `rustc` has support for that target and the library artifacts are available, then Rust static libraries can be built for that target:
+
+```shell
+$ rustc --target loongarch64-unknown-linux-gnu your-code.rs --crate-type staticlib
+$ ls libyour_code.a
+```
+
+On Rust Nightly it's possible to build without the target artifacts available:
+
+```text
+cargo build -Z build-std --target loongarch64-unknown-linux-gnu
+```
diff --git a/src/etc/rust-gdb b/src/etc/rust-gdb
index b950cea79ed..d812f7a802b 100755
--- a/src/etc/rust-gdb
+++ b/src/etc/rust-gdb
@@ -13,6 +13,8 @@ fi
 # Find out where the pretty printer Python module is
 RUSTC_SYSROOT="$("$RUSTC" --print=sysroot)"
 GDB_PYTHON_MODULE_DIRECTORY="$RUSTC_SYSROOT/lib/rustlib/etc"
+# Get the commit hash for path remapping
+RUSTC_COMMIT_HASH="$("$RUSTC" -vV | sed -n 's/commit-hash: \(\w*\)/\1/p')"
 
 # Run GDB with the additional arguments that load the pretty printers
 # Set the environment variable `RUST_GDB` to overwrite the call to a
@@ -21,4 +23,6 @@ RUST_GDB="${RUST_GDB:-gdb}"
 PYTHONPATH="$PYTHONPATH:$GDB_PYTHON_MODULE_DIRECTORY" exec ${RUST_GDB} \
   --directory="$GDB_PYTHON_MODULE_DIRECTORY" \
   -iex "add-auto-load-safe-path $GDB_PYTHON_MODULE_DIRECTORY" \
+  -iex "set substitute-path /rustc/$RUSTC_COMMIT_HASH $RUSTC_SYSROOT/lib/rustlib/src/rust" \
   "$@"
+ 
\ No newline at end of file
diff --git a/src/etc/rust-gdbgui b/src/etc/rust-gdbgui
index 590e488e643..e7bafcc99b8 100755
--- a/src/etc/rust-gdbgui
+++ b/src/etc/rust-gdbgui
@@ -42,6 +42,8 @@ fi
 # Find out where the pretty printer Python module is
 RUSTC_SYSROOT="$("$RUSTC" --print=sysroot)"
 GDB_PYTHON_MODULE_DIRECTORY="$RUSTC_SYSROOT/lib/rustlib/etc"
+# Get the commit hash for path remapping
+RUSTC_COMMIT_HASH="$("$RUSTC" -vV | sed -n 's/commit-hash: \(\w*\)/\1/p')"
 
 # Set the environment variable `RUST_GDB` to overwrite the call to a
 # different/specific command (defaults to `gdb`).
@@ -53,7 +55,9 @@ RUST_GDBGUI="${RUST_GDBGUI:-gdbgui}"
 
 # These arguments get passed through to GDB and make it load the
 # Rust pretty printers.
-GDB_ARGS="--directory=\"$GDB_PYTHON_MODULE_DIRECTORY\" -iex \"add-auto-load-safe-path $GDB_PYTHON_MODULE_DIRECTORY\""
+GDB_ARGS="--directory=\"$GDB_PYTHON_MODULE_DIRECTORY\"" \
+   "-iex \"add-auto-load-safe-path $GDB_PYTHON_MODULE_DIRECTORY\"" \
+   "-iex \"set substitute-path /rustc/$RUSTC_COMMIT_HASH $RUSTC_SYSROOT/lib/rustlib/src/rust\""
 
 # Finally we execute gdbgui.
 PYTHONPATH="$PYTHONPATH:$GDB_PYTHON_MODULE_DIRECTORY" \
diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs
index dd58a5b51fc..5177cffe6ba 100644
--- a/src/librustdoc/clean/cfg.rs
+++ b/src/librustdoc/clean/cfg.rs
@@ -517,6 +517,7 @@ impl<'a> fmt::Display for Display<'a> {
                         "aarch64" => "AArch64",
                         "arm" => "ARM",
                         "asmjs" => "JavaScript",
+                        "loongarch64" => "LoongArch LA64",
                         "m68k" => "M68k",
                         "mips" => "MIPS",
                         "mips64" => "MIPS-64",
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 7f150f38025..b3df12a9df1 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -2381,7 +2381,8 @@ fn clean_extern_crate<'tcx>(
                     Some(l) => attr::list_contains_name(&l, sym::inline),
                     None => false,
                 }
-        });
+        })
+        && !cx.output_format.is_json();
 
     let krate_owner_def_id = krate.owner_id.to_def_id();
     if please_inline {
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index cd97766752d..e34ece9264c 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -687,7 +687,7 @@ impl Item {
                 return None;
             }
             // Variants always inherit visibility
-            VariantItem(..) => return None,
+            VariantItem(..) | ImplItem(..) => return None,
             // Trait items inherit the trait's visibility
             AssocConstItem(..) | TyAssocConstItem(..) | AssocTypeItem(..) | TyAssocTypeItem(..)
             | TyMethodItem(..) | MethodItem(..) => {
diff --git a/src/tools/cargo b/src/tools/cargo
-Subproject 0e474cfd7b16b018cf46e95da3f6a5b2f1f6a9e
+Subproject 7bf43f028ba5eb1f4d70d271c2546c38512c987
diff --git a/src/tools/clippy/.cargo/config.toml b/src/tools/clippy/.cargo/config.toml
index f3dd9275a42..4d80d3ce63d 100644
--- a/src/tools/clippy/.cargo/config.toml
+++ b/src/tools/clippy/.cargo/config.toml
@@ -11,3 +11,6 @@ target-dir = "target"
 
 [unstable]
 binary-dep-depinfo = true
+
+[profile.dev]
+split-debuginfo = "unpacked"
diff --git a/src/tools/clippy/.editorconfig b/src/tools/clippy/.editorconfig
index ec6e107d547..bc7642bf8c7 100644
--- a/src/tools/clippy/.editorconfig
+++ b/src/tools/clippy/.editorconfig
@@ -11,6 +11,7 @@ trim_trailing_whitespace = true
 insert_final_newline = true
 indent_style = space
 indent_size = 4
+max_line_length = 120
 
 [*.md]
 # double whitespace at end of line
diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml
index 24e677ce8e1..93198aabdb5 100644
--- a/src/tools/clippy/.github/workflows/clippy_bors.yml
+++ b/src/tools/clippy/.github/workflows/clippy_bors.yml
@@ -180,6 +180,8 @@ jobs:
 
     # Run
     - name: Build Integration Test
+      env:
+        CARGO_PROFILE_DEV_SPLIT_DEBUGINFO: off
       run: cargo test --test integration --features integration --no-run
 
     # Upload
diff --git a/src/tools/clippy/.github/workflows/remark.yml b/src/tools/clippy/.github/workflows/remark.yml
index 81ef072bbb0..116058b7c75 100644
--- a/src/tools/clippy/.github/workflows/remark.yml
+++ b/src/tools/clippy/.github/workflows/remark.yml
@@ -29,7 +29,7 @@ jobs:
     - name: Install mdbook
       run: |
         mkdir mdbook
-        curl -Lf https://github.com/rust-lang/mdBook/releases/download/v0.4.18/mdbook-v0.4.18-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook
+        curl -Lf https://github.com/rust-lang/mdBook/releases/download/v0.4.28/mdbook-v0.4.28-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook
         echo `pwd`/mdbook >> $GITHUB_PATH
 
     # Run
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index 1323f973ccf..559b560dde4 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -4441,6 +4441,7 @@ Released 2018-09-13
 [`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp
 [`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp
 [`checked_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions
+[`clear_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#clear_with_drain
 [`clone_double_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref
 [`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy
 [`clone_on_ref_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_ref_ptr
@@ -4632,6 +4633,7 @@ Released 2018-09-13
 [`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays
 [`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups
 [`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant
+[`large_futures`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_futures
 [`large_include_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_include_file
 [`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays
 [`large_types_passed_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value
@@ -4645,6 +4647,7 @@ Released 2018-09-13
 [`let_underscore_untyped`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped
 [`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
 [`let_with_type_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_with_type_underscore
+[`lines_filter_map_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#lines_filter_map_ok
 [`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
 [`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
 [`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
@@ -4671,6 +4674,7 @@ Released 2018-09-13
 [`manual_rem_euclid`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid
 [`manual_retain`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain
 [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
+[`manual_slice_size_calculation`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_slice_size_calculation
 [`manual_split_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once
 [`manual_str_repeat`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat
 [`manual_string_new`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_string_new
@@ -4921,6 +4925,7 @@ Released 2018-09-13
 [`suspicious_arithmetic_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl
 [`suspicious_assignment_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_assignment_formatting
 [`suspicious_command_arg_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_command_arg_space
+[`suspicious_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_doc_comments
 [`suspicious_else_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_else_formatting
 [`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map
 [`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl
@@ -4933,6 +4938,7 @@ Released 2018-09-13
 [`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments
 [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
 [`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr
+[`tests_outside_test_module`]: https://rust-lang.github.io/rust-clippy/master/index.html#tests_outside_test_module
 [`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
 [`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display
 [`to_string_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_format_args
@@ -4974,6 +4980,7 @@ Released 2018-09-13
 [`unit_hash`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_hash
 [`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_return_expecting_ord
 [`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#unknown_clippy_lints
+[`unnecessary_box_returns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns
 [`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
 [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
 [`unnecessary_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_find_map
diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md
index b69ed8900a4..85798e0e80c 100644
--- a/src/tools/clippy/README.md
+++ b/src/tools/clippy/README.md
@@ -11,7 +11,7 @@ Lints are divided into categories, each with a default [lint level](https://doc.
 You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.
 
 | Category              | Description                                                                         | Default level |
-| --------------------- | ----------------------------------------------------------------------------------- | ------------- |
+|-----------------------|-------------------------------------------------------------------------------------|---------------|
 | `clippy::all`         | all lints that are on by default (correctness, suspicious, style, complexity, perf) | **warn/deny** |
 | `clippy::correctness` | code that is outright wrong or useless                                              | **deny**      |
 | `clippy::suspicious`  | code that is most likely wrong or useless                                           | **warn**      |
@@ -130,7 +130,7 @@ for example.
 
 You can add Clippy to Travis CI in the same way you use it locally:
 
-```yml
+```yaml
 language: rust
 rust:
   - stable
@@ -253,7 +253,7 @@ rust-version = "1.30"
 
 The MSRV can also be specified as an attribute, like below.
 
-```rust
+```rust,ignore
 #![feature(custom_inner_attributes)]
 #![clippy::msrv = "1.30.0"]
 
diff --git a/src/tools/clippy/book/src/README.md b/src/tools/clippy/book/src/README.md
index df4a1f2702e..3b627096268 100644
--- a/src/tools/clippy/book/src/README.md
+++ b/src/tools/clippy/book/src/README.md
@@ -14,7 +14,7 @@ much Clippy is supposed to ~~annoy~~ help you by changing the lint level by
 category.
 
 | Category              | Description                                                                         | Default level |
-| --------------------- | ----------------------------------------------------------------------------------- | ------------- |
+|-----------------------|-------------------------------------------------------------------------------------|---------------|
 | `clippy::all`         | all lints that are on by default (correctness, suspicious, style, complexity, perf) | **warn/deny** |
 | `clippy::correctness` | code that is outright wrong or useless                                              | **deny**      |
 | `clippy::suspicious`  | code that is most likely wrong or useless                                           | **warn**      |
diff --git a/src/tools/clippy/book/src/SUMMARY.md b/src/tools/clippy/book/src/SUMMARY.md
index 0649f7a631d..cbd73376dfa 100644
--- a/src/tools/clippy/book/src/SUMMARY.md
+++ b/src/tools/clippy/book/src/SUMMARY.md
@@ -13,6 +13,7 @@
 - [Development](development/README.md)
     - [Basics](development/basics.md)
     - [Adding Lints](development/adding_lints.md)
+    - [Type Checking](development/type_checking.md)
     - [Common Tools](development/common_tools_writing_lints.md)
     - [Infrastructure](development/infrastructure/README.md)
         - [Syncing changes between Clippy and rust-lang/rust](development/infrastructure/sync.md)
diff --git a/src/tools/clippy/book/src/configuration.md b/src/tools/clippy/book/src/configuration.md
index 87f4a697af9..1304f6a8c2f 100644
--- a/src/tools/clippy/book/src/configuration.md
+++ b/src/tools/clippy/book/src/configuration.md
@@ -3,7 +3,7 @@
 > **Note:** The configuration file is unstable and may be deprecated in the future.
 
 Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a
-basic `variable = value` mapping eg.
+basic `variable = value` mapping e.g.
 
 ```toml
 avoid-breaking-exported-api = false
@@ -60,7 +60,7 @@ And to warn on `lint_name`, run
 cargo clippy -- -W clippy::lint_name
 ```
 
-This also works with lint groups. For example you can run Clippy with warnings for all lints enabled:
+This also works with lint groups. For example, you can run Clippy with warnings for all lints enabled:
 
 ```terminal
 cargo clippy -- -W clippy::pedantic
@@ -84,7 +84,7 @@ msrv = "1.30.0"
 
 The MSRV can also be specified as an attribute, like below.
 
-```rust
+```rust,ignore
 #![feature(custom_inner_attributes)]
 #![clippy::msrv = "1.30.0"]
 
@@ -96,7 +96,28 @@ fn main() {
 You can also omit the patch version when specifying the MSRV, so `msrv = 1.30`
 is equivalent to `msrv = 1.30.0`.
 
-Note: `custom_inner_attributes` is an unstable feature so it has to be enabled explicitly.
+Note: `custom_inner_attributes` is an unstable feature, so it has to be enabled explicitly.
 
 Lints that recognize this configuration option can be
 found [here](https://rust-lang.github.io/rust-clippy/master/index.html#msrv)
+
+### Disabling evaluation of certain code
+
+> **Note:** This should only be used in cases where other solutions, like `#[allow(clippy::all)]`, are not sufficient.
+
+Very rarely, you may wish to prevent Clippy from evaluating certain sections of code entirely. You can do this with
+[conditional compilation](https://doc.rust-lang.org/reference/conditional-compilation.html) by checking that the
+`cargo-clippy` feature is not set. You may need to provide a stub so that the code compiles:
+
+```rust
+#[cfg(not(feature = "cargo-clippy"))]
+include!(concat!(env!("OUT_DIR"), "/my_big_function-generated.rs"));
+
+#[cfg(feature = "cargo-clippy")]
+fn my_big_function(_input: &str) -> Option<MyStruct> {
+    None
+}
+```
+
+This feature is not actually part of your crate, so specifying `--all-features` to other tools, e.g. `cargo test
+--all-features`, will not disable it.
diff --git a/src/tools/clippy/book/src/development/README.md b/src/tools/clippy/book/src/development/README.md
index 5cf7201cffa..616e6d182b7 100644
--- a/src/tools/clippy/book/src/development/README.md
+++ b/src/tools/clippy/book/src/development/README.md
@@ -5,7 +5,7 @@ making Clippy better by contributing to it. In that case, welcome to the
 project!
 
 > _Note:_ If you're just interested in using Clippy, there's nothing to see from
-> this point onward and you should return to one of the earlier chapters.
+> this point onward, and you should return to one of the earlier chapters.
 
 ## Getting started
 
diff --git a/src/tools/clippy/book/src/development/adding_lints.md b/src/tools/clippy/book/src/development/adding_lints.md
index f57dc627dce..9dacaaaae5c 100644
--- a/src/tools/clippy/book/src/development/adding_lints.md
+++ b/src/tools/clippy/book/src/development/adding_lints.md
@@ -18,6 +18,7 @@ because that's clearly a non-descriptive name.
     - [Cargo lints](#cargo-lints)
   - [Rustfix tests](#rustfix-tests)
   - [Testing manually](#testing-manually)
+  - [Running directly](#running-directly)
   - [Lint declaration](#lint-declaration)
   - [Lint registration](#lint-registration)
   - [Lint passes](#lint-passes)
@@ -186,6 +187,15 @@ cargo dev lint input.rs
 from the working copy root. With tests in place, let's have a look at
 implementing our lint now.
 
+## Running directly
+
+While it's easier to just use `cargo dev lint`, it might be desirable to get
+`target/release/cargo-clippy` and `target/release/clippy-driver` to work as well in some cases.
+By default, they don't work because clippy dynamically links rustc. To help them find rustc,
+add the path printed by`rustc --print target-libdir` (ran inside this workspace so that the rustc version matches)
+to your library search path.
+On linux, this can be done by setting the `LD_LIBRARY_PATH` environment variable to that path.
+
 ## Lint declaration
 
 Let's start by opening the new file created in the `clippy_lints` crate at
@@ -265,7 +275,7 @@ When declaring a new lint by hand and `cargo dev update_lints` is used, the lint
 pass may have to be registered manually in the `register_plugins` function in
 `clippy_lints/src/lib.rs`:
 
-```rust
+```rust,ignore
 store.register_early_pass(|| Box::new(foo_functions::FooFunctions));
 ```
 
@@ -291,7 +301,7 @@ either [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass].
 
 In short, the `LateLintPass` has access to type information while the
 `EarlyLintPass` doesn't. If you don't need access to type information, use the
-`EarlyLintPass`. The `EarlyLintPass` is also faster. However linting speed
+`EarlyLintPass`. The `EarlyLintPass` is also faster. However, linting speed
 hasn't really been a concern with Clippy so far.
 
 Since we don't need type information for checking the function name, we used
@@ -308,7 +318,7 @@ implementation of the lint logic.
 
 Let's start by implementing the `EarlyLintPass` for our `FooFunctions`:
 
-```rust
+```rust,ignore
 impl EarlyLintPass for FooFunctions {
     fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) {
         // TODO: Emit lint here
@@ -327,10 +337,10 @@ variety of lint emission functions. They can all be found in
 [`clippy_utils/src/diagnostics.rs`][diagnostics].
 
 `span_lint_and_help` seems most appropriate in this case. It allows us to
-provide an extra help message and we can't really suggest a better name
+provide an extra help message, and we can't really suggest a better name
 automatically. This is how it looks:
 
-```rust
+```rust,ignore
 impl EarlyLintPass for FooFunctions {
     fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) {
         span_lint_and_help(
@@ -469,7 +479,7 @@ the value from `clippy.toml`. This can be accounted for using the
 `extract_msrv_attr!(LintContext)` macro and passing
 `LateContext`/`EarlyContext`.
 
-```rust
+```rust,ignore
 impl<'tcx> LateLintPass<'tcx> for ManualStrip {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         ...
@@ -483,7 +493,7 @@ the lint's test file, `tests/ui/manual_strip.rs` in this example. It should
 have a case for the version below the MSRV and one with the same contents but
 for the MSRV version itself.
 
-```rust
+```rust,ignore
 ...
 
 #[clippy::msrv = "1.44"]
@@ -514,7 +524,7 @@ define_Conf! {
 
 If you have trouble implementing your lint, there is also the internal `author`
 lint to generate Clippy code that detects the offending pattern. It does not
-work for all of the Rust syntax, but can give a good starting point.
+work for all the Rust syntax, but can give a good starting point.
 
 The quickest way to use it, is the [Rust playground:
 play.rust-lang.org][author_example]. Put the code you want to lint into the
@@ -607,7 +617,7 @@ output in the `stdout` part.
 
 ## PR Checklist
 
-Before submitting your PR make sure you followed all of the basic requirements:
+Before submitting your PR make sure you followed all the basic requirements:
 
 <!-- Sync this with `.github/PULL_REQUEST_TEMPLATE` -->
 
@@ -627,7 +637,7 @@ for some users. Adding a configuration is done in the following steps:
 
 1. Adding a new configuration entry to [`clippy_lints::utils::conf`] like this:
 
-   ```rust
+   ```rust,ignore
    /// Lint: LINT_NAME.
    ///
    /// <The configuration field doc comment>
@@ -680,7 +690,7 @@ for some users. Adding a configuration is done in the following steps:
    configuration value is now cloned or copied into a local value that is then
    passed to the impl struct like this:
 
-   ```rust
+   ```rust,ignore
    // Default generated registration:
    store.register_*_pass(|| box module::StructName);
 
diff --git a/src/tools/clippy/book/src/development/basics.md b/src/tools/clippy/book/src/development/basics.md
index 6fb53236e6f..7615dc12f9e 100644
--- a/src/tools/clippy/book/src/development/basics.md
+++ b/src/tools/clippy/book/src/development/basics.md
@@ -4,8 +4,8 @@ This document explains the basics for hacking on Clippy. Besides others, this
 includes how to build and test Clippy. For a more in depth description on the
 codebase take a look at [Adding Lints] or [Common Tools].
 
-[Adding Lints]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/adding_lints.md
-[Common Tools]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/common_tools_writing_lints.md
+[Adding Lints]: adding_lints.md
+[Common Tools]: common_tools_writing_lints.md
 
 - [Basics for hacking on Clippy](#basics-for-hacking-on-clippy)
   - [Get the Code](#get-the-code)
@@ -125,7 +125,7 @@ We follow a rustc no merge-commit policy. See
 ## Common Abbreviations
 
 | Abbreviation | Meaning                                |
-| ------------ | -------------------------------------- |
+|--------------|----------------------------------------|
 | UB           | Undefined Behavior                     |
 | FP           | False Positive                         |
 | FN           | False Negative                         |
diff --git a/src/tools/clippy/book/src/development/common_tools_writing_lints.md b/src/tools/clippy/book/src/development/common_tools_writing_lints.md
index f5aa06e4bf6..09171d86a20 100644
--- a/src/tools/clippy/book/src/development/common_tools_writing_lints.md
+++ b/src/tools/clippy/book/src/development/common_tools_writing_lints.md
@@ -3,7 +3,7 @@
 You may need following tooltips to catch up with common operations.
 
 - [Common tools for writing lints](#common-tools-for-writing-lints)
-  - [Retrieving the type of an expression](#retrieving-the-type-of-an-expression)
+  - [Retrieving the type of expression](#retrieving-the-type-of-expression)
   - [Checking if an expr is calling a specific method](#checking-if-an-expr-is-calling-a-specific-method)
   - [Checking for a specific type](#checking-for-a-specific-type)
   - [Checking if a type implements a specific trait](#checking-if-a-type-implements-a-specific-trait)
@@ -16,7 +16,7 @@ Useful Rustc dev guide links:
 - [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html)
 - [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html)
 
-## Retrieving the type of an expression
+## Retrieving the type of expression
 
 Sometimes you may want to retrieve the type `Ty` of an expression `Expr`, for
 example to answer following questions:
@@ -45,7 +45,7 @@ impl LateLintPass<'_> for MyStructLint {
 }
 ```
 
-Similarly in [`TypeckResults`][TypeckResults] methods, you have the
+Similarly, in [`TypeckResults`][TypeckResults] methods, you have the
 [`pat_ty()`][pat_ty] method to retrieve a type from a pattern.
 
 Two noticeable items here:
@@ -192,7 +192,7 @@ functions to deal with macros:
 - `span.from_expansion()`: detects if a span is from macro expansion or
   desugaring. Checking this is a common first step in a lint.
 
-   ```rust
+   ```rust,ignore
    if expr.span.from_expansion() {
        // just forget it
        return;
@@ -203,11 +203,11 @@ functions to deal with macros:
   if so, which macro call expanded it. It is sometimes useful to check if the
   context of two spans are equal.
 
-  ```rust
+  ```rust,ignore
   // expands to `1 + 0`, but don't lint
   1 + mac!()
   ```
-  ```rust
+  ```rust,ignore
   if left.span.ctxt() != right.span.ctxt() {
       // the coder most likely cannot modify this expression
       return;
@@ -246,7 +246,7 @@ functions to deal with macros:
   `macro_rules!` with `a == $b`, `$b` is expanded to some expression with a
   different context from `a`.
 
-   ```rust
+   ```rust,ignore
    macro_rules! m {
        ($a:expr, $b:expr) => {
            if $a.is_some() {
diff --git a/src/tools/clippy/book/src/development/infrastructure/book.md b/src/tools/clippy/book/src/development/infrastructure/book.md
index dbd624ecd73..de5de4bebaa 100644
--- a/src/tools/clippy/book/src/development/infrastructure/book.md
+++ b/src/tools/clippy/book/src/development/infrastructure/book.md
@@ -13,7 +13,7 @@ guide to Clippy that you're reading right now. The Clippy book is formatted with
 While not strictly necessary since the book source is simply Markdown text
 files, having mdBook locally will allow you to build, test and serve the book
 locally to view changes before you commit them to the repository. You likely
-already have `cargo` installed, so the easiest option is to simply:
+already have `cargo` installed, so the easiest option is to:
 
 ```shell
 cargo install mdbook
@@ -26,7 +26,7 @@ instructions for other options.
 
 The book's
 [src](https://github.com/rust-lang/rust-clippy/tree/master/book/src)
-directory contains all of the markdown files used to generate the book. If you
+directory contains all the markdown files used to generate the book. If you
 want to see your changes in real time, you can use the mdBook `serve` command to
 run a web server locally that will automatically update changes as they are
 made. From the top level of your `rust-clippy` directory:
diff --git a/src/tools/clippy/book/src/development/infrastructure/changelog_update.md b/src/tools/clippy/book/src/development/infrastructure/changelog_update.md
index d1ac7237b5e..df9b1bbe18f 100644
--- a/src/tools/clippy/book/src/development/infrastructure/changelog_update.md
+++ b/src/tools/clippy/book/src/development/infrastructure/changelog_update.md
@@ -101,7 +101,7 @@ Look for the [`beta-accepted`] label and make sure to also include the PRs with
 that label in the changelog. If you can, remove the `beta-accepted` labels
 **after** the changelog PR was merged.
 
-> _Note:_ Some of those PRs might even got backported to the previous `beta`.
+> _Note:_ Some of those PRs might even get backported to the previous `beta`.
 > Those have to be included in the changelog of the _previous_ release.
 
 ### 4. Update `clippy::version` attributes
diff --git a/src/tools/clippy/book/src/development/infrastructure/release.md b/src/tools/clippy/book/src/development/infrastructure/release.md
index 0572281803e..98fabf8e89a 100644
--- a/src/tools/clippy/book/src/development/infrastructure/release.md
+++ b/src/tools/clippy/book/src/development/infrastructure/release.md
@@ -44,7 +44,7 @@ $ git push origin backport_remerge  # This can be pushed to your fork
 ```
 
 After this, open a PR to the master branch. In this PR, the commit hash of the
-`HEAD` of the `beta` branch must exists. In addition to that, no files should be
+`HEAD` of the `beta` branch must exist. In addition to that, no files should be
 changed by this PR.
 
 ## Update the `beta` branch
diff --git a/src/tools/clippy/book/src/development/infrastructure/sync.md b/src/tools/clippy/book/src/development/infrastructure/sync.md
index 02cfc11b55a..e1fe92f9525 100644
--- a/src/tools/clippy/book/src/development/infrastructure/sync.md
+++ b/src/tools/clippy/book/src/development/infrastructure/sync.md
@@ -19,8 +19,7 @@ to beta. For reference, the first sync following this cadence was performed the
 2020-08-27.
 
 This process is described in detail in the following sections. For general
-information about `subtree`s in the Rust repository see [Rust's
-`CONTRIBUTING.md`][subtree].
+information about `subtree`s in the Rust repository see [the rustc-dev-guide][subtree].
 
 ## Patching git-subtree to work with big repos
 
@@ -47,7 +46,7 @@ sudo chown --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subt
 
 > _Note:_ If you are a Debian user, `dash` is the shell used by default for
 > scripts instead of `sh`. This shell has a hardcoded recursion limit set to
-> 1000. In order to make this process work, you need to force the script to run
+> 1,000. In order to make this process work, you need to force the script to run
 > `bash` instead. You can do this by editing the first line of the `git-subtree`
 > script and changing `sh` to `bash`.
 
@@ -71,10 +70,10 @@ $ git remote add clippy-local /path/to/rust-clippy
 
 ## Performing the sync from [`rust-lang/rust`] to Clippy
 
-Here is a TL;DR version of the sync process (all of the following commands have
+Here is a TL;DR version of the sync process (all the following commands have
 to be run inside the `rust` directory):
 
-1. Clone the [`rust-lang/rust`] repository or make sure it is up to date.
+1. Clone the [`rust-lang/rust`] repository or make sure it is up-to-date.
 2. Checkout the commit from the latest available nightly. You can get it using
    `rustup check`.
 3. Sync the changes to the rust-copy of Clippy to your Clippy fork:
@@ -107,7 +106,7 @@ to be run inside the `rust` directory):
 
 ## Performing the sync from Clippy to [`rust-lang/rust`]
 
-All of the following commands have to be run inside the `rust` directory.
+All the following commands have to be run inside the `rust` directory.
 
 1. Make sure you have checked out the latest `master` of `rust-lang/rust`.
 2. Sync the `rust-lang/rust-clippy` master to the rust-copy of Clippy:
@@ -118,5 +117,5 @@ All of the following commands have to be run inside the `rust` directory.
 3. Open a PR to [`rust-lang/rust`]
 
 [gitgitgadget-pr]: https://github.com/gitgitgadget/git/pull/493
-[subtree]: https://rustc-dev-guide.rust-lang.org/contributing.html#external-dependencies-subtree
+[subtree]: https://rustc-dev-guide.rust-lang.org/external-repos.html#external-dependencies-subtree
 [`rust-lang/rust`]: https://github.com/rust-lang/rust
diff --git a/src/tools/clippy/book/src/development/proposals/README.md b/src/tools/clippy/book/src/development/proposals/README.md
index 78fe34ebf8f..059c22ce1ce 100644
--- a/src/tools/clippy/book/src/development/proposals/README.md
+++ b/src/tools/clippy/book/src/development/proposals/README.md
@@ -6,6 +6,6 @@ or around Clippy in the long run.
 Besides adding more and more lints and improve the lints that Clippy already
 has, Clippy is also interested in making the experience of its users, developers
 and maintainers better over time. Projects that address bigger picture things
-like this usually take more time and it is useful to have a proposal for those
+like this usually take more time, and it is useful to have a proposal for those
 first. This is the place where such proposals are collected, so that we can
 refer to them when working on them.
diff --git a/src/tools/clippy/book/src/development/proposals/roadmap-2021.md b/src/tools/clippy/book/src/development/proposals/roadmap-2021.md
index fe8b080f56f..4406616bbb6 100644
--- a/src/tools/clippy/book/src/development/proposals/roadmap-2021.md
+++ b/src/tools/clippy/book/src/development/proposals/roadmap-2021.md
@@ -52,8 +52,8 @@ In the following, plans to improve the usability are covered.
 
 #### No Output After `cargo check`
 
-Currently when `cargo clippy` is run after `cargo check`, it does not produce
-any output. This is especially problematic since `rust-analyzer` is on the rise
+Currently, when `cargo clippy` is run after `cargo check`, it does not produce
+any output. This is especially problematic since `rust-analyzer` is on the rise,
 and it uses `cargo check` for checking code. A fix is already implemented, but
 it still has to be pushed over the finish line. This also includes the
 stabilization of the `cargo clippy --fix` command or the support of multi-span
@@ -221,7 +221,7 @@ regarding the user facing issues.
 
 Rust's roadmap process was established by [RFC 1728] in 2016. Since then every
 year a roadmap was published, that defined the bigger plans for the coming
-years. This years roadmap can be found [here][Rust Roadmap 2021].
+years. This year roadmap can be found [here][Rust Roadmap 2021].
 
 [RFC 1728]: https://rust-lang.github.io/rfcs/1728-north-star.html
 
diff --git a/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md b/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md
index ea4978011b1..36d722609f4 100644
--- a/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md
+++ b/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md
@@ -16,7 +16,7 @@ lints. For non-trivial lints, it often requires nested pattern matching of AST /
 HIR nodes. For example, testing that an expression is a boolean literal requires
 the following checks:
 
-```rust
+```rust,ignore
 if let ast::ExprKind::Lit(lit) = &expr.node {
     if let ast::LitKind::Bool(_) = &lit.node {
         ...
@@ -28,7 +28,7 @@ Writing this kind of matching code quickly becomes a complex task and the
 resulting code is often hard to comprehend. The code below shows a simplified
 version of the pattern matching required by the `collapsible_if` lint:
 
-```rust
+```rust,ignore
 // simplified version of the collapsible_if lint
 if let ast::ExprKind::If(check, then, None) = &expr.node {
     if then.stmts.len() == 1 {
@@ -111,7 +111,7 @@ expressions that are boolean literals with value `false`.
 
 The pattern can then be used to implement lints in the following way:
 
-```rust
+```rust,ignore
 ...
 
 impl EarlyLintPass for MyAwesomeLint {
@@ -346,7 +346,7 @@ pattern!{
 one could get references to the nodes that matched the subpatterns in the
 following way:
 
-```rust
+```rust,ignore
 ...
 fn check_expr(expr: &syntax::ast::Expr) {
     if let Some(result) = my_pattern(expr) {
@@ -372,7 +372,7 @@ matches arrays that consist of any number of literal expressions. Because those
 expressions are named `foo`, the result struct contains a `foo` attribute which
 is a vector of expressions:
 
-```rust
+```rust,ignore
 ...
 if let Some(result) = my_pattern_seq(expr) {
     result.foo        // type: Vec<&syntax::ast::Expr>
@@ -394,7 +394,7 @@ In the pattern above, the `bar` name is only defined if the pattern matches a
 boolean literal. If it matches an integer literal, the name isn't set. To
 account for this, the result struct's `bar` attribute is an option type:
 
-```rust
+```rust,ignore
 ...
 if let Some(result) = my_pattern_alt(expr) {
     result.bar        // type: Option<&bool>
@@ -404,7 +404,7 @@ if let Some(result) = my_pattern_alt(expr) {
 It's also possible to use a name in multiple alternation branches if they have
 compatible types:
 
-```rust
+```rust,ignore
 pattern!{
     // matches if expression is a boolean or integer literal
     my_pattern_mult: Expr =
@@ -519,7 +519,7 @@ The `Alt`, `Seq` and `Opt` structs look like these:
 > Note: The current implementation can be found
 > [here](https://github.com/fkohlgrueber/pattern-matching/blob/dfb3bc9fbab69cec7c91e72564a63ebaa2ede638/pattern-match/src/matchers.rs#L35-L60).
 
-```rust
+```rust,ignore
 pub enum Alt<T> {
     Any,
     Elmt(Box<T>),
@@ -580,7 +580,7 @@ implementations is the `IsMatch` trait. It defines how to match *PatternTree*
 nodes against specific syntax tree nodes. A simplified implementation of the
 `IsMatch` trait is shown below:
 
-```rust
+```rust,ignore
 pub trait IsMatch<O> {
     fn is_match(&self, other: &'o O) -> bool;
 }
@@ -619,7 +619,7 @@ approach (matching against the coarse pattern first and checking for additional
 properties later) might be slower than the current practice of checking for
 structure and additional properties in one pass. For example, the following lint
 
-```rust
+```rust,ignore
 pattern!{
     pat_if_without_else: Expr =
         If(
@@ -644,7 +644,7 @@ first matches against the pattern and then checks that the `then` block doesn't
 start with a comment. Using clippy's current approach, it's possible to check
 for these conditions earlier:
 
-```rust
+```rust,ignore
 fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
     if_chain! {
         if let ast::ExprKind::If(ref check, ref then, None) = expr.node;
@@ -708,7 +708,7 @@ is similar to actual Rust syntax (probably like the `quote!` macro). For
 example, a pattern that matches `if` expressions that have `false` in their
 condition could look like this:
 
-```rust
+```rust,ignore
 if false {
     #[*]
 }
@@ -742,7 +742,7 @@ affects the structure of the resulting AST. `1 + 0 + 0` is parsed as `(1 + 0) +
 Another example of a problem would be named submatches. Take a look at this
 pattern:
 
-```rust
+```rust,ignore
 fn test() {
     1 #foo
 }
@@ -862,7 +862,7 @@ op b` and recommends changing it to `a op= b` requires that both occurrences of
 `a` are the same. Using `=#...` as syntax for backreferences, the lint could be
 implemented like this:
 
-```rust
+```rust,ignore
 pattern!{
     assign_op_pattern: Expr =
         Assign(_#target, Binary(_, =#target, _)
diff --git a/src/tools/clippy/book/src/development/type_checking.md b/src/tools/clippy/book/src/development/type_checking.md
new file mode 100644
index 00000000000..5ce434b99a1
--- /dev/null
+++ b/src/tools/clippy/book/src/development/type_checking.md
@@ -0,0 +1,144 @@
+# Type Checking
+
+When we work on a new lint or improve an existing lint, we might want
+to retrieve the type `Ty` of an expression `Expr` for a variety of
+reasons. This can be achieved by utilizing the [`LateContext`][LateContext]
+that is available for [`LateLintPass`][LateLintPass].
+
+## `LateContext` and `TypeckResults`
+
+The lint context [`LateContext`][LateContext] and [`TypeckResults`][TypeckResults]
+(returned by `LateContext::typeck_results`) are the two most useful data structures
+in `LateLintPass`. They allow us to jump to type definitions and other compilation
+stages such as HIR.
+
+> Note: `LateContext.typeck_results`'s return value is [`TypeckResults`][TypeckResults]
+> and is created in the type checking step, it includes useful information such as types of
+> expressions, ways to resolve methods and so on.
+
+`TypeckResults` contains useful methods such as [`expr_ty`][expr_ty],
+which gives us access to the underlying structure [`Ty`][Ty] of a given expression.
+
+```rust
+pub fn expr_ty(&self, expr: &Expr<'_>) -> Ty<'tcx>
+```
+
+As a side note, besides `expr_ty`, [`TypeckResults`][TypeckResults] contains a
+[`pat_ty()`][pat_ty] method that is useful for retrieving a type from a pattern.
+
+## `Ty`
+
+`Ty` struct contains the type information of an expression.
+Let's take a look at `rustc_middle`'s [`Ty`][Ty] struct to examine this struct:
+
+```rust
+pub struct Ty<'tcx>(Interned<'tcx, WithStableHash<TyS<'tcx>>>);
+```
+
+At a first glance, this struct looks quite esoteric. But at a closer look,
+we will see that this struct contains many useful methods for type checking.
+
+For instance, [`is_char`][is_char] checks if the given `Ty` struct corresponds
+to the primitive character type.
+
+### `is_*` Usage
+
+In some scenarios, all we need to do is check if the `Ty` of an expression
+is a specific type, such as `char` type, so we could write the following:
+
+```rust
+impl LateLintPass<'_> for MyStructLint {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+        // Get type of `expr`
+        let ty = cx.typeck_results().expr_ty(expr);
+        
+        // Check if the `Ty` of this expression is of character type
+        if ty.is_char() {
+            println!("Our expression is a char!");
+        }
+    }
+}
+```
+
+Furthermore, if we examine the [source code][is_char_source] for `is_char`,
+we find something very interesting:
+
+```rust
+#[inline]
+pub fn is_char(self) -> bool {
+    matches!(self.kind(), Char)
+}
+```
+
+Indeed, we just discovered `Ty`'s [`kind` method][kind], which provides us
+with [`TyKind`][TyKind] of a `Ty`.
+
+## `TyKind`
+
+`TyKind` defines the kinds of types in Rust's type system.
+Peeking into [`TyKind` documentation][TyKind], we will see that it is an
+enum of 27 variants, including items such as `Bool`, `Int`, `Ref`, etc.
+
+### `kind` Usage
+
+The `TyKind` of `Ty` can be returned by calling [`Ty.kind` method][kind].
+We often use this method to perform pattern matching in Clippy.
+
+For instance, if we want to check for a `struct`, we could examine if the
+`ty.kind` corresponds to an [`Adt`][Adt] (algebraic data type) and if its
+[`AdtDef`][AdtDef] is a struct:
+
+```rust
+impl LateLintPass<'_> for MyStructLint {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+        // Get type of `expr`
+        let ty = cx.typeck_results().expr_ty(expr);
+        // Match its kind to enter the type
+        match ty.kind {
+            ty::Adt(adt_def, _) if adt_def.is_struct() => println!("Our `expr` is a struct!"),
+            _ => ()
+        }
+    }
+}
+```
+
+## `hir::Ty` and `ty::Ty`
+
+We've been talking about [`ty::Ty`][middle_ty] this whole time without addressing [`hir::Ty`][hir_ty], but the latter
+is also important to understand.
+
+`hir::Ty` would represent *what* an user wrote, while `ty::Ty` would understand the meaning of it (because it has more
+information).
+
+**Example: `fn foo(x: u32) -> u32 { x }`**
+
+Here the HIR sees the types without "thinking" about them, it knows that the function takes an `u32` and returns
+an `u32`. But at the `ty::Ty` level the compiler understands that they're the same type, in-depth lifetimes, etc...
+
+you can use the [`hir_ty_to_ty`][hir_ty_to_ty] function to convert from a `hir::Ty` to a `ty::Ty`
+
+## Useful Links
+
+Below are some useful links to further explore the concepts covered
+in this chapter:
+
+- [Stages of compilation](https://rustc-dev-guide.rust-lang.org/compiler-src.html#the-main-stages-of-compilation)
+- [Diagnostic items](https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html)
+- [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html)
+- [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html)
+
+[Adt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html#variant.Adt
+[AdtDef]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/adt/struct.AdtDef.html
+[expr_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.expr_ty
+[is_char]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html#method.is_char
+[is_char_source]: https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_middle/ty/sty.rs.html#1831-1834
+[kind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html#method.kind
+[LateContext]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LateContext.html
+[LateLintPass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html
+[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TypeckResults.html#method.pat_ty
+[Ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html
+[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html
+[TypeckResults]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html
+[middle_ty]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_middle/ty/struct.Ty.html
+[hir_ty]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_hir/struct.Ty.html
+[hir_ty_to_ty]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_hir_analysis/fn.hir_ty_to_ty.html
diff --git a/src/tools/clippy/book/src/installation.md b/src/tools/clippy/book/src/installation.md
index cce888b17d4..d54fff9deba 100644
--- a/src/tools/clippy/book/src/installation.md
+++ b/src/tools/clippy/book/src/installation.md
@@ -17,8 +17,8 @@ $ rustup component add clippy [--toolchain=<name>]
 
 ## From Source
 
-Take a look at the [Basics] chapter in the Clippy developer guide to find step
-by step instructions on how to build and install Clippy from source.
+Take a look at the [Basics] chapter in the Clippy developer guide to find step-by-step
+instructions on how to build and install Clippy from source.
 
 [Basics]: development/basics.md#install-from-source
 [Usage]: usage.md
diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md
index 9ed6627b741..78e1a55cff3 100644
--- a/src/tools/clippy/book/src/lint_configuration.md
+++ b/src/tools/clippy/book/src/lint_configuration.md
@@ -54,6 +54,7 @@ Please use that command to update the file and do not edit it by hand.
 | [allow-mixed-uninlined-format-args](#allow-mixed-uninlined-format-args) | `true` |
 | [suppress-restriction-lint-in-const](#suppress-restriction-lint-in-const) | `false` |
 | [missing-docs-in-crate-items](#missing-docs-in-crate-items) | `false` |
+| [future-size-threshold](#future-size-threshold) | `16384` |
 
 ### arithmetic-side-effects-allowed
 Suppress checking of the passed type names in all types of operations.
@@ -130,6 +131,7 @@ Suppress lints whenever the suggested change would cause breakage for other crat
 * [option_option](https://rust-lang.github.io/rust-clippy/master/index.html#option_option)
 * [linkedlist](https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist)
 * [rc_mutex](https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex)
+* [unnecessary_box_returns](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns)
 
 
 ### msrv
@@ -193,7 +195,7 @@ The maximum cognitive complexity a function can have
 ### disallowed-names
 The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value
 `".."` can be used as part of the list to indicate, that the configured values should be appended to the
-default configuration of Clippy. By default any configuration will replace the default value.
+default configuration of Clippy. By default, any configuration will replace the default value.
 
 **Default Value:** `["foo", "baz", "quux"]` (`Vec<String>`)
 
@@ -203,7 +205,7 @@ default configuration of Clippy. By default any configuration will replace the d
 ### doc-valid-idents
 The list of words this lint should not consider as identifiers needing ticks. The value
 `".."` can be used as part of the list to indicate, that the configured values should be appended to the
-default configuration of Clippy. By default any configuraction will replace the default value. For example:
+default configuration of Clippy. By default, any configuration will replace the default value. For example:
 * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`.
 * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list.
 
@@ -413,7 +415,7 @@ For internal testing only, ignores the current `publish` settings in the Cargo m
 Enforce the named macros always use the braces specified.
 
 A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro
-is could be used with a full path two `MacroMatcher`s have to be added one with the full path
+could be used with a full path two `MacroMatcher`s have to be added one with the full path
 `crate_name::macro_name` and one with just the macro name.
 
 **Default Value:** `[]` (`Vec<crate::nonstandard_macro_braces::MacroMatcher>`)
@@ -447,7 +449,7 @@ Whether to apply the raw pointer heuristic to determine if a type is `Send`.
 
 ### max-suggested-slice-pattern-length
 When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in
-the slice pattern that is suggested. If more elements would be necessary, the lint is suppressed.
+the slice pattern that is suggested. If more elements are necessary, the lint is suppressed.
 For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements.
 
 **Default Value:** `3` (`u64`)
@@ -551,4 +553,12 @@ crate. For example, `pub(crate)` items.
 * [missing_docs_in_private_items](https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items)
 
 
+### future-size-threshold
+The maximum byte size a `Future` can have, before it triggers the `clippy::large_futures` lint
+
+**Default Value:** `16384` (`u64`)
+
+* [large_futures](https://rust-lang.github.io/rust-clippy/master/index.html#large_futures)
+
+
 
diff --git a/src/tools/clippy/book/src/lints.md b/src/tools/clippy/book/src/lints.md
index 35e30960b56..442dc63914e 100644
--- a/src/tools/clippy/book/src/lints.md
+++ b/src/tools/clippy/book/src/lints.md
@@ -17,7 +17,7 @@ The different lint groups were defined in the [Clippy 1.0 RFC].
 The `clippy::correctness` group is the only lint group in Clippy which lints are
 deny-by-default and abort the compilation when triggered. This is for good
 reason: If you see a `correctness` lint, it means that your code is outright
-wrong or useless and you should try to fix it.
+wrong or useless, and you should try to fix it.
 
 Lints in this category are carefully picked and should be free of false
 positives. So just `#[allow]`ing those lints is not recommended.
@@ -41,7 +41,7 @@ simplify your code. It mostly focuses on code that can be written in a shorter
 and more readable way, while preserving the semantics.
 
 If you should see a complexity lint, it usually means that you can remove or
-replace some code and it is recommended to do so. However, if you need the more
+replace some code, and it is recommended to do so. However, if you need the more
 complex code for some expressiveness reason, it is recommended to allow
 complexity lints on a case-by-case basis.
 
@@ -50,9 +50,9 @@ complexity lints on a case-by-case basis.
 The `clippy::perf` group gives you suggestions on how you can increase the
 performance of your code. Those lints are mostly about code that the compiler
 can't trivially optimize, but has to be written in a slightly different way to
-make the optimizer's job easier.
+make the optimizer job easier.
 
-Perf lints are usually easy to apply and it is recommended to do so.
+Perf lints are usually easy to apply, and it is recommended to do so.
 
 ## Style
 
@@ -91,7 +91,7 @@ and your use case.
 
 Lints from this group will restrict you in some way. If you enable a restriction
 lint for your crate it is recommended to also fix code that this lint triggers
-on. However, those lints are really strict by design and you might want to
+on. However, those lints are really strict by design, and you might want to
 `#[allow]` them in some special cases, with a comment justifying that.
 
 ## Cargo
diff --git a/src/tools/clippy/book/src/usage.md b/src/tools/clippy/book/src/usage.md
index 61a90445d75..32084a9199b 100644
--- a/src/tools/clippy/book/src/usage.md
+++ b/src/tools/clippy/book/src/usage.md
@@ -19,7 +19,7 @@ cargo clippy
 ### Lint configuration
 
 The above command will run the default set of lints, which are included in the
-lint group `clippy::all`. You might want to use even more lints or you might not
+lint group `clippy::all`. You might want to use even more lints, or you may not
 agree with every Clippy lint, and for that there are ways to configure lint
 levels.
 
@@ -98,7 +98,7 @@ other of Clippy's lint groups.
 You can configure lint levels in source code the same way you can configure
 `rustc` lints:
 
-```rust
+```rust,ignore
 #![allow(clippy::style)]
 
 #[warn(clippy::double_neg)]
diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs
index 8871873c661..3a8b070d735 100644
--- a/src/tools/clippy/clippy_dev/src/lib.rs
+++ b/src/tools/clippy/clippy_dev/src/lib.rs
@@ -1,3 +1,4 @@
+#![feature(lazy_cell)]
 #![feature(let_chains)]
 #![feature(rustc_private)]
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs
index 420214d9256..13a27703427 100644
--- a/src/tools/clippy/clippy_dev/src/new_lint.rs
+++ b/src/tools/clippy/clippy_dev/src/new_lint.rs
@@ -369,9 +369,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R
                     }}
                     todo!();
                 }}
-           "#,
-            context_import = context_import,
-            name_upper = name_upper,
+           "#
         );
     } else {
         let _: fmt::Result = writedoc!(
@@ -385,9 +383,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R
                 pub(super) fn check(cx: &{context_import}) {{
                     todo!();
                 }}
-           "#,
-            context_import = context_import,
-            name_upper = name_upper,
+           "#
         );
     }
 
diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs
index 779e4d0e1e3..95222a9acdf 100644
--- a/src/tools/clippy/clippy_dev/src/update_lints.rs
+++ b/src/tools/clippy/clippy_dev/src/update_lints.rs
@@ -537,17 +537,13 @@ fn declare_deprecated(name: &str, path: &Path, reason: &str) -> io::Result<()> {
             /// Nothing. This lint has been deprecated.
             ///
             /// ### Deprecation reason
-            /// {}
-            #[clippy::version = \"{}\"]
-            pub {},
-            \"{}\"
+            /// {deprecation_reason}
+            #[clippy::version = \"{version}\"]
+            pub {name},
+            \"{reason}\"
         }}
 
-        ",
-        deprecation_reason,
-        version,
-        name,
-        reason,
+        "
     )
 }
 
diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml
index 0b3846c1316..18e8bf77225 100644
--- a/src/tools/clippy/clippy_lints/Cargo.toml
+++ b/src/tools/clippy/clippy_lints/Cargo.toml
@@ -9,6 +9,7 @@ keywords = ["clippy", "lint", "plugin"]
 edition = "2021"
 
 [dependencies]
+arrayvec = { version = "0.7", default-features = false }
 cargo_metadata = "0.15.3"
 clippy_utils = { path = "../clippy_utils" }
 declare_clippy_lint = { path = "../declare_clippy_lint" }
diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs
index 29fde9336c0..455f0df7cd0 100644
--- a/src/tools/clippy/clippy_lints/src/booleans.rs
+++ b/src/tools/clippy/clippy_lints/src/booleans.rs
@@ -7,7 +7,7 @@ use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
 use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, UnOp};
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_lint::{LateContext, LateLintPass, Level};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::def_id::LocalDefId;
 use rustc_span::source_map::Span;
@@ -430,23 +430,25 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
                 }
             }
             let nonminimal_bool_lint = |suggestions: Vec<_>| {
-                span_lint_hir_and_then(
-                    self.cx,
-                    NONMINIMAL_BOOL,
-                    e.hir_id,
-                    e.span,
-                    "this boolean expression can be simplified",
-                    |diag| {
-                        diag.span_suggestions(
-                            e.span,
-                            "try",
-                            suggestions.into_iter(),
-                            // nonminimal_bool can produce minimal but
-                            // not human readable expressions (#3141)
-                            Applicability::Unspecified,
-                        );
-                    },
-                );
+                if self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, e.hir_id).0 != Level::Allow {
+                    span_lint_hir_and_then(
+                        self.cx,
+                        NONMINIMAL_BOOL,
+                        e.hir_id,
+                        e.span,
+                        "this boolean expression can be simplified",
+                        |diag| {
+                            diag.span_suggestions(
+                                e.span,
+                                "try",
+                                suggestions.into_iter(),
+                                // nonminimal_bool can produce minimal but
+                                // not human readable expressions (#3141)
+                                Applicability::Unspecified,
+                            );
+                        },
+                    );
+                }
             };
             if improvements.is_empty() {
                 let mut visitor = NotSimplificationVisitor { cx: self.cx };
@@ -498,6 +500,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NotSimplificationVisitor<'a, 'tcx> {
         if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind &&
             !inner.span.from_expansion() &&
             let Some(suggestion) = simplify_not(self.cx, inner)
+			&& self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow
         {
             span_lint_and_sugg(
                 self.cx,
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
index 823970e35ab..95c2ecbf791 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
@@ -2,8 +2,9 @@ use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
 use clippy_utils::expr_or_init;
 use clippy_utils::source::snippet;
+use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize};
-use rustc_errors::{Applicability, SuggestionStyle};
+use rustc_errors::{Applicability, Diagnostic, SuggestionStyle};
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::LateContext;
@@ -163,19 +164,34 @@ pub(super) fn check(
         _ => return,
     };
 
-    let name_of_cast_from = snippet(cx, cast_expr.span, "..");
-    let cast_to_snip = snippet(cx, cast_to_span, "..");
-    let suggestion = format!("{cast_to_snip}::try_from({name_of_cast_from})");
-
     span_lint_and_then(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg, |diag| {
         diag.help("if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...");
-        diag.span_suggestion_with_style(
-            expr.span,
-            "... or use `try_from` and handle the error accordingly",
-            suggestion,
-            Applicability::Unspecified,
-            // always show the suggestion in a separate line
-            SuggestionStyle::ShowAlways,
-        );
+        if !cast_from.is_floating_point() {
+            offer_suggestion(cx, expr, cast_expr, cast_to_span, diag);
+        }
     });
 }
+
+fn offer_suggestion(
+    cx: &LateContext<'_>,
+    expr: &Expr<'_>,
+    cast_expr: &Expr<'_>,
+    cast_to_span: Span,
+    diag: &mut Diagnostic,
+) {
+    let cast_to_snip = snippet(cx, cast_to_span, "..");
+    let suggestion = if cast_to_snip == "_" {
+        format!("{}.try_into()", Sugg::hir(cx, cast_expr, "..").maybe_par())
+    } else {
+        format!("{cast_to_snip}::try_from({})", Sugg::hir(cx, cast_expr, ".."))
+    };
+
+    diag.span_suggestion_with_style(
+        expr.span,
+        "... or use `try_from` and handle the error accordingly",
+        suggestion,
+        Applicability::Unspecified,
+        // always show the suggestion in a separate line
+        SuggestionStyle::ShowAlways,
+    );
+}
diff --git a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs
index 10f2bef268a..5e2eb5789f6 100644
--- a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs
+++ b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs
@@ -1,9 +1,9 @@
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
 use clippy_utils::visitors::for_each_expr_with_closures;
 use clippy_utils::{get_enclosing_block, get_parent_node, path_to_local_id};
 use core::ops::ControlFlow;
-use rustc_hir::{Block, ExprKind, HirId, Local, Node, PatKind};
+use rustc_hir::{Block, ExprKind, HirId, LangItem, Local, Node, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::symbol::sym;
@@ -44,7 +44,8 @@ declare_clippy_lint! {
 }
 declare_lint_pass!(CollectionIsNeverRead => [COLLECTION_IS_NEVER_READ]);
 
-static COLLECTIONS: [Symbol; 10] = [
+// Add `String` here when it is added to diagnostic items
+static COLLECTIONS: [Symbol; 9] = [
     sym::BTreeMap,
     sym::BTreeSet,
     sym::BinaryHeap,
@@ -52,7 +53,6 @@ static COLLECTIONS: [Symbol; 10] = [
     sym::HashSet,
     sym::LinkedList,
     sym::Option,
-    sym::String,
     sym::Vec,
     sym::VecDeque,
 ];
@@ -60,8 +60,7 @@ static COLLECTIONS: [Symbol; 10] = [
 impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead {
     fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
         // Look for local variables whose type is a container. Search surrounding bock for read access.
-        let ty = cx.typeck_results().pat_ty(local.pat);
-        if COLLECTIONS.iter().any(|&sym| is_type_diagnostic_item(cx, ty, sym))
+        if match_acceptable_type(cx, local, &COLLECTIONS)
             && let PatKind::Binding(_, local_id, _, _) = local.pat.kind
             && let Some(enclosing_block) = get_enclosing_block(cx, local.hir_id)
             && has_no_read_access(cx, local_id, enclosing_block)
@@ -71,6 +70,13 @@ impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead {
     }
 }
 
+fn match_acceptable_type(cx: &LateContext<'_>, local: &Local<'_>, collections: &[rustc_span::Symbol]) -> bool {
+    let ty = cx.typeck_results().pat_ty(local.pat);
+    collections.iter().any(|&sym| is_type_diagnostic_item(cx, ty, sym))
+    // String type is a lang item but not a diagnostic item for now so we need a separate check
+        || is_type_lang_item(cx, ty, LangItem::String)
+}
+
 fn has_no_read_access<'tcx>(cx: &LateContext<'tcx>, id: HirId, block: &'tcx Block<'tcx>) -> bool {
     let mut has_access = false;
     let mut has_read_access = false;
@@ -95,9 +101,9 @@ fn has_no_read_access<'tcx>(cx: &LateContext<'tcx>, id: HirId, block: &'tcx Bloc
             return ControlFlow::Continue(());
         }
 
-        // Method call on `id` in a statement ignores any return value, so it's not a read access:
+        // Look for method call with receiver `id`. It might be a non-read access:
         //
-        // id.foo(...); // Not reading `id`.
+        // id.foo(args)
         //
         // Only assuming this for "official" methods defined on the type. For methods defined in extension
         // traits (identified as local, based on the orphan rule), pessimistically assume that they might
@@ -105,11 +111,24 @@ fn has_no_read_access<'tcx>(cx: &LateContext<'tcx>, id: HirId, block: &'tcx Bloc
         if let Some(Node::Expr(parent)) = get_parent_node(cx.tcx, expr.hir_id)
             && let ExprKind::MethodCall(_, receiver, _, _) = parent.kind
             && path_to_local_id(receiver, id)
-            && let Some(Node::Stmt(..)) = get_parent_node(cx.tcx, parent.hir_id)
             && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id)
             && !method_def_id.is_local()
         {
-            return ControlFlow::Continue(());
+            // The method call is a statement, so the return value is not used. That's not a read access:
+            //
+            // id.foo(args);
+            if let Some(Node::Stmt(..)) = get_parent_node(cx.tcx, parent.hir_id) {
+                return ControlFlow::Continue(());
+            }
+
+            // The method call is not a statement, so its return value is used somehow but its type is the
+            // unit type, so this is not a real read access. Examples:
+            //
+            // let y = x.clear();
+            // println!("{:?}", x.clear());
+            if cx.typeck_results().expr_ty(parent).is_unit() {
+                return ControlFlow::Continue(());
+            }
         }
 
         // Any other access to `id` is a read access. Stop searching.
diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs
index 8ca91301472..f24dab62780 100644
--- a/src/tools/clippy/clippy_lints/src/declared_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs
@@ -218,6 +218,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR_INFO,
     crate::large_const_arrays::LARGE_CONST_ARRAYS_INFO,
     crate::large_enum_variant::LARGE_ENUM_VARIANT_INFO,
+    crate::large_futures::LARGE_FUTURES_INFO,
     crate::large_include_file::LARGE_INCLUDE_FILE_INFO,
     crate::large_stack_arrays::LARGE_STACK_ARRAYS_INFO,
     crate::len_zero::COMPARISON_TO_EMPTY_INFO,
@@ -231,6 +232,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::let_with_type_underscore::LET_WITH_TYPE_UNDERSCORE_INFO,
     crate::lifetimes::EXTRA_UNUSED_LIFETIMES_INFO,
     crate::lifetimes::NEEDLESS_LIFETIMES_INFO,
+    crate::lines_filter_map_ok::LINES_FILTER_MAP_OK_INFO,
     crate::literal_representation::DECIMAL_LITERAL_REPRESENTATION_INFO,
     crate::literal_representation::INCONSISTENT_DIGIT_GROUPING_INFO,
     crate::literal_representation::LARGE_DIGIT_GROUPS_INFO,
@@ -267,6 +269,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO,
     crate::manual_rem_euclid::MANUAL_REM_EUCLID_INFO,
     crate::manual_retain::MANUAL_RETAIN_INFO,
+    crate::manual_slice_size_calculation::MANUAL_SLICE_SIZE_CALCULATION_INFO,
     crate::manual_string_new::MANUAL_STRING_NEW_INFO,
     crate::manual_strip::MANUAL_STRIP_INFO,
     crate::map_unit_fn::OPTION_MAP_UNIT_FN_INFO,
@@ -307,6 +310,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS_INFO,
     crate::methods::CHARS_LAST_CMP_INFO,
     crate::methods::CHARS_NEXT_CMP_INFO,
+    crate::methods::CLEAR_WITH_DRAIN_INFO,
     crate::methods::CLONED_INSTEAD_OF_COPIED_INFO,
     crate::methods::CLONE_DOUBLE_REF_INFO,
     crate::methods::CLONE_ON_COPY_INFO,
@@ -565,6 +569,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::strings::STR_TO_STRING_INFO,
     crate::strings::TRIM_SPLIT_WHITESPACE_INFO,
     crate::strlen_on_c_strings::STRLEN_ON_C_STRINGS_INFO,
+    crate::suspicious_doc_comments::SUSPICIOUS_DOC_COMMENTS_INFO,
     crate::suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS_INFO,
     crate::suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL_INFO,
     crate::suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL_INFO,
@@ -574,6 +579,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::swap_ptr_to_ref::SWAP_PTR_TO_REF_INFO,
     crate::tabs_in_doc_comments::TABS_IN_DOC_COMMENTS_INFO,
     crate::temporary_assignment::TEMPORARY_ASSIGNMENT_INFO,
+    crate::tests_outside_test_module::TESTS_OUTSIDE_TEST_MODULE_INFO,
     crate::to_digit_is_some::TO_DIGIT_IS_SOME_INFO,
     crate::trailing_empty_array::TRAILING_EMPTY_ARRAY_INFO,
     crate::trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS_INFO,
@@ -616,6 +622,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::unit_types::UNIT_CMP_INFO,
     crate::unnamed_address::FN_ADDRESS_COMPARISONS_INFO,
     crate::unnamed_address::VTABLE_ADDRESS_COMPARISONS_INFO,
+    crate::unnecessary_box_returns::UNNECESSARY_BOX_RETURNS_INFO,
     crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO,
     crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO,
     crate::unnecessary_struct_initialization::UNNECESSARY_STRUCT_INITIALIZATION_INFO,
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs
index 084190f0013..c9fad98e437 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs
@@ -32,7 +32,7 @@ declare_clippy_lint! {
     /// ### Example
     /// ```rust
     /// // Assuming that `clippy.toml` contains the following line:
-    /// // allowed-locales = ["Latin", "Cyrillic"]
+    /// // allowed-scripts = ["Latin", "Cyrillic"]
     /// let counter = 10; // OK, latin is allowed.
     /// let счётчик = 10; // OK, cyrillic is allowed.
     /// let zähler = 10; // OK, it's still latin.
diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs
index c0ea6f338a2..315df6c714f 100644
--- a/src/tools/clippy/clippy_lints/src/explicit_write.rs
+++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::macros::FormatArgsExpn;
+use clippy_utils::macros::{find_format_args, format_args_inputs_span};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::{is_expn_of, match_function_call, paths};
 use if_chain::if_chain;
@@ -8,7 +8,7 @@ use rustc_hir::def::Res;
 use rustc_hir::{BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
+use rustc_span::{sym, ExpnId};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -43,23 +43,22 @@ declare_lint_pass!(ExplicitWrite => [EXPLICIT_WRITE]);
 
 impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if_chain! {
-            // match call to unwrap
-            if let ExprKind::MethodCall(unwrap_fun, write_call, [], _) = expr.kind;
-            if unwrap_fun.ident.name == sym::unwrap;
+        // match call to unwrap
+        if let ExprKind::MethodCall(unwrap_fun, write_call, [], _) = expr.kind
+            && unwrap_fun.ident.name == sym::unwrap
             // match call to write_fmt
-            if let ExprKind::MethodCall(write_fun, write_recv, [write_arg], _) = look_in_block(cx, &write_call.kind);
-            if write_fun.ident.name == sym!(write_fmt);
+            && let ExprKind::MethodCall(write_fun, write_recv, [write_arg], _) = look_in_block(cx, &write_call.kind)
+            && write_fun.ident.name == sym!(write_fmt)
             // match calls to std::io::stdout() / std::io::stderr ()
-            if let Some(dest_name) = if match_function_call(cx, write_recv, &paths::STDOUT).is_some() {
+            && let Some(dest_name) = if match_function_call(cx, write_recv, &paths::STDOUT).is_some() {
                 Some("stdout")
             } else if match_function_call(cx, write_recv, &paths::STDERR).is_some() {
                 Some("stderr")
             } else {
                 None
-            };
-            if let Some(format_args) = FormatArgsExpn::parse(cx, write_arg);
-            then {
+            }
+        {
+            find_format_args(cx, write_arg, ExpnId::root(), |format_args| {
                 let calling_macro =
                     // ordering is important here, since `writeln!` uses `write!` internally
                     if is_expn_of(write_call.span, "writeln").is_some() {
@@ -92,7 +91,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
                 let mut applicability = Applicability::MachineApplicable;
                 let inputs_snippet = snippet_with_applicability(
                     cx,
-                    format_args.inputs_span(),
+                    format_args_inputs_span(format_args),
                     "..",
                     &mut applicability,
                 );
@@ -104,8 +103,8 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
                     "try this",
                     format!("{prefix}{sugg_mac}!({inputs_snippet})"),
                     applicability,
-                )
-            }
+                );
+            });
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
index 20565e1d232..eeb4de8b58f 100644
--- a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
+++ b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
@@ -1,10 +1,10 @@
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
 use clippy_utils::trait_ref_of_method;
-use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::MultiSpan;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_impl_item, walk_item, walk_param_bound, walk_ty, Visitor};
 use rustc_hir::{
-    BodyId, ExprKind, GenericBound, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind,
+    BodyId, ExprKind, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind,
     PredicateOrigin, Ty, TyKind, WherePredicate,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -53,13 +53,19 @@ impl ExtraUnusedTypeParameters {
         }
     }
 
-    /// Don't lint external macros or functions with empty bodies. Also, don't lint public items if
-    /// the `avoid_breaking_exported_api` config option is set.
-    fn check_false_positive(&self, cx: &LateContext<'_>, span: Span, def_id: LocalDefId, body_id: BodyId) -> bool {
+    /// Don't lint external macros or functions with empty bodies. Also, don't lint exported items
+    /// if the `avoid_breaking_exported_api` config option is set.
+    fn is_empty_exported_or_macro(
+        &self,
+        cx: &LateContext<'_>,
+        span: Span,
+        def_id: LocalDefId,
+        body_id: BodyId,
+    ) -> bool {
         let body = cx.tcx.hir().body(body_id).value;
         let fn_empty = matches!(&body.kind, ExprKind::Block(blk, None) if blk.stmts.is_empty() && blk.expr.is_none());
         let is_exported = cx.effective_visibilities.is_exported(def_id);
-        in_external_macro(cx.sess(), span) || (self.avoid_breaking_exported_api && is_exported) || fn_empty
+        in_external_macro(cx.sess(), span) || fn_empty || (is_exported && self.avoid_breaking_exported_api)
     }
 }
 
@@ -69,85 +75,129 @@ impl_lint_pass!(ExtraUnusedTypeParameters => [EXTRA_UNUSED_TYPE_PARAMETERS]);
 /// trait bounds those parameters have.
 struct TypeWalker<'cx, 'tcx> {
     cx: &'cx LateContext<'tcx>,
-    /// Collection of all the function's type parameters.
+    /// Collection of the function's type parameters. Once the function has been walked, this will
+    /// contain only unused type parameters.
     ty_params: FxHashMap<DefId, Span>,
-    /// Collection of any (inline) trait bounds corresponding to each type parameter.
-    bounds: FxHashMap<DefId, Span>,
+    /// Collection of any inline trait bounds corresponding to each type parameter.
+    inline_bounds: FxHashMap<DefId, Span>,
+    /// Collection of any type parameters with trait bounds that appear in a where clause.
+    where_bounds: FxHashSet<DefId>,
     /// The entire `Generics` object of the function, useful for querying purposes.
     generics: &'tcx Generics<'tcx>,
-    /// The value of this will remain `true` if *every* parameter:
-    ///   1. Is a type parameter, and
-    ///   2. Goes unused in the function.
-    /// Otherwise, if any type parameters end up being used, or if any lifetime or const-generic
-    /// parameters are present, this will be set to `false`.
-    all_params_unused: bool,
 }
 
 impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> {
     fn new(cx: &'cx LateContext<'tcx>, generics: &'tcx Generics<'tcx>) -> Self {
-        let mut all_params_unused = true;
         let ty_params = generics
             .params
             .iter()
-            .filter_map(|param| {
-                if let GenericParamKind::Type { synthetic, .. } = param.kind {
-                    (!synthetic).then_some((param.def_id.into(), param.span))
-                } else {
-                    if !param.is_elided_lifetime() {
-                        all_params_unused = false;
-                    }
-                    None
-                }
+            .filter_map(|param| match param.kind {
+                GenericParamKind::Type { synthetic, .. } if !synthetic => Some((param.def_id.into(), param.span)),
+                _ => None,
             })
             .collect();
 
         Self {
             cx,
             ty_params,
-            bounds: FxHashMap::default(),
+            inline_bounds: FxHashMap::default(),
+            where_bounds: FxHashSet::default(),
             generics,
-            all_params_unused,
         }
     }
 
-    fn mark_param_used(&mut self, def_id: DefId) {
-        if self.ty_params.remove(&def_id).is_some() {
-            self.all_params_unused = false;
-        }
+    fn get_bound_span(&self, param: &'tcx GenericParam<'tcx>) -> Span {
+        self.inline_bounds
+            .get(&param.def_id.to_def_id())
+            .map_or(param.span, |bound_span| param.span.with_hi(bound_span.hi()))
+    }
+
+    fn emit_help(&self, spans: Vec<Span>, msg: &str, help: &'static str) {
+        span_lint_and_help(self.cx, EXTRA_UNUSED_TYPE_PARAMETERS, spans, msg, None, help);
+    }
+
+    fn emit_sugg(&self, spans: Vec<Span>, msg: &str, help: &'static str) {
+        let suggestions: Vec<(Span, String)> = spans.iter().copied().zip(std::iter::repeat(String::new())).collect();
+        span_lint_and_then(self.cx, EXTRA_UNUSED_TYPE_PARAMETERS, spans, msg, |diag| {
+            diag.multipart_suggestion(help, suggestions, Applicability::MachineApplicable);
+        });
     }
 
     fn emit_lint(&self) {
-        let (msg, help) = match self.ty_params.len() {
+        let explicit_params = self
+            .generics
+            .params
+            .iter()
+            .filter(|param| !param.is_elided_lifetime() && !param.is_impl_trait())
+            .collect::<Vec<_>>();
+
+        let extra_params = explicit_params
+            .iter()
+            .enumerate()
+            .filter(|(_, param)| self.ty_params.contains_key(&param.def_id.to_def_id()))
+            .collect::<Vec<_>>();
+
+        let (msg, help) = match extra_params.len() {
             0 => return,
             1 => (
-                "type parameter goes unused in function definition",
+                format!(
+                    "type parameter `{}` goes unused in function definition",
+                    extra_params[0].1.name.ident()
+                ),
                 "consider removing the parameter",
             ),
             _ => (
-                "type parameters go unused in function definition",
+                format!(
+                    "type parameters go unused in function definition: {}",
+                    extra_params
+                        .iter()
+                        .map(|(_, param)| param.name.ident().to_string())
+                        .collect::<Vec<_>>()
+                        .join(", ")
+                ),
                 "consider removing the parameters",
             ),
         };
 
-        let source_map = self.cx.sess().source_map();
-        let span = if self.all_params_unused {
-            self.generics.span.into() // Remove the entire list of generics
+        // If any parameters are bounded in where clauses, don't try to form a suggestion.
+        // Otherwise, the leftover where bound would produce code that wouldn't compile.
+        if extra_params
+            .iter()
+            .any(|(_, param)| self.where_bounds.contains(&param.def_id.to_def_id()))
+        {
+            let spans = extra_params
+                .iter()
+                .map(|(_, param)| self.get_bound_span(param))
+                .collect::<Vec<_>>();
+            self.emit_help(spans, &msg, help);
         } else {
-            MultiSpan::from_spans(
-                self.ty_params
+            let spans = if explicit_params.len() == extra_params.len() {
+                vec![self.generics.span] // Remove the entire list of generics
+            } else {
+                let mut end: Option<LocalDefId> = None;
+                extra_params
                     .iter()
-                    .map(|(def_id, &span)| {
-                        // Extend the span past any trait bounds, and include the comma at the end.
-                        let span_to_extend = self.bounds.get(def_id).copied().map_or(span, Span::shrink_to_hi);
-                        let comma_range = source_map.span_extend_to_next_char(span_to_extend, '>', false);
-                        let comma_span = source_map.span_through_char(comma_range, ',');
-                        span.with_hi(comma_span.hi())
+                    .rev()
+                    .map(|(idx, param)| {
+                        if let Some(next) = explicit_params.get(idx + 1) && end != Some(next.def_id) {
+                        // Extend the current span forward, up until the next param in the list.
+                        param.span.until(next.span)
+                    } else {
+                        // Extend the current span back to include the comma following the previous
+                        // param. If the span of the next param in the list has already been
+                        // extended, we continue the chain. This is why we're iterating in reverse.
+                        end = Some(param.def_id);
+
+                        // idx will never be 0, else we'd be removing the entire list of generics
+                        let prev = explicit_params[idx - 1];
+                        let prev_span = self.get_bound_span(prev);
+                        self.get_bound_span(param).with_lo(prev_span.hi())
+                    }
                     })
-                    .collect(),
-            )
+                    .collect()
+            };
+            self.emit_sugg(spans, &msg, help);
         };
-
-        span_lint_and_help(self.cx, EXTRA_UNUSED_TYPE_PARAMETERS, span, msg, None, help);
     }
 }
 
@@ -162,7 +212,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
 
     fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) {
         if let Some((def_id, _)) = t.peel_refs().as_generic_param() {
-            self.mark_param_used(def_id);
+            self.ty_params.remove(&def_id);
         } else if let TyKind::OpaqueDef(id, _, _) = t.kind {
             // Explicitly walk OpaqueDef. Normally `walk_ty` would do the job, but it calls
             // `visit_nested_item`, which checks that `Self::NestedFilter::INTER` is set. We're
@@ -176,9 +226,18 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
 
     fn visit_where_predicate(&mut self, predicate: &'tcx WherePredicate<'tcx>) {
         if let WherePredicate::BoundPredicate(predicate) = predicate {
-            // Collect spans for any bounds on type parameters. We only keep bounds that appear in
-            // the list of generics (not in a where-clause).
+            // Collect spans for any bounds on type parameters.
             if let Some((def_id, _)) = predicate.bounded_ty.peel_refs().as_generic_param() {
+                match predicate.origin {
+                    PredicateOrigin::GenericParam => {
+                        self.inline_bounds.insert(def_id, predicate.span);
+                    },
+                    PredicateOrigin::WhereClause => {
+                        self.where_bounds.insert(def_id);
+                    },
+                    PredicateOrigin::ImplTrait => (),
+                }
+
                 // If the bound contains non-public traits, err on the safe side and don't lint the
                 // corresponding parameter.
                 if !predicate
@@ -187,12 +246,10 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
                     .filter_map(bound_to_trait_def_id)
                     .all(|id| self.cx.effective_visibilities.is_exported(id))
                 {
-                    self.mark_param_used(def_id);
-                } else if let PredicateOrigin::GenericParam = predicate.origin {
-                    self.bounds.insert(def_id, predicate.span);
+                    self.ty_params.remove(&def_id);
                 }
             }
-            // Only walk the right-hand side of where-bounds
+            // Only walk the right-hand side of where bounds
             for bound in predicate.bounds {
                 walk_param_bound(self, bound);
             }
@@ -207,7 +264,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
 impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
         if let ItemKind::Fn(_, generics, body_id) = item.kind
-            && !self.check_false_positive(cx, item.span, item.owner_id.def_id, body_id)
+            && !self.is_empty_exported_or_macro(cx, item.span, item.owner_id.def_id, body_id)
         {
             let mut walker = TypeWalker::new(cx, generics);
             walk_item(&mut walker, item);
@@ -219,7 +276,7 @@ impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters {
         // Only lint on inherent methods, not trait methods.
         if let ImplItemKind::Fn(.., body_id) = item.kind
             && trait_ref_of_method(cx, item.owner_id.def_id).is_none()
-            && !self.check_false_positive(cx, item.span, item.owner_id.def_id, body_id)
+            && !self.is_empty_exported_or_macro(cx, item.span, item.owner_id.def_id, body_id)
         {
             let mut walker = TypeWalker::new(cx, item.generics);
             walk_impl_item(&mut walker, item);
diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs
index 8040938c626..d34d6e9279e 100644
--- a/src/tools/clippy/clippy_lints/src/format.rs
+++ b/src/tools/clippy/clippy_lints/src/format.rs
@@ -1,14 +1,13 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn};
-use clippy_utils::source::snippet_with_context;
+use clippy_utils::macros::{find_format_arg_expr, find_format_args, root_macro_call_first_node};
+use clippy_utils::source::{snippet_opt, snippet_with_context};
 use clippy_utils::sugg::Sugg;
-use if_chain::if_chain;
+use rustc_ast::{FormatArgsPiece, FormatOptions, FormatTrait};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::kw;
 use rustc_span::{sym, Span};
 
 declare_clippy_lint! {
@@ -44,55 +43,53 @@ declare_lint_pass!(UselessFormat => [USELESS_FORMAT]);
 
 impl<'tcx> LateLintPass<'tcx> for UselessFormat {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        let (format_args, call_site) = if_chain! {
-            if let Some(macro_call) = root_macro_call_first_node(cx, expr);
-            if cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id);
-            if let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, macro_call.expn);
-            then {
-                (format_args, macro_call.span)
-            } else {
-                return
-            }
-        };
+        let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
+        if !cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) {
+            return;
+        }
+
+        find_format_args(cx, expr, macro_call.expn, |format_args| {
+            let mut applicability = Applicability::MachineApplicable;
+            let call_site = macro_call.span;
 
-        let mut applicability = Applicability::MachineApplicable;
-        if format_args.args.is_empty() {
-            match *format_args.format_string.parts {
-                [] => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability),
-                [_] => {
+            match (format_args.arguments.all_args(), &format_args.template[..]) {
+                ([], []) => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability),
+                ([], [_]) => {
                     // Simulate macro expansion, converting {{ and }} to { and }.
-                    let s_expand = format_args.format_string.snippet.replace("{{", "{").replace("}}", "}");
+                    let Some(snippet) = snippet_opt(cx, format_args.span) else { return };
+                    let s_expand = snippet.replace("{{", "{").replace("}}", "}");
                     let sugg = format!("{s_expand}.to_string()");
                     span_useless_format(cx, call_site, sugg, applicability);
                 },
-                [..] => {},
-            }
-        } else if let [arg] = &*format_args.args {
-            let value = arg.param.value;
-            if_chain! {
-                if format_args.format_string.parts == [kw::Empty];
-                if arg.format.is_default();
-                if match cx.typeck_results().expr_ty(value).peel_refs().kind() {
-                    ty::Adt(adt, _) => Some(adt.did()) == cx.tcx.lang_items().string(),
-                    ty::Str => true,
-                    _ => false,
-                };
-                then {
-                    let is_new_string = match value.kind {
-                        ExprKind::Binary(..) => true,
-                        ExprKind::MethodCall(path, ..) => path.ident.name == sym::to_string,
-                        _ => false,
-                    };
-                    let sugg = if is_new_string {
-                        snippet_with_context(cx, value.span, call_site.ctxt(), "..", &mut applicability).0.into_owned()
-                    } else {
-                        let sugg = Sugg::hir_with_context(cx, value, call_site.ctxt(), "<arg>", &mut applicability);
-                        format!("{}.to_string()", sugg.maybe_par())
-                    };
-                    span_useless_format(cx, call_site, sugg, applicability);
-                }
+                ([arg], [piece]) => {
+                    if let Ok(value) = find_format_arg_expr(expr, arg)
+                        && let FormatArgsPiece::Placeholder(placeholder) = piece
+                        && placeholder.format_trait == FormatTrait::Display
+                        && placeholder.format_options == FormatOptions::default()
+                        && match cx.typeck_results().expr_ty(value).peel_refs().kind() {
+                            ty::Adt(adt, _) => Some(adt.did()) == cx.tcx.lang_items().string(),
+                            ty::Str => true,
+                            _ => false,
+                        }
+                    {
+                        let is_new_string = match value.kind {
+                            ExprKind::Binary(..) => true,
+                            ExprKind::MethodCall(path, ..) => path.ident.name == sym::to_string,
+                            _ => false,
+                        };
+                        let sugg = if is_new_string {
+                            snippet_with_context(cx, value.span, call_site.ctxt(), "..", &mut applicability).0.into_owned()
+                        } else {
+                            let sugg = Sugg::hir_with_context(cx, value, call_site.ctxt(), "<arg>", &mut applicability);
+                            format!("{}.to_string()", sugg.maybe_par())
+                        };
+                        span_useless_format(cx, call_site, sugg, applicability);
+
+                    }
+                },
+                _ => {},
             }
-        };
+        });
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs
index c511d85e9cf..08e45ed7d0e 100644
--- a/src/tools/clippy/clippy_lints/src/format_args.rs
+++ b/src/tools/clippy/clippy_lints/src/format_args.rs
@@ -1,27 +1,31 @@
+use arrayvec::ArrayVec;
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::is_diag_trait_item;
-use clippy_utils::macros::FormatParamKind::{Implicit, Named, NamedInline, Numbered, Starred};
 use clippy_utils::macros::{
-    is_assert_macro, is_format_macro, is_panic, root_macro_call, Count, FormatArg, FormatArgsExpn, FormatParam,
-    FormatParamUsage,
+    find_format_arg_expr, find_format_args, format_arg_removal_span, format_placeholder_format_span, is_assert_macro,
+    is_format_macro, is_panic, root_macro_call, root_macro_call_first_node, FormatParamUsage,
 };
 use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::{implements_trait, is_type_lang_item};
 use if_chain::if_chain;
 use itertools::Itertools;
+use rustc_ast::{
+    FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, FormatOptions,
+    FormatPlaceholder, FormatTrait,
+};
 use rustc_errors::{
     Applicability,
     SuggestionStyle::{CompletelyHidden, ShowCode},
 };
-use rustc_hir::{Expr, ExprKind, HirId, LangItem, QPath};
+use rustc_hir::{Expr, ExprKind, LangItem};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::ty::adjustment::{Adjust, Adjustment};
 use rustc_middle::ty::Ty;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::def_id::DefId;
 use rustc_span::edition::Edition::Edition2021;
-use rustc_span::{sym, ExpnData, ExpnKind, Span, Symbol};
+use rustc_span::{sym, Span, Symbol};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -184,72 +188,79 @@ impl FormatArgs {
 
 impl<'tcx> LateLintPass<'tcx> for FormatArgs {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-        if let Some(format_args) = FormatArgsExpn::parse(cx, expr)
-            && let expr_expn_data = expr.span.ctxt().outer_expn_data()
-            && let outermost_expn_data = outermost_expn_data(expr_expn_data)
-            && let Some(macro_def_id) = outermost_expn_data.macro_def_id
-            && is_format_macro(cx, macro_def_id)
-            && let ExpnKind::Macro(_, name) = outermost_expn_data.kind
-        {
-            for arg in &format_args.args {
-                check_unused_format_specifier(cx, arg);
-                if !arg.format.is_default() {
-                    continue;
-                }
-                if is_aliased(&format_args, arg.param.value.hir_id) {
-                    continue;
+        let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
+        if !is_format_macro(cx, macro_call.def_id) {
+            return;
+        }
+        let name = cx.tcx.item_name(macro_call.def_id);
+
+        find_format_args(cx, expr, macro_call.expn, |format_args| {
+            for piece in &format_args.template {
+                if let FormatArgsPiece::Placeholder(placeholder) = piece
+                    && let Ok(index) = placeholder.argument.index
+                    && let Some(arg) = format_args.arguments.all_args().get(index)
+                {
+                    let arg_expr = find_format_arg_expr(expr, arg);
+
+                    check_unused_format_specifier(cx, placeholder, arg_expr);
+
+                    if placeholder.format_trait != FormatTrait::Display
+                        || placeholder.format_options != FormatOptions::default()
+                        || is_aliased(format_args, index)
+                    {
+                        continue;
+                    }
+
+                    if let Ok(arg_hir_expr) = arg_expr {
+                        check_format_in_format_args(cx, macro_call.span, name, arg_hir_expr);
+                        check_to_string_in_format_args(cx, name, arg_hir_expr);
+                    }
                 }
-                check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value);
-                check_to_string_in_format_args(cx, name, arg.param.value);
             }
+
             if self.msrv.meets(msrvs::FORMAT_ARGS_CAPTURE) {
-                check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_id, self.ignore_mixed);
+                check_uninlined_args(cx, format_args, macro_call.span, macro_call.def_id, self.ignore_mixed);
             }
-        }
+        });
     }
 
     extract_msrv_attr!(LateContext);
 }
 
-fn check_unused_format_specifier(cx: &LateContext<'_>, arg: &FormatArg<'_>) {
-    let param_ty = cx.typeck_results().expr_ty(arg.param.value).peel_refs();
+fn check_unused_format_specifier(
+    cx: &LateContext<'_>,
+    placeholder: &FormatPlaceholder,
+    arg_expr: Result<&Expr<'_>, &rustc_ast::Expr>,
+) {
+    let ty_or_ast_expr = arg_expr.map(|expr| cx.typeck_results().expr_ty(expr).peel_refs());
 
-    if let Count::Implied(Some(mut span)) = arg.format.precision
-        && !span.is_empty()
-    {
-        span_lint_and_then(
-            cx,
-            UNUSED_FORMAT_SPECS,
-            span,
-            "empty precision specifier has no effect",
-            |diag| {
-                if param_ty.is_floating_point() {
-                    diag.note("a precision specifier is not required to format floats");
-                }
+    let is_format_args = match ty_or_ast_expr {
+        Ok(ty) => is_type_lang_item(cx, ty, LangItem::FormatArguments),
+        Err(expr) => matches!(expr.peel_parens_and_refs().kind, rustc_ast::ExprKind::FormatArgs(_)),
+    };
 
-                if arg.format.is_default() {
-                    // If there's no other specifiers remove the `:` too
-                    span = arg.format_span();
-                }
+    let options = &placeholder.format_options;
 
-                diag.span_suggestion_verbose(span, "remove the `.`", "", Applicability::MachineApplicable);
-            },
-        );
-    }
+    let arg_span = match arg_expr {
+        Ok(expr) => expr.span,
+        Err(expr) => expr.span,
+    };
 
-    if is_type_lang_item(cx, param_ty, LangItem::FormatArguments) && !arg.format.is_default_for_trait() {
+    if let Some(placeholder_span) = placeholder.span
+        && is_format_args
+        && *options != FormatOptions::default()
+    {
         span_lint_and_then(
             cx,
             UNUSED_FORMAT_SPECS,
-            arg.span,
+            placeholder_span,
             "format specifiers have no effect on `format_args!()`",
             |diag| {
-                let mut suggest_format = |spec, span| {
+                let mut suggest_format = |spec| {
                     let message = format!("for the {spec} to apply consider using `format!()`");
 
-                    if let Some(mac_call) = root_macro_call(arg.param.value.span)
+                    if let Some(mac_call) = root_macro_call(arg_span)
                         && cx.tcx.is_diagnostic_item(sym::format_args_macro, mac_call.def_id)
-                        && arg.span.eq_ctxt(mac_call.span)
                     {
                         diag.span_suggestion(
                             cx.sess().source_map().span_until_char(mac_call.span, '!'),
@@ -257,25 +268,27 @@ fn check_unused_format_specifier(cx: &LateContext<'_>, arg: &FormatArg<'_>) {
                             "format",
                             Applicability::MaybeIncorrect,
                         );
-                    } else if let Some(span) = span {
-                        diag.span_help(span, message);
+                    } else {
+                        diag.help(message);
                     }
                 };
 
-                if !arg.format.width.is_implied() {
-                    suggest_format("width", arg.format.width.span());
+                if options.width.is_some() {
+                    suggest_format("width");
                 }
 
-                if !arg.format.precision.is_implied() {
-                    suggest_format("precision", arg.format.precision.span());
+                if options.precision.is_some() {
+                    suggest_format("precision");
                 }
 
-                diag.span_suggestion_verbose(
-                    arg.format_span(),
-                    "if the current behavior is intentional, remove the format specifiers",
-                    "",
-                    Applicability::MaybeIncorrect,
-                );
+                if let Some(format_span) = format_placeholder_format_span(placeholder) {
+                    diag.span_suggestion_verbose(
+                        format_span,
+                        "if the current behavior is intentional, remove the format specifiers",
+                        "",
+                        Applicability::MaybeIncorrect,
+                    );
+                }
             },
         );
     }
@@ -283,12 +296,12 @@ fn check_unused_format_specifier(cx: &LateContext<'_>, arg: &FormatArg<'_>) {
 
 fn check_uninlined_args(
     cx: &LateContext<'_>,
-    args: &FormatArgsExpn<'_>,
+    args: &rustc_ast::FormatArgs,
     call_site: Span,
     def_id: DefId,
     ignore_mixed: bool,
 ) {
-    if args.format_string.span.from_expansion() {
+    if args.span.from_expansion() {
         return;
     }
     if call_site.edition() < Edition2021 && (is_panic(cx, def_id) || is_assert_macro(cx, def_id)) {
@@ -303,7 +316,13 @@ fn check_uninlined_args(
     // we cannot remove any other arguments in the format string,
     // because the index numbers might be wrong after inlining.
     // Example of an un-inlinable format:  print!("{}{1}", foo, 2)
-    if !args.params().all(|p| check_one_arg(args, &p, &mut fixes, ignore_mixed)) || fixes.is_empty() {
+    for (pos, usage) in format_arg_positions(args) {
+        if !check_one_arg(args, pos, usage, &mut fixes, ignore_mixed) {
+            return;
+        }
+    }
+
+    if fixes.is_empty() {
         return;
     }
 
@@ -332,47 +351,40 @@ fn check_uninlined_args(
 }
 
 fn check_one_arg(
-    args: &FormatArgsExpn<'_>,
-    param: &FormatParam<'_>,
+    args: &rustc_ast::FormatArgs,
+    pos: &FormatArgPosition,
+    usage: FormatParamUsage,
     fixes: &mut Vec<(Span, String)>,
     ignore_mixed: bool,
 ) -> bool {
-    if matches!(param.kind, Implicit | Starred | Named(_) | Numbered)
-        && let ExprKind::Path(QPath::Resolved(None, path)) = param.value.kind
-        && let [segment] = path.segments
+    let index = pos.index.unwrap();
+    let arg = &args.arguments.all_args()[index];
+
+    if !matches!(arg.kind, FormatArgumentKind::Captured(_))
+        && let rustc_ast::ExprKind::Path(None, path) = &arg.expr.kind
+        && let [segment] = path.segments.as_slice()
         && segment.args.is_none()
-        && let Some(arg_span) = args.value_with_prev_comma_span(param.value.hir_id)
+        && let Some(arg_span) = format_arg_removal_span(args, index)
+        && let Some(pos_span) = pos.span
     {
-        let replacement = match param.usage {
+        let replacement = match usage {
             FormatParamUsage::Argument => segment.ident.name.to_string(),
             FormatParamUsage::Width => format!("{}$", segment.ident.name),
             FormatParamUsage::Precision => format!(".{}$", segment.ident.name),
         };
-        fixes.push((param.span, replacement));
+        fixes.push((pos_span, replacement));
         fixes.push((arg_span, String::new()));
         true  // successful inlining, continue checking
     } else {
         // Do not continue inlining (return false) in case
         // * if we can't inline a numbered argument, e.g. `print!("{0} ...", foo.bar, ...)`
         // * if allow_mixed_uninlined_format_args is false and this arg hasn't been inlined already
-        param.kind != Numbered && (!ignore_mixed || matches!(param.kind, NamedInline(_)))
-    }
-}
-
-fn outermost_expn_data(expn_data: ExpnData) -> ExpnData {
-    if expn_data.call_site.from_expansion() {
-        outermost_expn_data(expn_data.call_site.ctxt().outer_expn_data())
-    } else {
-        expn_data
+        pos.kind != FormatArgPositionKind::Number
+            && (!ignore_mixed || matches!(arg.kind, FormatArgumentKind::Captured(_)))
     }
 }
 
-fn check_format_in_format_args(
-    cx: &LateContext<'_>,
-    call_site: Span,
-    name: Symbol,
-    arg: &Expr<'_>,
-) {
+fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symbol, arg: &Expr<'_>) {
     let expn_data = arg.span.ctxt().outer_expn_data();
     if expn_data.call_site.from_expansion() {
         return;
@@ -443,9 +455,33 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex
     }
 }
 
-/// Returns true if `hir_id` is referred to by multiple format params
-fn is_aliased(args: &FormatArgsExpn<'_>, hir_id: HirId) -> bool {
-    args.params().filter(|param| param.value.hir_id == hir_id).at_most_one().is_err()
+fn format_arg_positions(
+    format_args: &rustc_ast::FormatArgs,
+) -> impl Iterator<Item = (&FormatArgPosition, FormatParamUsage)> {
+    format_args.template.iter().flat_map(|piece| match piece {
+        FormatArgsPiece::Placeholder(placeholder) => {
+            let mut positions = ArrayVec::<_, 3>::new();
+
+            positions.push((&placeholder.argument, FormatParamUsage::Argument));
+            if let Some(FormatCount::Argument(position)) = &placeholder.format_options.width {
+                positions.push((position, FormatParamUsage::Width));
+            }
+            if let Some(FormatCount::Argument(position)) = &placeholder.format_options.precision {
+                positions.push((position, FormatParamUsage::Precision));
+            }
+
+            positions
+        },
+        FormatArgsPiece::Literal(_) => ArrayVec::new(),
+    })
+}
+
+/// Returns true if the format argument at `index` is referred to by multiple format params
+fn is_aliased(format_args: &rustc_ast::FormatArgs, index: usize) -> bool {
+    format_arg_positions(format_args)
+        .filter(|(position, _)| position.index == Ok(index))
+        .at_most_one()
+        .is_err()
 }
 
 fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>)
@@ -455,7 +491,11 @@ where
     let mut n_total = 0;
     let mut n_needed = 0;
     loop {
-        if let Some(Adjustment { kind: Adjust::Deref(overloaded_deref), target }) = iter.next() {
+        if let Some(Adjustment {
+            kind: Adjust::Deref(overloaded_deref),
+            target,
+        }) = iter.next()
+        {
             n_total += 1;
             if overloaded_deref.is_some() {
                 n_needed = n_total;
diff --git a/src/tools/clippy/clippy_lints/src/format_impl.rs b/src/tools/clippy/clippy_lints/src/format_impl.rs
index ed1342a5465..e3ddbfb5981 100644
--- a/src/tools/clippy/clippy_lints/src/format_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/format_impl.rs
@@ -1,11 +1,13 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
-use clippy_utils::macros::{is_format_macro, root_macro_call_first_node, FormatArg, FormatArgsExpn};
+use clippy_utils::macros::{find_format_arg_expr, find_format_args, is_format_macro, root_macro_call_first_node};
 use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators};
 use if_chain::if_chain;
+use rustc_ast::{FormatArgsPiece, FormatTrait};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::Span;
 use rustc_span::{sym, symbol::kw, Symbol};
 
 declare_clippy_lint! {
@@ -89,7 +91,7 @@ declare_clippy_lint! {
 }
 
 #[derive(Clone, Copy)]
-struct FormatTrait {
+struct FormatTraitNames {
     /// e.g. `sym::Display`
     name: Symbol,
     /// `f` in `fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {}`
@@ -99,7 +101,7 @@ struct FormatTrait {
 #[derive(Default)]
 pub struct FormatImpl {
     // Whether we are inside Display or Debug trait impl - None for neither
-    format_trait_impl: Option<FormatTrait>,
+    format_trait_impl: Option<FormatTraitNames>,
 }
 
 impl FormatImpl {
@@ -161,43 +163,57 @@ fn check_to_string_in_display(cx: &LateContext<'_>, expr: &Expr<'_>) {
     }
 }
 
-fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, impl_trait: FormatTrait) {
+fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, impl_trait: FormatTraitNames) {
     // Check each arg in format calls - do we ever use Display on self (directly or via deref)?
-    if_chain! {
-        if let Some(outer_macro) = root_macro_call_first_node(cx, expr);
-        if let macro_def_id = outer_macro.def_id;
-        if let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, outer_macro.expn);
-        if is_format_macro(cx, macro_def_id);
-        then {
-            for arg in format_args.args {
-                if arg.format.r#trait != impl_trait.name {
-                    continue;
+    if let Some(outer_macro) = root_macro_call_first_node(cx, expr)
+        && let macro_def_id = outer_macro.def_id
+        && is_format_macro(cx, macro_def_id)
+    {
+        find_format_args(cx, expr, outer_macro.expn, |format_args| {
+            for piece in &format_args.template {
+                if let FormatArgsPiece::Placeholder(placeholder) = piece
+                    && let trait_name = match placeholder.format_trait {
+                        FormatTrait::Display => sym::Display,
+                        FormatTrait::Debug => sym::Debug,
+                        FormatTrait::LowerExp => sym!(LowerExp),
+                        FormatTrait::UpperExp => sym!(UpperExp),
+                        FormatTrait::Octal => sym!(Octal),
+                        FormatTrait::Pointer => sym::Pointer,
+                        FormatTrait::Binary => sym!(Binary),
+                        FormatTrait::LowerHex => sym!(LowerHex),
+                        FormatTrait::UpperHex => sym!(UpperHex),
+                    }
+                    && trait_name == impl_trait.name
+                    && let Ok(index) = placeholder.argument.index
+                    && let Some(arg) = format_args.arguments.all_args().get(index)
+                    && let Ok(arg_expr) = find_format_arg_expr(expr, arg)
+                {
+                    check_format_arg_self(cx, expr.span, arg_expr, impl_trait);
                 }
-                check_format_arg_self(cx, expr, &arg, impl_trait);
             }
-        }
+        });
     }
 }
 
-fn check_format_arg_self(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &FormatArg<'_>, impl_trait: FormatTrait) {
+fn check_format_arg_self(cx: &LateContext<'_>, span: Span, arg: &Expr<'_>, impl_trait: FormatTraitNames) {
     // Handle multiple dereferencing of references e.g. &&self
     // Handle dereference of &self -> self that is equivalent (i.e. via *self in fmt() impl)
     // Since the argument to fmt is itself a reference: &self
-    let reference = peel_ref_operators(cx, arg.param.value);
+    let reference = peel_ref_operators(cx, arg);
     let map = cx.tcx.hir();
     // Is the reference self?
     if path_to_local(reference).map(|x| map.name(x)) == Some(kw::SelfLower) {
-        let FormatTrait { name, .. } = impl_trait;
+        let FormatTraitNames { name, .. } = impl_trait;
         span_lint(
             cx,
             RECURSIVE_FORMAT_IMPL,
-            expr.span,
+            span,
             &format!("using `self` as `{name}` in `impl {name}` will cause infinite recursion"),
         );
     }
 }
 
-fn check_print_in_format_impl(cx: &LateContext<'_>, expr: &Expr<'_>, impl_trait: FormatTrait) {
+fn check_print_in_format_impl(cx: &LateContext<'_>, expr: &Expr<'_>, impl_trait: FormatTraitNames) {
     if_chain! {
         if let Some(macro_call) = root_macro_call_first_node(cx, expr);
         if let Some(name) = cx.tcx.get_diagnostic_name(macro_call.def_id);
@@ -227,7 +243,7 @@ fn check_print_in_format_impl(cx: &LateContext<'_>, expr: &Expr<'_>, impl_trait:
     }
 }
 
-fn is_format_trait_impl(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) -> Option<FormatTrait> {
+fn is_format_trait_impl(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) -> Option<FormatTraitNames> {
     if_chain! {
         if impl_item.ident.name == sym::fmt;
         if let ImplItemKind::Fn(_, body_id) = impl_item.kind;
@@ -241,7 +257,7 @@ fn is_format_trait_impl(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) -> Optio
                 .and_then(|param| param.pat.simple_ident())
                 .map(|ident| ident.name);
 
-            Some(FormatTrait {
+            Some(FormatTraitNames {
                 name,
                 formatter_name,
             })
diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs
index 1e9e826631c..d0ad2628264 100644
--- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs
@@ -1,7 +1,9 @@
+use hir::FnSig;
 use rustc_ast::ast::Attribute;
 use rustc_errors::Applicability;
 use rustc_hir::def_id::DefIdSet;
 use rustc_hir::{self as hir, def::Res, QPath};
+use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::{
     lint::in_external_macro,
@@ -27,7 +29,7 @@ pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>
         let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id);
         let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
         if let Some(attr) = attr {
-            check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr);
+            check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, sig);
         } else if is_public && !is_proc_macro(attrs) && !attrs.iter().any(|a| a.has_name(sym::no_mangle)) {
             check_must_use_candidate(
                 cx,
@@ -49,7 +51,7 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp
         let attrs = cx.tcx.hir().attrs(item.hir_id());
         let attr = cx.tcx.get_attr(item.owner_id, sym::must_use);
         if let Some(attr) = attr {
-            check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr);
+            check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, sig);
         } else if is_public && !is_proc_macro(attrs) && trait_ref_of_method(cx, item.owner_id.def_id).is_none() {
             check_must_use_candidate(
                 cx,
@@ -72,7 +74,7 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr
         let attrs = cx.tcx.hir().attrs(item.hir_id());
         let attr = cx.tcx.get_attr(item.owner_id, sym::must_use);
         if let Some(attr) = attr {
-            check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr);
+            check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, sig);
         } else if let hir::TraitFn::Provided(eid) = *eid {
             let body = cx.tcx.hir().body(eid);
             if attr.is_none() && is_public && !is_proc_macro(attrs) {
@@ -97,6 +99,7 @@ fn check_needless_must_use(
     item_span: Span,
     fn_header_span: Span,
     attr: &Attribute,
+    sig: &FnSig<'_>,
 ) {
     if in_external_macro(cx.sess(), item_span) {
         return;
@@ -112,6 +115,15 @@ fn check_needless_must_use(
             },
         );
     } else if attr.value_str().is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) {
+        // Ignore async functions unless Future::Output type is a must_use type
+        if sig.header.is_async() {
+            let infcx = cx.tcx.infer_ctxt().build();
+            if let Some(future_ty) = infcx.get_impl_future_output_ty(return_ty(cx, item_id))
+			&& !is_must_use_ty(cx, future_ty) {
+				return;
+			}
+        }
+
         span_lint_and_help(
             cx,
             DOUBLE_MUST_USE,
diff --git a/src/tools/clippy/clippy_lints/src/items_after_statements.rs b/src/tools/clippy/clippy_lints/src/items_after_statements.rs
index 46d439b4497..a7ec57e2850 100644
--- a/src/tools/clippy/clippy_lints/src/items_after_statements.rs
+++ b/src/tools/clippy/clippy_lints/src/items_after_statements.rs
@@ -1,8 +1,8 @@
 //! lint when items are used after statements
 
-use clippy_utils::diagnostics::span_lint;
-use rustc_ast::ast::{Block, ItemKind, StmtKind};
-use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
+use clippy_utils::diagnostics::span_lint_hir;
+use rustc_hir::{Block, ItemKind, StmtKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
@@ -52,33 +52,34 @@ declare_clippy_lint! {
 
 declare_lint_pass!(ItemsAfterStatements => [ITEMS_AFTER_STATEMENTS]);
 
-impl EarlyLintPass for ItemsAfterStatements {
-    fn check_block(&mut self, cx: &EarlyContext<'_>, item: &Block) {
-        if in_external_macro(cx.sess(), item.span) {
+impl LateLintPass<'_> for ItemsAfterStatements {
+    fn check_block(&mut self, cx: &LateContext<'_>, block: &Block<'_>) {
+        if in_external_macro(cx.sess(), block.span) {
             return;
         }
 
-        // skip initial items and trailing semicolons
-        let stmts = item
+        // skip initial items
+        let stmts = block
             .stmts
             .iter()
-            .map(|stmt| &stmt.kind)
-            .skip_while(|s| matches!(**s, StmtKind::Item(..) | StmtKind::Empty));
+            .skip_while(|stmt| matches!(stmt.kind, StmtKind::Item(..)));
 
         // lint on all further items
         for stmt in stmts {
-            if let StmtKind::Item(ref it) = *stmt {
-                if in_external_macro(cx.sess(), it.span) {
+            if let StmtKind::Item(item_id) = stmt.kind {
+                let item = cx.tcx.hir().item(item_id);
+                if in_external_macro(cx.sess(), item.span) || !item.span.eq_ctxt(block.span) {
                     return;
                 }
-                if let ItemKind::MacroDef(..) = it.kind {
+                if let ItemKind::Macro(..) = item.kind {
                     // do not lint `macro_rules`, but continue processing further statements
                     continue;
                 }
-                span_lint(
+                span_lint_hir(
                     cx,
                     ITEMS_AFTER_STATEMENTS,
-                    it.span,
+                    item.hir_id(),
+                    item.span,
                     "adding items after statements is confusing, since items exist from the \
                      start of the scope",
                 );
diff --git a/src/tools/clippy/clippy_lints/src/large_futures.rs b/src/tools/clippy/clippy_lints/src/large_futures.rs
new file mode 100644
index 00000000000..1b054481371
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/large_futures.rs
@@ -0,0 +1,87 @@
+use clippy_utils::source::snippet;
+use clippy_utils::{diagnostics::span_lint_and_sugg, ty::implements_trait};
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_target::abi::Size;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// It checks for the size of a `Future` created by `async fn` or `async {}`.
+    ///
+    /// ### Why is this bad?
+    /// Due to the current [unideal implemention](https://github.com/rust-lang/rust/issues/69826) of `Generator`,
+    /// large size of a `Future` may cause stack overflows.
+    ///
+    /// ### Example
+    /// ```rust
+    /// async fn wait(f: impl std::future::Future<Output = ()>) {}
+    ///
+    /// async fn big_fut(arg: [u8; 1024]) {}
+    ///
+    /// pub async fn test() {
+    ///     let fut = big_fut([0u8; 1024]);
+    ///     wait(fut).await;
+    /// }
+    /// ```
+    ///
+    /// `Box::pin` the big future instead.
+    ///
+    /// ```rust
+    /// async fn wait(f: impl std::future::Future<Output = ()>) {}
+    ///
+    /// async fn big_fut(arg: [u8; 1024]) {}
+    ///
+    /// pub async fn test() {
+    ///     let fut = Box::pin(big_fut([0u8; 1024]));
+    ///     wait(fut).await;
+    /// }
+    /// ```
+    #[clippy::version = "1.68.0"]
+    pub LARGE_FUTURES,
+    pedantic,
+    "large future may lead to unexpected stack overflows"
+}
+
+#[derive(Copy, Clone)]
+pub struct LargeFuture {
+    future_size_threshold: u64,
+}
+
+impl LargeFuture {
+    pub fn new(future_size_threshold: u64) -> Self {
+        Self { future_size_threshold }
+    }
+}
+
+impl_lint_pass!(LargeFuture => [LARGE_FUTURES]);
+
+impl<'tcx> LateLintPass<'tcx> for LargeFuture {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+        if matches!(expr.span.ctxt().outer_expn_data().kind, rustc_span::ExpnKind::Macro(..)) {
+            return;
+        }
+        if let ExprKind::Match(expr, _, MatchSource::AwaitDesugar) = expr.kind {
+            if let ExprKind::Call(func, [expr, ..]) = expr.kind
+                && let ExprKind::Path(QPath::LangItem(LangItem::IntoFutureIntoFuture, ..)) = func.kind
+                && let ty = cx.typeck_results().expr_ty(expr)
+                && let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait()
+                && implements_trait(cx, ty, future_trait_def_id, &[])
+                && let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty))
+                && let size = layout.layout.size()
+                && size >= Size::from_bytes(self.future_size_threshold)
+            {
+                span_lint_and_sugg(
+                    cx,
+                    LARGE_FUTURES,
+                    expr.span,
+                    &format!("large future with a size of {} bytes", size.bytes()),
+                    "consider `Box::pin` on it",
+                    format!("Box::pin({})", snippet(cx, expr.span, "..")),
+                    Applicability::Unspecified,
+                );
+            }
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index 3da7f95c1b9..b0ec14855e7 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -1,7 +1,6 @@
 #![feature(array_windows)]
 #![feature(binary_heap_into_iter_sorted)]
 #![feature(box_patterns)]
-#![feature(drain_filter)]
 #![feature(if_let_guard)]
 #![feature(iter_intersperse)]
 #![feature(let_chains)]
@@ -162,6 +161,7 @@ mod items_after_statements;
 mod iter_not_returning_iterator;
 mod large_const_arrays;
 mod large_enum_variant;
+mod large_futures;
 mod large_include_file;
 mod large_stack_arrays;
 mod len_zero;
@@ -169,6 +169,7 @@ mod let_if_seq;
 mod let_underscore;
 mod let_with_type_underscore;
 mod lifetimes;
+mod lines_filter_map_ok;
 mod literal_representation;
 mod loops;
 mod macro_use;
@@ -183,6 +184,7 @@ mod manual_main_separator_str;
 mod manual_non_exhaustive;
 mod manual_rem_euclid;
 mod manual_retain;
+mod manual_slice_size_calculation;
 mod manual_string_new;
 mod manual_strip;
 mod map_unit_fn;
@@ -281,6 +283,7 @@ mod slow_vector_initialization;
 mod std_instead_of_core;
 mod strings;
 mod strlen_on_c_strings;
+mod suspicious_doc_comments;
 mod suspicious_operation_groupings;
 mod suspicious_trait_impl;
 mod suspicious_xor_used_as_pow;
@@ -288,6 +291,7 @@ mod swap;
 mod swap_ptr_to_ref;
 mod tabs_in_doc_comments;
 mod temporary_assignment;
+mod tests_outside_test_module;
 mod to_digit_is_some;
 mod trailing_empty_array;
 mod trait_bounds;
@@ -299,6 +303,7 @@ mod uninit_vec;
 mod unit_return_expecting_ord;
 mod unit_types;
 mod unnamed_address;
+mod unnecessary_box_returns;
 mod unnecessary_owned_empty_strings;
 mod unnecessary_self_imports;
 mod unnecessary_struct_initialization;
@@ -344,13 +349,17 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Se
 }
 
 #[doc(hidden)]
-pub fn read_conf(sess: &Session, path: &io::Result<Option<PathBuf>>) -> Conf {
+pub fn read_conf(sess: &Session, path: &io::Result<(Option<PathBuf>, Vec<String>)>) -> Conf {
+    if let Ok((_, warnings)) = path {
+        for warning in warnings {
+            sess.warn(warning);
+        }
+    }
     let file_name = match path {
-        Ok(Some(path)) => path,
-        Ok(None) => return Conf::default(),
+        Ok((Some(path), _)) => path,
+        Ok((None, _)) => return Conf::default(),
         Err(error) => {
-            sess.struct_err(format!("error finding Clippy's configuration file: {error}"))
-                .emit();
+            sess.err(format!("error finding Clippy's configuration file: {error}"));
             return Conf::default();
         },
     };
@@ -746,7 +755,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_early_pass(|| Box::new(unused_unit::UnusedUnit));
     store.register_late_pass(|_| Box::new(returns::Return));
     store.register_early_pass(|| Box::new(collapsible_if::CollapsibleIf));
-    store.register_early_pass(|| Box::new(items_after_statements::ItemsAfterStatements));
+    store.register_late_pass(|_| Box::new(items_after_statements::ItemsAfterStatements));
     store.register_early_pass(|| Box::new(precedence::Precedence));
     store.register_late_pass(|_| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals));
     store.register_early_pass(|| Box::new(needless_continue::NeedlessContinue));
@@ -808,6 +817,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv())));
     store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse));
     store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend));
+    let future_size_threshold = conf.future_size_threshold;
+    store.register_late_pass(move |_| Box::new(large_futures::LargeFuture::new(future_size_threshold)));
     store.register_late_pass(|_| Box::new(if_let_mutex::IfLetMutex));
     store.register_late_pass(|_| Box::new(if_not_else::IfNotElse));
     store.register_late_pass(|_| Box::new(equatable_if_let::PatternEquality));
@@ -934,11 +945,20 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi));
     store.register_late_pass(|_| Box::new(collection_is_never_read::CollectionIsNeverRead));
     store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage));
-    store.register_early_pass(|| Box::new(redundant_async_block::RedundantAsyncBlock));
+    store.register_late_pass(|_| Box::new(redundant_async_block::RedundantAsyncBlock));
     store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped));
     store.register_late_pass(|_| Box::new(allow_attributes::AllowAttribute));
     store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(msrv())));
     store.register_late_pass(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct));
+    store.register_late_pass(move |_| {
+        Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(
+            avoid_breaking_exported_api,
+        ))
+    });
+    store.register_late_pass(|_| Box::new(lines_filter_map_ok::LinesFilterMapOk));
+    store.register_late_pass(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule));
+    store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation));
+    store.register_early_pass(|| Box::new(suspicious_doc_comments::SuspiciousDocComments));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs b/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs
new file mode 100644
index 00000000000..b0f9276475d
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs
@@ -0,0 +1,100 @@
+use clippy_utils::{
+    diagnostics::span_lint_and_then, is_diag_item_method, is_trait_method, match_def_path, path_to_local_id, paths,
+    ty::match_type,
+};
+use rustc_errors::Applicability;
+use rustc_hir::{Body, Closure, Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Detect uses of `lines.filter_map(Result::ok)` or `lines.flat_map(Result::ok)`
+    /// when `lines` has type `std::io::Lines`.
+    ///
+    /// ### Why is this bad?
+    /// `Lines` instances might produce a never-ending stream of `Err`, in which case
+    /// `filter_map(Result::ok)` will enter an infinite loop while waiting for an
+    /// `Ok` variant. Calling `next()` once is sufficient to enter the infinite loop,
+    /// even in the absence of explicit loops in the user code.
+    ///
+    /// This situation can arise when working with user-provided paths. On some platforms,
+    /// `std::fs::File::open(path)` might return `Ok(fs)` even when `path` is a directory,
+    /// but any later attempt to read from `fs` will return an error.
+    ///
+    /// ### Known problems
+    /// This lint suggests replacing `filter_map()` or `flat_map()` applied to a `Lines`
+    /// instance in all cases. There two cases where the suggestion might not be
+    /// appropriate or necessary:
+    ///
+    /// - If the `Lines` instance can never produce any error, or if an error is produced
+    ///   only once just before terminating the iterator, using `map_while()` is not
+    ///   necessary but will not do any harm.
+    /// - If the `Lines` instance can produce intermittent errors then recover and produce
+    ///   successful results, using `map_while()` would stop at the first error.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # use std::{fs::File, io::{self, BufRead, BufReader}};
+    /// # let _ = || -> io::Result<()> {
+    /// let mut lines = BufReader::new(File::open("some-path")?).lines().filter_map(Result::ok);
+    /// // If "some-path" points to a directory, the next statement never terminates:
+    /// let first_line: Option<String> = lines.next();
+    /// # Ok(()) };
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # use std::{fs::File, io::{self, BufRead, BufReader}};
+    /// # let _ = || -> io::Result<()> {
+    /// let mut lines = BufReader::new(File::open("some-path")?).lines().map_while(Result::ok);
+    /// let first_line: Option<String> = lines.next();
+    /// # Ok(()) };
+    /// ```
+    #[clippy::version = "1.70.0"]
+    pub LINES_FILTER_MAP_OK,
+    suspicious,
+    "filtering `std::io::Lines` with `filter_map()` or `flat_map()` might cause an infinite loop"
+}
+declare_lint_pass!(LinesFilterMapOk => [LINES_FILTER_MAP_OK]);
+
+impl LateLintPass<'_> for LinesFilterMapOk {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+        if let ExprKind::MethodCall(fm_method, fm_receiver, [fm_arg], fm_span) = expr.kind &&
+            is_trait_method(cx, expr, sym::Iterator) &&
+            (fm_method.ident.as_str() == "filter_map" || fm_method.ident.as_str() == "flat_map") &&
+            match_type(cx, cx.typeck_results().expr_ty_adjusted(fm_receiver), &paths::STD_IO_LINES)
+        {
+            let lint = match &fm_arg.kind {
+                // Detect `Result::ok`
+                ExprKind::Path(qpath) =>
+                    cx.qpath_res(qpath, fm_arg.hir_id).opt_def_id().map(|did|
+                        match_def_path(cx, did, &paths::CORE_RESULT_OK_METHOD)).unwrap_or_default(),
+                // Detect `|x| x.ok()`
+                ExprKind::Closure(Closure { body, .. }) =>
+                    if let Body { params: [param], value, .. } = cx.tcx.hir().body(*body) &&
+                        let ExprKind::MethodCall(method, receiver, [], _) = value.kind &&
+                        path_to_local_id(receiver, param.pat.hir_id) &&
+                        let Some(method_did) = cx.typeck_results().type_dependent_def_id(value.hir_id)
+                    {
+                        is_diag_item_method(cx, method_did, sym::Result) && method.ident.as_str() == "ok"
+                    } else {
+                        false
+                    }
+                _ => false,
+            };
+            if lint {
+                span_lint_and_then(cx,
+                    LINES_FILTER_MAP_OK,
+                    fm_span,
+                    &format!("`{}()` will run forever if the iterator repeatedly produces an `Err`", fm_method.ident),
+                    |diag| {
+                        diag.span_note(
+                            fm_receiver.span,
+                            "this expression returning a `std::io::Lines` may produce an infinite number of `Err` in case of a read error");
+                        diag.span_suggestion(fm_span, "replace with", "map_while(Result::ok)", Applicability::MaybeIncorrect);
+                    });
+                }
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs b/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs
new file mode 100644
index 00000000000..92ee79453a3
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs
@@ -0,0 +1,93 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::{expr_or_init, in_constant};
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::sym;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// When `a` is `&[T]`, detect `a.len() * size_of::<T>()` and suggest `size_of_val(a)`
+    /// instead.
+    ///
+    /// ### Why is this better?
+    /// * Shorter to write
+    /// * Removes the need for the human and the compiler to worry about overflow in the
+    ///   multiplication
+    /// * Potentially faster at runtime as rust emits special no-wrapping flags when it
+    ///   calculates the byte length
+    /// * Less turbofishing
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let data : &[i32] = &[1, 2, 3];
+    /// let newlen = data.len() * std::mem::size_of::<i32>();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # let data : &[i32] = &[1, 2, 3];
+    /// let newlen = std::mem::size_of_val(data);
+    /// ```
+    #[clippy::version = "1.70.0"]
+    pub MANUAL_SLICE_SIZE_CALCULATION,
+    complexity,
+    "manual slice size calculation"
+}
+declare_lint_pass!(ManualSliceSizeCalculation => [MANUAL_SLICE_SIZE_CALCULATION]);
+
+impl<'tcx> LateLintPass<'tcx> for ManualSliceSizeCalculation {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+        // Does not apply inside const because size_of_value is not cost in stable.
+        if !in_constant(cx, expr.hir_id)
+            && let ExprKind::Binary(ref op, left, right) = expr.kind
+            && BinOpKind::Mul == op.node
+            && let Some(_receiver) = simplify(cx, left, right)
+        {
+            span_lint_and_help(
+                cx,
+                MANUAL_SLICE_SIZE_CALCULATION,
+                expr.span,
+                "manual slice size calculation",
+                None,
+                "consider using std::mem::size_of_value instead");
+        }
+    }
+}
+
+fn simplify<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr1: &'tcx Expr<'tcx>,
+    expr2: &'tcx Expr<'tcx>,
+) -> Option<&'tcx Expr<'tcx>> {
+    let expr1 = expr_or_init(cx, expr1);
+    let expr2 = expr_or_init(cx, expr2);
+
+    simplify_half(cx, expr1, expr2).or_else(|| simplify_half(cx, expr2, expr1))
+}
+
+fn simplify_half<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr1: &'tcx Expr<'tcx>,
+    expr2: &'tcx Expr<'tcx>,
+) -> Option<&'tcx Expr<'tcx>> {
+    if
+        // expr1 is `[T1].len()`?
+        let ExprKind::MethodCall(method_path, receiver, _, _) = expr1.kind
+        && method_path.ident.name == sym::len
+        && let receiver_ty = cx.typeck_results().expr_ty(receiver)
+        && let ty::Slice(ty1) = receiver_ty.peel_refs().kind()
+        // expr2 is `size_of::<T2>()`?
+        && let ExprKind::Call(func, _) = expr2.kind
+        && let ExprKind::Path(ref func_qpath) = func.kind
+        && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id()
+        && cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id)
+        && let Some(ty2) = cx.typeck_results().node_substs(func.hir_id).types().next()
+        // T1 == T2?
+        && *ty1 == ty2
+    {
+        Some(receiver)
+    } else {
+        None
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/mem_replace.rs b/src/tools/clippy/clippy_lints/src/mem_replace.rs
index 35024ec1224..8a921d4af16 100644
--- a/src/tools/clippy/clippy_lints/src/mem_replace.rs
+++ b/src/tools/clippy/clippy_lints/src/mem_replace.rs
@@ -1,12 +1,13 @@
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::{snippet, snippet_with_applicability};
+use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_non_aggregate_primitive_type;
-use clippy_utils::{is_default_equivalent, is_res_lang_ctor, path_res};
+use clippy_utils::{is_default_equivalent, is_res_lang_ctor, path_res, peel_ref_operators};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::OptionNone;
-use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath};
+use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -101,40 +102,26 @@ declare_clippy_lint! {
 impl_lint_pass!(MemReplace =>
     [MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]);
 
-fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
-    // Check that second argument is `Option::None`
-    if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) {
-        // Since this is a late pass (already type-checked),
-        // and we already know that the second argument is an
-        // `Option`, we do not need to check the first
-        // argument's type. All that's left is to get
-        // replacee's path.
-        let replaced_path = match dest.kind {
-            ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, replaced) => {
-                if let ExprKind::Path(QPath::Resolved(None, replaced_path)) = replaced.kind {
-                    replaced_path
-                } else {
-                    return;
-                }
-            },
-            ExprKind::Path(QPath::Resolved(None, replaced_path)) => replaced_path,
-            _ => return,
-        };
-
-        let mut applicability = Applicability::MachineApplicable;
-        span_lint_and_sugg(
-            cx,
-            MEM_REPLACE_OPTION_WITH_NONE,
-            expr_span,
-            "replacing an `Option` with `None`",
-            "consider `Option::take()` instead",
-            format!(
-                "{}.take()",
-                snippet_with_applicability(cx, replaced_path.span, "", &mut applicability)
-            ),
-            applicability,
-        );
-    }
+fn check_replace_option_with_none(cx: &LateContext<'_>, dest: &Expr<'_>, expr_span: Span) {
+    // Since this is a late pass (already type-checked),
+    // and we already know that the second argument is an
+    // `Option`, we do not need to check the first
+    // argument's type. All that's left is to get
+    // the replacee's expr after peeling off the `&mut`
+    let sugg_expr = peel_ref_operators(cx, dest);
+    let mut applicability = Applicability::MachineApplicable;
+    span_lint_and_sugg(
+        cx,
+        MEM_REPLACE_OPTION_WITH_NONE,
+        expr_span,
+        "replacing an `Option` with `None`",
+        "consider `Option::take()` instead",
+        format!(
+            "{}.take()",
+            Sugg::hir_with_context(cx, sugg_expr, expr_span.ctxt(), "", &mut applicability).maybe_par()
+        ),
+        applicability,
+    );
 }
 
 fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
@@ -200,10 +187,6 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<
     if is_non_aggregate_primitive_type(expr_type) {
         return;
     }
-    // disable lint for Option since it is covered in another lint
-    if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) {
-        return;
-    }
     if is_default_equivalent(cx, src) && !in_external_macro(cx.tcx.sess, expr_span) {
         span_lint_and_then(
             cx,
@@ -246,11 +229,13 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace {
             if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id();
             if cx.tcx.is_diagnostic_item(sym::mem_replace, def_id);
             then {
-                check_replace_option_with_none(cx, src, dest, expr.span);
-                check_replace_with_uninit(cx, src, dest, expr.span);
-                if self.msrv.meets(msrvs::MEM_TAKE) {
+                // Check that second argument is `Option::None`
+                if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) {
+                    check_replace_option_with_none(cx, dest, expr.span);
+                } else if self.msrv.meets(msrvs::MEM_TAKE) {
                     check_replace_with_default(cx, src, dest, expr.span);
                 }
+                check_replace_with_uninit(cx, src, dest, expr.span);
             }
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs b/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs
new file mode 100644
index 00000000000..67ad58d5a8c
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs
@@ -0,0 +1,53 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_range_full;
+use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_hir::{Expr, ExprKind, LangItem, QPath};
+use rustc_lint::LateContext;
+use rustc_span::symbol::sym;
+use rustc_span::Span;
+
+use super::CLEAR_WITH_DRAIN;
+
+// Add `String` here when it is added to diagnostic items
+const ACCEPTABLE_TYPES_WITH_ARG: [rustc_span::Symbol; 2] = [sym::Vec, sym::VecDeque];
+
+const ACCEPTABLE_TYPES_WITHOUT_ARG: [rustc_span::Symbol; 3] = [sym::BinaryHeap, sym::HashMap, sym::HashSet];
+
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, arg: Option<&Expr<'_>>) {
+    if let Some(arg) = arg {
+        if match_acceptable_type(cx, recv, &ACCEPTABLE_TYPES_WITH_ARG)
+            && let ExprKind::Path(QPath::Resolved(None, container_path)) = recv.kind
+            && is_range_full(cx, arg, Some(container_path))
+        {
+            suggest(cx, expr, recv, span);
+        }
+    } else if match_acceptable_type(cx, recv, &ACCEPTABLE_TYPES_WITHOUT_ARG) {
+        suggest(cx, expr, recv, span);
+    }
+}
+
+fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, types: &[rustc_span::Symbol]) -> bool {
+    let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs();
+    types.iter().any(|&ty| is_type_diagnostic_item(cx, expr_ty, ty))
+    // String type is a lang item but not a diagnostic item for now so we need a separate check
+        || is_type_lang_item(cx, expr_ty, LangItem::String)
+}
+
+fn suggest(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span) {
+    if let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def()
+    // Use `opt_item_name` while `String` is not a diagnostic item
+        && let Some(ty_name) = cx.tcx.opt_item_name(adt.did())
+    {
+        span_lint_and_sugg(
+            cx,
+            CLEAR_WITH_DRAIN,
+            span.with_hi(expr.span.hi()),
+            &format!("`drain` used to clear a `{ty_name}`"),
+            "try",
+            "clear()".to_string(),
+            Applicability::MachineApplicable,
+        );
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
index a22285058d4..92d21bb8932 100644
--- a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn};
+use clippy_utils::macros::{find_format_args, format_args_inputs_span, root_macro_call_first_node};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
 use rustc_errors::Applicability;
@@ -136,18 +136,19 @@ pub(super) fn check<'tcx>(
         if !cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) {
             return;
         }
-        let Some(format_args) = FormatArgsExpn::find_nested(cx, arg_root, macro_call.expn) else { return };
-        let span = format_args.inputs_span();
-        let sugg = snippet_with_applicability(cx, span, "..", &mut applicability);
-        span_lint_and_sugg(
-            cx,
-            EXPECT_FUN_CALL,
-            span_replace_word,
-            &format!("use of `{name}` followed by a function call"),
-            "try this",
-            format!("unwrap_or_else({closure_args} panic!({sugg}))"),
-            applicability,
-        );
+        find_format_args(cx, arg_root, macro_call.expn, |format_args| {
+            let span = format_args_inputs_span(format_args);
+            let sugg = snippet_with_applicability(cx, span, "..", &mut applicability);
+            span_lint_and_sugg(
+                cx,
+                EXPECT_FUN_CALL,
+                span_replace_word,
+                &format!("use of `{name}` followed by a function call"),
+                "try this",
+                format!("unwrap_or_else({closure_args} panic!({sugg}))"),
+                applicability,
+            );
+        });
         return;
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs b/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs
index 3da230e12d7..f6772c5c6b3 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs
@@ -1,7 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::higher::Range;
-use clippy_utils::is_integer_const;
-use rustc_ast::ast::RangeLimits;
+use clippy_utils::is_range_full;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, QPath};
 use rustc_lint::LateContext;
@@ -15,8 +13,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span
         && let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def()
         && let Some(ty_name) = cx.tcx.get_diagnostic_name(adt.did())
         && matches!(ty_name, sym::Vec | sym::VecDeque)
-        && let Some(range) = Range::hir(arg)
-        && is_full_range(cx, recv, range)
+        && let ExprKind::Path(QPath::Resolved(None, container_path)) = recv.kind
+        && is_range_full(cx, arg, Some(container_path))
     {
         span_lint_and_sugg(
             cx,
@@ -29,19 +27,3 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span
         );
     };
 }
-
-fn is_full_range(cx: &LateContext<'_>, container: &Expr<'_>, range: Range<'_>) -> bool {
-    range.start.map_or(true, |e| is_integer_const(cx, e, 0))
-        && range.end.map_or(true, |e| {
-            if range.limits == RangeLimits::HalfOpen
-                && let ExprKind::Path(QPath::Resolved(None, container_path)) = container.kind
-                && let ExprKind::MethodCall(name, self_arg, [], _) = e.kind
-                && name.ident.name == sym::len
-                && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind
-            {
-                container_path.res == path.res
-            } else {
-                false
-            }
-        })
-}
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 56e3988bf09..64bf55ba24c 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -9,6 +9,7 @@ mod chars_last_cmp;
 mod chars_last_cmp_with_unwrap;
 mod chars_next_cmp;
 mod chars_next_cmp_with_unwrap;
+mod clear_with_drain;
 mod clone_on_copy;
 mod clone_on_ref_ptr;
 mod cloned_instead_of_copied;
@@ -110,7 +111,7 @@ use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_
 use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, return_ty};
 use if_chain::if_chain;
 use rustc_hir as hir;
-use rustc_hir::{Expr, ExprKind, TraitItem, TraitItemKind};
+use rustc_hir::{Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind};
 use rustc_hir_analysis::hir_ty_to_ty;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
@@ -3190,6 +3191,31 @@ declare_clippy_lint! {
     "single command line argument that looks like it should be multiple arguments"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for usage of `.drain(..)` for the sole purpose of clearing a container.
+    ///
+    /// ### Why is this bad?
+    /// This creates an unnecessary iterator that is dropped immediately.
+    ///
+    /// Calling `.clear()` also makes the intent clearer.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let mut v = vec![1, 2, 3];
+    /// v.drain(..);
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let mut v = vec![1, 2, 3];
+    /// v.clear();
+    /// ```
+    #[clippy::version = "1.69.0"]
+    pub CLEAR_WITH_DRAIN,
+    nursery,
+    "calling `drain` in order to `clear` a container"
+}
+
 pub struct Methods {
     avoid_breaking_exported_api: bool,
     msrv: Msrv,
@@ -3318,6 +3344,7 @@ impl_lint_pass!(Methods => [
     SEEK_TO_START_INSTEAD_OF_REWIND,
     NEEDLESS_COLLECT,
     SUSPICIOUS_COMMAND_ARG_SPACE,
+    CLEAR_WITH_DRAIN,
 ]);
 
 /// Extracts a method call name, args, and `Span` of the method name.
@@ -3562,8 +3589,15 @@ impl Methods {
                     Some(("bytes", recv2, [], _, _)) => bytes_count_to_len::check(cx, expr, recv, recv2),
                     _ => {},
                 },
-                ("drain", [arg]) => {
-                    iter_with_drain::check(cx, expr, recv, span, arg);
+                ("drain", ..) => {
+                    if let Node::Stmt(Stmt { hir_id: _, kind, .. }) = cx.tcx.hir().get_parent(expr.hir_id)
+                        && matches!(kind, StmtKind::Semi(_))
+                        && args.len() <= 1
+                    {
+                        clear_with_drain::check(cx, expr, recv, span, args.first());
+                    } else if let [arg] = args {
+                        iter_with_drain::check(cx, expr, recv, span, arg);
+                    }
                 },
                 ("ends_with", [arg]) => {
                     if let ExprKind::MethodCall(.., span) = expr.kind {
diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
index 87bd007a26a..f1831a30461 100644
--- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
@@ -41,6 +41,7 @@ declare_clippy_lint! {
     /// can't be const as it calls a non-const function. Making `a` const and running Clippy again,
     /// will suggest to make `b` const, too.
     ///
+    /// If you are marking a public function with `const`, removing it again will break API compatibility.
     /// ### Example
     /// ```rust
     /// # struct Foo {
diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
index 25e8de94863..e5713735672 100644
--- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
@@ -154,10 +154,18 @@ impl ArithmeticSideEffects {
                 Self::literal_integer(cx, actual_rhs),
             ) {
                 (None, None) => false,
-                (None, Some(n)) | (Some(n), None) => match (&op.node, n) {
+                (None, Some(n)) => match (&op.node, n) {
                     // Division and module are always valid if applied to non-zero integers
                     (hir::BinOpKind::Div | hir::BinOpKind::Rem, local_n) if local_n != 0 => true,
-                    // Addition or subtracting zeros is always a no-op
+                    // Adding or subtracting zeros is always a no-op
+                    (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0)
+                    // Multiplication by 1 or 0 will never overflow
+                    | (hir::BinOpKind::Mul, 0 | 1)
+                    => true,
+                    _ => false,
+                },
+                (Some(n), None) => match (&op.node, n) {
+                    // Adding or subtracting zeros is always a no-op
                     (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0)
                     // Multiplication by 1 or 0 will never overflow
                     | (hir::BinOpKind::Mul, 0 | 1)
diff --git a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
index 5ac203665d0..a0f831764d0 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
@@ -1,8 +1,15 @@
-use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet};
-use rustc_ast::ast::{Expr, ExprKind, Stmt, StmtKind};
-use rustc_ast::visit::Visitor as AstVisitor;
+use std::ops::ControlFlow;
+
+use clippy_utils::{
+    diagnostics::span_lint_and_sugg,
+    peel_blocks,
+    source::{snippet, walk_span_to_context},
+    visitors::for_each_expr,
+};
 use rustc_errors::Applicability;
-use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_hir::{AsyncGeneratorKind, Closure, Expr, ExprKind, GeneratorKind, MatchSource};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::{lint::in_external_macro, ty::UpvarCapture};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
 declare_clippy_lint! {
@@ -14,106 +21,88 @@ declare_clippy_lint! {
     ///
     /// ### Example
     /// ```rust
-    /// async fn f() -> i32 {
-    ///     1 + 2
-    /// }
-    ///
+    /// let f = async {
+    ///    1 + 2
+    /// };
     /// let fut = async {
-    ///     f().await
+    ///     f.await
     /// };
     /// ```
     /// Use instead:
     /// ```rust
-    /// async fn f() -> i32 {
-    ///     1 + 2
-    /// }
-    ///
-    /// let fut = f();
+    /// let f = async {
+    ///    1 + 2
+    /// };
+    /// let fut = f;
     /// ```
     #[clippy::version = "1.69.0"]
     pub REDUNDANT_ASYNC_BLOCK,
-    nursery,
+    complexity,
     "`async { future.await }` can be replaced by `future`"
 }
 declare_lint_pass!(RedundantAsyncBlock => [REDUNDANT_ASYNC_BLOCK]);
 
-impl EarlyLintPass for RedundantAsyncBlock {
-    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
-        if expr.span.from_expansion() {
-            return;
-        }
-        if let ExprKind::Async(_, block) = &expr.kind && block.stmts.len() == 1 &&
-            let Some(Stmt { kind: StmtKind::Expr(last), .. }) = block.stmts.last() &&
-            let ExprKind::Await(future) = &last.kind &&
-            !future.span.from_expansion() &&
-            !await_in_expr(future)
+impl<'tcx> LateLintPass<'tcx> for RedundantAsyncBlock {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        let span = expr.span;
+        if !in_external_macro(cx.tcx.sess, span) &&
+            let Some(body_expr) = desugar_async_block(cx, expr) &&
+            let Some(expr) = desugar_await(peel_blocks(body_expr)) &&
+            // The await prefix must not come from a macro as its content could change in the future.
+            expr.span.ctxt() == body_expr.span.ctxt() &&
+            // An async block does not have immediate side-effects from a `.await` point-of-view.
+            (!expr.can_have_side_effects() || desugar_async_block(cx, expr).is_some()) &&
+            let Some(shortened_span) = walk_span_to_context(expr.span, span.ctxt())
         {
-            if captures_value(last) {
-                // If the async block captures variables then there is no equivalence.
-                return;
-            }
-
             span_lint_and_sugg(
                 cx,
                 REDUNDANT_ASYNC_BLOCK,
-                expr.span,
+                span,
                 "this async expression only awaits a single future",
                 "you can reduce it to",
-                snippet(cx, future.span, "..").into_owned(),
+                snippet(cx, shortened_span, "..").into_owned(),
                 Applicability::MachineApplicable,
             );
         }
     }
 }
 
-/// Check whether an expression contains `.await`
-fn await_in_expr(expr: &Expr) -> bool {
-    let mut detector = AwaitDetector::default();
-    detector.visit_expr(expr);
-    detector.await_found
-}
-
-#[derive(Default)]
-struct AwaitDetector {
-    await_found: bool,
-}
-
-impl<'ast> AstVisitor<'ast> for AwaitDetector {
-    fn visit_expr(&mut self, ex: &'ast Expr) {
-        match (&ex.kind, self.await_found) {
-            (ExprKind::Await(_), _) => self.await_found = true,
-            (_, false) => rustc_ast::visit::walk_expr(self, ex),
-            _ => (),
-        }
+/// If `expr` is a desugared `async` block, return the original expression if it does not capture
+/// any variable by ref.
+fn desugar_async_block<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+    if let ExprKind::Closure(Closure { body, def_id, .. }) = expr.kind &&
+        let body = cx.tcx.hir().body(*body) &&
+        matches!(body.generator_kind, Some(GeneratorKind::Async(AsyncGeneratorKind::Block)))
+    {
+        cx
+            .typeck_results()
+            .closure_min_captures
+            .get(def_id)
+            .map_or(true, |m| {
+                m.values().all(|places| {
+                    places
+                        .iter()
+                        .all(|place| matches!(place.info.capture_kind, UpvarCapture::ByValue))
+                })
+            })
+            .then_some(body.value)
+    } else {
+        None
     }
 }
 
-/// Check whether an expression may have captured a local variable.
-/// This is done by looking for paths with only one segment, except as
-/// a prefix of `.await` since this would be captured by value.
-///
-/// This function will sometimes return `true` even tough there are no
-/// captures happening: at the AST level, it is impossible to
-/// dinstinguish a function call from a call to a closure which comes
-/// from the local environment.
-fn captures_value(expr: &Expr) -> bool {
-    let mut detector = CaptureDetector::default();
-    detector.visit_expr(expr);
-    detector.capture_found
-}
-
-#[derive(Default)]
-struct CaptureDetector {
-    capture_found: bool,
-}
-
-impl<'ast> AstVisitor<'ast> for CaptureDetector {
-    fn visit_expr(&mut self, ex: &'ast Expr) {
-        match (&ex.kind, self.capture_found) {
-            (ExprKind::Await(fut), _) if matches!(fut.kind, ExprKind::Path(..)) => (),
-            (ExprKind::Path(_, path), _) if path.segments.len() == 1 => self.capture_found = true,
-            (_, false) => rustc_ast::visit::walk_expr(self, ex),
-            _ => (),
-        }
+/// If `expr` is a desugared `.await`, return the original expression if it does not come from a
+/// macro expansion.
+fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+    if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind &&
+        let ExprKind::Call(_, [into_future_arg]) = match_value.kind &&
+        let ctxt = expr.span.ctxt() &&
+        for_each_expr(into_future_arg, |e|
+            walk_span_to_context(e.span, ctxt)
+                .map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))).is_none()
+    {
+        Some(into_future_arg)
+    } else {
+        None
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
index 11b908e7e53..038dfe8e480 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet;
-use rustc_ast::ast::{Item, ItemKind, Ty, TyKind, StaticItem, ConstItem};
+use rustc_ast::ast::{ConstItem, Item, ItemKind, StaticItem, Ty, TyKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -106,7 +106,7 @@ impl EarlyLintPass for RedundantStaticLifetimes {
                 // #2438)
             }
 
-            if let ItemKind::Static(box StaticItem { ty: ref var_type,.. }) = item.kind {
+            if let ItemKind::Static(box StaticItem { ty: ref var_type, .. }) = item.kind {
                 Self::visit_type(var_type, cx, "statics have by default a `'static` lifetime");
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs
index f0d7dd23a67..df126d7617e 100644
--- a/src/tools/clippy/clippy_lints/src/returns.rs
+++ b/src/tools/clippy/clippy_lints/src/returns.rs
@@ -9,7 +9,7 @@ use rustc_hir::intravisit::FnKind;
 use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, LangItem, MatchSource, PatKind, QPath, StmtKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::subst::GenericArgKind;
+use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::def_id::LocalDefId;
 use rustc_span::source_map::Span;
@@ -175,7 +175,7 @@ impl<'tcx> LateLintPass<'tcx> for Return {
                 } else {
                     RetReplacement::Empty
                 };
-                check_final_expr(cx, body.value, vec![], replacement);
+                check_final_expr(cx, body.value, vec![], replacement, None);
             },
             FnKind::ItemFn(..) | FnKind::Method(..) => {
                 check_block_return(cx, &body.value.kind, sp, vec![]);
@@ -188,11 +188,11 @@ impl<'tcx> LateLintPass<'tcx> for Return {
 fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, sp: Span, mut semi_spans: Vec<Span>) {
     if let ExprKind::Block(block, _) = expr_kind {
         if let Some(block_expr) = block.expr {
-            check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty);
+            check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty, None);
         } else if let Some(stmt) = block.stmts.iter().last() {
             match stmt.kind {
                 StmtKind::Expr(expr) => {
-                    check_final_expr(cx, expr, semi_spans, RetReplacement::Empty);
+                    check_final_expr(cx, expr, semi_spans, RetReplacement::Empty, None);
                 },
                 StmtKind::Semi(semi_expr) => {
                     // Remove ending semicolons and any whitespace ' ' in between.
@@ -202,7 +202,7 @@ fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>,
                             span_find_starting_semi(cx.sess().source_map(), semi_span.with_hi(sp.hi()));
                         semi_spans.push(semi_span_to_remove);
                     }
-                    check_final_expr(cx, semi_expr, semi_spans, RetReplacement::Empty);
+                    check_final_expr(cx, semi_expr, semi_spans, RetReplacement::Empty, None);
                 },
                 _ => (),
             }
@@ -216,6 +216,7 @@ fn check_final_expr<'tcx>(
     semi_spans: Vec<Span>, /* containing all the places where we would need to remove semicolons if finding an
                             * needless return */
     replacement: RetReplacement<'tcx>,
+    match_ty_opt: Option<Ty<'_>>,
 ) {
     let peeled_drop_expr = expr.peel_drop_temps();
     match &peeled_drop_expr.kind {
@@ -244,7 +245,22 @@ fn check_final_expr<'tcx>(
                     RetReplacement::Expr(snippet, applicability)
                 }
             } else {
-                replacement
+                match match_ty_opt {
+                    Some(match_ty) => {
+                        match match_ty.kind() {
+                            // If the code got till here with
+                            // tuple not getting detected before it,
+                            // then we are sure it's going to be Unit
+                            // type
+                            ty::Tuple(_) => RetReplacement::Unit,
+                            // We don't want to anything in this case
+                            // cause we can't predict what the user would
+                            // want here
+                            _ => return,
+                        }
+                    },
+                    None => replacement,
+                }
             };
 
             if !cx.tcx.hir().attrs(expr.hir_id).is_empty() {
@@ -268,8 +284,9 @@ fn check_final_expr<'tcx>(
         // note, if without else is going to be a type checking error anyways
         // (except for unit type functions) so we don't match it
         ExprKind::Match(_, arms, MatchSource::Normal) => {
+            let match_ty = cx.typeck_results().expr_ty(peeled_drop_expr);
             for arm in arms.iter() {
-                check_final_expr(cx, arm.body, semi_spans.clone(), RetReplacement::Unit);
+                check_final_expr(cx, arm.body, semi_spans.clone(), RetReplacement::Unit, Some(match_ty));
             }
         },
         // if it's a whole block, check it
@@ -293,6 +310,7 @@ fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, semi_spans: Vec<Span>,
     if ret_span.from_expansion() {
         return;
     }
+
     let applicability = replacement.applicability().unwrap_or(Applicability::MachineApplicable);
     let return_replacement = replacement.to_string();
     let sugg_help = replacement.sugg_help();
diff --git a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
index d46f6a6352c..5743dd21c28 100644
--- a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
+++ b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
@@ -1,6 +1,7 @@
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
 use rustc_ast::node_id::{NodeId, NodeMap};
-use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind};
+use rustc_ast::visit::{walk_expr, Visitor};
+use rustc_ast::{ptr::P, Crate, Expr, ExprKind, Item, ItemKind, MacroDef, ModKind, Ty, TyKind, UseTreeKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -55,7 +56,7 @@ impl EarlyLintPass for SingleComponentPathImports {
             return;
         }
 
-        self.check_mod(cx, &krate.items);
+        self.check_mod(&krate.items);
     }
 
     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
@@ -84,8 +85,43 @@ impl EarlyLintPass for SingleComponentPathImports {
     }
 }
 
+#[derive(Default)]
+struct ImportUsageVisitor {
+    // keep track of imports reused with `self` keyword, such as `self::std` in the example below.
+    // Removing the `use std;` would make this a compile error (#10549)
+    // ```
+    // use std;
+    //
+    // fn main() {
+    //     let _ = self::std::io::stdout();
+    // }
+    // ```
+    imports_referenced_with_self: Vec<Symbol>,
+}
+
+impl<'tcx> Visitor<'tcx> for ImportUsageVisitor {
+    fn visit_expr(&mut self, expr: &Expr) {
+        if let ExprKind::Path(_, path) = &expr.kind
+            && path.segments.len() > 1
+            && path.segments[0].ident.name == kw::SelfLower
+        {
+            self.imports_referenced_with_self.push(path.segments[1].ident.name);
+        }
+        walk_expr(self, expr);
+    }
+
+    fn visit_ty(&mut self, ty: &Ty) {
+        if let TyKind::Path(_, path) = &ty.kind
+            && path.segments.len() > 1
+            && path.segments[0].ident.name == kw::SelfLower
+        {
+            self.imports_referenced_with_self.push(path.segments[1].ident.name);
+        }
+    }
+}
+
 impl SingleComponentPathImports {
-    fn check_mod(&mut self, cx: &EarlyContext<'_>, items: &[P<Item>]) {
+    fn check_mod(&mut self, items: &[P<Item>]) {
         // keep track of imports reused with `self` keyword, such as `self::crypto_hash` in the example
         // below. Removing the `use crypto_hash;` would make this a compile error
         // ```
@@ -108,18 +144,16 @@ impl SingleComponentPathImports {
         // ```
         let mut macros = Vec::new();
 
+        let mut import_usage_visitor = ImportUsageVisitor::default();
         for item in items {
-            self.track_uses(
-                cx,
-                item,
-                &mut imports_reused_with_self,
-                &mut single_use_usages,
-                &mut macros,
-            );
+            self.track_uses(item, &mut imports_reused_with_self, &mut single_use_usages, &mut macros);
+            import_usage_visitor.visit_item(item);
         }
 
         for usage in single_use_usages {
-            if !imports_reused_with_self.contains(&usage.name) {
+            if !imports_reused_with_self.contains(&usage.name)
+                && !import_usage_visitor.imports_referenced_with_self.contains(&usage.name)
+            {
                 self.found.entry(usage.item_id).or_default().push(usage);
             }
         }
@@ -127,7 +161,6 @@ impl SingleComponentPathImports {
 
     fn track_uses(
         &mut self,
-        cx: &EarlyContext<'_>,
         item: &Item,
         imports_reused_with_self: &mut Vec<Symbol>,
         single_use_usages: &mut Vec<SingleUse>,
@@ -139,7 +172,7 @@ impl SingleComponentPathImports {
 
         match &item.kind {
             ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => {
-                self.check_mod(cx, items);
+                self.check_mod(items);
             },
             ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => {
                 macros.push(item.ident.name);
diff --git a/src/tools/clippy/clippy_lints/src/suspicious_doc_comments.rs b/src/tools/clippy/clippy_lints/src/suspicious_doc_comments.rs
new file mode 100644
index 00000000000..e5746ca99ca
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/suspicious_doc_comments.rs
@@ -0,0 +1,94 @@
+use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_then};
+use if_chain::if_chain;
+use rustc_ast::{token::CommentKind, AttrKind, AttrStyle, Attribute, Item};
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::Span;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Detects the use of outer doc comments (`///`, `/**`) followed by a bang (`!`): `///!`
+    ///
+    /// ### Why is this bad?
+    /// Triple-slash comments (known as "outer doc comments") apply to items that follow it.
+    /// An outer doc comment followed by a bang (i.e. `///!`) has no specific meaning.
+    ///
+    /// The user most likely meant to write an inner doc comment (`//!`, `/*!`), which
+    /// applies to the parent item (i.e. the item that the comment is contained in,
+    /// usually a module or crate).
+    ///
+    /// ### Known problems
+    /// Inner doc comments can only appear before items, so there are certain cases where the suggestion
+    /// made by this lint is not valid code. For example:
+    /// ```rs
+    /// fn foo() {}
+    /// ///!
+    /// fn bar() {}
+    /// ```
+    /// This lint detects the doc comment and suggests changing it to `//!`, but an inner doc comment
+    /// is not valid at that position.
+    ///
+    /// ### Example
+    /// In this example, the doc comment is attached to the *function*, rather than the *module*.
+    /// ```rust
+    /// pub mod util {
+    ///     ///! This module contains utility functions.
+    ///
+    ///     pub fn dummy() {}
+    /// }
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// pub mod util {
+    ///     //! This module contains utility functions.
+    ///
+    ///     pub fn dummy() {}
+    /// }
+    /// ```
+    #[clippy::version = "1.70.0"]
+    pub SUSPICIOUS_DOC_COMMENTS,
+    suspicious,
+    "suspicious usage of (outer) doc comments"
+}
+declare_lint_pass!(SuspiciousDocComments => [SUSPICIOUS_DOC_COMMENTS]);
+
+const WARNING: &str = "this is an outer doc comment and does not apply to the parent module or crate";
+const HELP: &str = "use an inner doc comment to document the parent module or crate";
+
+impl EarlyLintPass for SuspiciousDocComments {
+    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
+        let replacements = collect_doc_comment_replacements(&item.attrs);
+
+        if let Some(((lo_span, _), (hi_span, _))) = replacements.first().zip(replacements.last()) {
+            let span = lo_span.to(*hi_span);
+
+            span_lint_and_then(cx, SUSPICIOUS_DOC_COMMENTS, span, WARNING, |diag| {
+                multispan_sugg_with_applicability(diag, HELP, Applicability::MaybeIncorrect, replacements);
+            });
+        }
+    }
+}
+
+fn collect_doc_comment_replacements(attrs: &[Attribute]) -> Vec<(Span, String)> {
+    attrs
+        .iter()
+        .filter_map(|attr| {
+            if_chain! {
+                if let AttrKind::DocComment(com_kind, sym) = attr.kind;
+                if let AttrStyle::Outer = attr.style;
+                if let Some(com) = sym.as_str().strip_prefix('!');
+                then {
+                    let sugg = match com_kind {
+                        CommentKind::Line => format!("//!{com}"),
+                        CommentKind::Block => format!("/*!{com}*/")
+                    };
+                    Some((attr.span, sugg))
+                } else {
+                    None
+                }
+            }
+        })
+        .collect()
+}
diff --git a/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs b/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs
new file mode 100644
index 00000000000..0a0a77082e0
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs
@@ -0,0 +1,71 @@
+use clippy_utils::{diagnostics::span_lint_and_note, is_in_cfg_test, is_in_test_function};
+use rustc_hir::{intravisit::FnKind, Body, FnDecl};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::{def_id::LocalDefId, Span};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Triggers when a testing function (marked with the `#[test]` attribute) isn't inside a testing module
+    /// (marked with `#[cfg(test)]`).
+    /// ### Why is this bad?
+    /// The idiomatic (and more performant) way of writing tests is inside a testing module (flagged with `#[cfg(test)]`),
+    /// having test functions outside of this module is confusing and may lead to them being "hidden".
+    /// ### Example
+    /// ```rust
+    /// #[test]
+    /// fn my_cool_test() {
+    ///     // [...]
+    /// }
+    ///
+    /// #[cfg(test)]
+    /// mod tests {
+    ///     // [...]
+    /// }
+    ///
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// #[cfg(test)]
+    /// mod tests {
+    ///     #[test]
+    ///     fn my_cool_test() {
+    ///         // [...]
+    ///     }
+    /// }
+    /// ```
+    #[clippy::version = "1.70.0"]
+    pub TESTS_OUTSIDE_TEST_MODULE,
+    restriction,
+    "A test function is outside the testing module."
+}
+
+declare_lint_pass!(TestsOutsideTestModule => [TESTS_OUTSIDE_TEST_MODULE]);
+
+impl LateLintPass<'_> for TestsOutsideTestModule {
+    fn check_fn(
+        &mut self,
+        cx: &LateContext<'_>,
+        kind: FnKind<'_>,
+        _: &FnDecl<'_>,
+        body: &Body<'_>,
+        sp: Span,
+        _: LocalDefId,
+    ) {
+        if_chain! {
+            if !matches!(kind, FnKind::Closure);
+            if is_in_test_function(cx.tcx, body.id().hir_id);
+            if !is_in_cfg_test(cx.tcx, body.id().hir_id);
+            then {
+                span_lint_and_note(
+                    cx,
+                    TESTS_OUTSIDE_TEST_MODULE,
+                    sp,
+                    "this function marked with #[test] is outside a #[cfg(test)] module",
+                    None,
+                    "move it to a testing module marked with #[cfg(test)]",
+                );
+            }
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs
index 8530b43243f..85cd74f23ef 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs
@@ -2,8 +2,9 @@ use super::utils::check_cast;
 use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::sugg::Sugg;
+use rustc_ast::ExprPrecedence;
 use rustc_errors::Applicability;
-use rustc_hir::Expr;
+use rustc_hir::{Expr, Node};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{cast::CastKind, Ty};
 
@@ -19,7 +20,7 @@ pub(super) fn check<'tcx>(
 ) -> bool {
     use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast};
     let mut app = Applicability::MachineApplicable;
-    let sugg = match check_cast(cx, e, from_ty, to_ty) {
+    let mut sugg = match check_cast(cx, e, from_ty, to_ty) {
         Some(PtrPtrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast) => {
             Sugg::hir_with_context(cx, arg, e.span.ctxt(), "..", &mut app)
                 .as_ty(to_ty.to_string())
@@ -39,6 +40,12 @@ pub(super) fn check<'tcx>(
         _ => return false,
     };
 
+    if let Node::Expr(parent) = cx.tcx.hir().get_parent(e.hir_id)
+        && parent.precedence().order() > ExprPrecedence::Cast.order()
+    {
+        sugg = format!("({sugg})");
+    }
+
     span_lint_and_sugg(
         cx,
         TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs
new file mode 100644
index 00000000000..912bcda630b
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs
@@ -0,0 +1,120 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_errors::Applicability;
+use rustc_hir::{def_id::LocalDefId, FnDecl, FnRetTy, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::Symbol;
+
+declare_clippy_lint! {
+    /// ### What it does
+    ///
+    /// Checks for a return type containing a `Box<T>` where `T` implements `Sized`
+    ///
+    /// ### Why is this bad?
+    ///
+    /// It's better to just return `T` in these cases. The caller may not need
+    /// the value to be boxed, and it's expensive to free the memory once the
+    /// `Box<T>` been dropped.
+    ///
+    /// ### Example
+    /// ```rust
+    /// fn foo() -> Box<String> {
+    ///     Box::new(String::from("Hello, world!"))
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// fn foo() -> String {
+    ///     String::from("Hello, world!")
+    /// }
+    /// ```
+    #[clippy::version = "1.70.0"]
+    pub UNNECESSARY_BOX_RETURNS,
+    pedantic,
+    "Needlessly returning a Box"
+}
+
+pub struct UnnecessaryBoxReturns {
+    avoid_breaking_exported_api: bool,
+}
+
+impl_lint_pass!(UnnecessaryBoxReturns => [UNNECESSARY_BOX_RETURNS]);
+
+impl UnnecessaryBoxReturns {
+    pub fn new(avoid_breaking_exported_api: bool) -> Self {
+        Self {
+            avoid_breaking_exported_api,
+        }
+    }
+
+    fn check_fn_item(&mut self, cx: &LateContext<'_>, decl: &FnDecl<'_>, def_id: LocalDefId, name: Symbol) {
+        // we don't want to tell someone to break an exported function if they ask us not to
+        if self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(def_id) {
+            return;
+        }
+
+        // functions which contain the word "box" are exempt from this lint
+        if name.as_str().contains("box") {
+            return;
+        }
+
+        let FnRetTy::Return(return_ty_hir) = &decl.output else { return };
+
+        let return_ty = cx
+            .tcx
+            .erase_late_bound_regions(cx.tcx.fn_sig(def_id).skip_binder())
+            .output();
+
+        if !return_ty.is_box() {
+            return;
+        }
+
+        let boxed_ty = return_ty.boxed_ty();
+
+        // it's sometimes useful to return Box<T> if T is unsized, so don't lint those
+        if boxed_ty.is_sized(cx.tcx, cx.param_env) {
+            span_lint_and_then(
+                cx,
+                UNNECESSARY_BOX_RETURNS,
+                return_ty_hir.span,
+                format!("boxed return of the sized type `{boxed_ty}`").as_str(),
+                |diagnostic| {
+                    diagnostic.span_suggestion(
+                        return_ty_hir.span,
+                        "try",
+                        boxed_ty.to_string(),
+                        // the return value and function callers also needs to
+                        // be changed, so this can't be MachineApplicable
+                        Applicability::Unspecified,
+                    );
+                    diagnostic.help("changing this also requires a change to the return expressions in this function");
+                },
+            );
+        }
+    }
+}
+
+impl LateLintPass<'_> for UnnecessaryBoxReturns {
+    fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) {
+        let TraitItemKind::Fn(signature, _) = &item.kind else { return };
+        self.check_fn_item(cx, signature.decl, item.owner_id.def_id, item.ident.name);
+    }
+
+    fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::ImplItem<'_>) {
+        // Ignore implementations of traits, because the lint should be on the
+        // trait, not on the implmentation of it.
+        let Node::Item(parent) = cx.tcx.hir().get_parent(item.hir_id()) else { return };
+        let ItemKind::Impl(parent) = parent.kind else { return };
+        if parent.of_trait.is_some() {
+            return;
+        }
+
+        let ImplItemKind::Fn(signature, ..) = &item.kind else { return };
+        self.check_fn_item(cx, signature.decl, item.owner_id.def_id, item.ident.name);
+    }
+
+    fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
+        let ItemKind::Fn(signature, ..) = &item.kind else { return };
+        self.check_fn_item(cx, signature.decl, item.owner_id.def_id, item.ident.name);
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs b/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs
index af0b4b1592f..084b031982d 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs
@@ -9,7 +9,7 @@ declare_clippy_lint! {
     /// any field.
     ///
     /// ### Why is this bad?
-    /// Readibility suffers from unnecessary struct building.
+    /// Readability suffers from unnecessary struct building.
     ///
     /// ### Example
     /// ```rust
@@ -25,9 +25,13 @@ declare_clippy_lint! {
     /// let a = S { s: String::from("Hello, world!") };
     /// let b = a;
     /// ```
+    ///
+    /// ### Known Problems
+    /// Has false positives when the base is a place expression that cannot be
+    /// moved out of, see [#10547](https://github.com/rust-lang/rust-clippy/issues/10547).
     #[clippy::version = "1.70.0"]
     pub UNNECESSARY_STRUCT_INITIALIZATION,
-    complexity,
+    nursery,
     "struct built from a base that can be written mode concisely"
 }
 declare_lint_pass!(UnnecessaryStruct => [UNNECESSARY_STRUCT_INITIALIZATION]);
diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs
index 7dfb0956077..5a02987453c 100644
--- a/src/tools/clippy/clippy_lints/src/use_self.rs
+++ b/src/tools/clippy/clippy_lints/src/use_self.rs
@@ -10,8 +10,8 @@ use rustc_hir::{
     def::{CtorOf, DefKind, Res},
     def_id::LocalDefId,
     intravisit::{walk_inf, walk_ty, Visitor},
-    Expr, ExprKind, FnRetTy, FnSig, GenericArg, GenericArgsParentheses, GenericParam, GenericParamKind, HirId, Impl, ImplItemKind, Item,
-    ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind,
+    Expr, ExprKind, FnRetTy, FnSig, GenericArg, GenericArgsParentheses, GenericParam, GenericParamKind, HirId, Impl,
+    ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind,
 };
 use rustc_hir_analysis::hir_ty_to_ty;
 use rustc_lint::{LateContext, LateLintPass};
diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs
index 8ba252425a3..896a01af37d 100644
--- a/src/tools/clippy/clippy_lints/src/utils/conf.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs
@@ -249,7 +249,7 @@ define_Conf! {
     /// arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"]
     /// ```
     (arithmetic_side_effects_allowed_unary: rustc_data_structures::fx::FxHashSet<String> = <_>::default()),
-    /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX.
+    /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX, UNNECESSARY_BOX_RETURNS.
     ///
     /// Suppress lints whenever the suggested change would cause breakage for other crates.
     (avoid_breaking_exported_api: bool = true),
@@ -275,13 +275,13 @@ define_Conf! {
     ///
     /// The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value
     /// `".."` can be used as part of the list to indicate, that the configured values should be appended to the
-    /// default configuration of Clippy. By default any configuration will replace the default value.
+    /// default configuration of Clippy. By default, any configuration will replace the default value.
     (disallowed_names: Vec<String> = super::DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect()),
     /// Lint: DOC_MARKDOWN.
     ///
     /// The list of words this lint should not consider as identifiers needing ticks. The value
     /// `".."` can be used as part of the list to indicate, that the configured values should be appended to the
-    /// default configuration of Clippy. By default any configuraction will replace the default value. For example:
+    /// default configuration of Clippy. By default, any configuration will replace the default value. For example:
     /// * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`.
     /// * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list.
     ///
@@ -390,7 +390,7 @@ define_Conf! {
     /// Enforce the named macros always use the braces specified.
     ///
     /// A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro
-    /// is could be used with a full path two `MacroMatcher`s have to be added one with the full path
+    /// could be used with a full path two `MacroMatcher`s have to be added one with the full path
     /// `crate_name::macro_name` and one with just the macro name.
     (standard_macro_braces: Vec<crate::nonstandard_macro_braces::MacroMatcher> = Vec::new()),
     /// Lint: MISSING_ENFORCED_IMPORT_RENAMES.
@@ -408,7 +408,7 @@ define_Conf! {
     /// Lint: INDEX_REFUTABLE_SLICE.
     ///
     /// When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in
-    /// the slice pattern that is suggested. If more elements would be necessary, the lint is suppressed.
+    /// the slice pattern that is suggested. If more elements are necessary, the lint is suppressed.
     /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements.
     (max_suggested_slice_pattern_length: u64 = 3),
     /// Lint: AWAIT_HOLDING_INVALID_TYPE.
@@ -459,6 +459,10 @@ define_Conf! {
     /// Whether to **only** check for missing documentation in items visible within the current
     /// crate. For example, `pub(crate)` items.
     (missing_docs_in_crate_items: bool = false),
+    /// Lint: LARGE_FUTURES.
+    ///
+    /// The maximum byte size a `Future` can have, before it triggers the `clippy::large_futures` lint
+    (future_size_threshold: u64 = 16 * 1024),
 }
 
 /// Search for the configuration file.
@@ -466,7 +470,7 @@ define_Conf! {
 /// # Errors
 ///
 /// Returns any unexpected filesystem error encountered when searching for the config file
-pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
+pub fn lookup_conf_file() -> io::Result<(Option<PathBuf>, Vec<String>)> {
     /// Possible filename to search for.
     const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"];
 
@@ -474,9 +478,11 @@ pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
     // If neither of those exist, use ".".
     let mut current = env::var_os("CLIPPY_CONF_DIR")
         .or_else(|| env::var_os("CARGO_MANIFEST_DIR"))
-        .map_or_else(|| PathBuf::from("."), PathBuf::from);
+        .map_or_else(|| PathBuf::from("."), PathBuf::from)
+        .canonicalize()?;
 
     let mut found_config: Option<PathBuf> = None;
+    let mut warnings = vec![];
 
     loop {
         for config_file_name in &CONFIG_FILE_NAMES {
@@ -487,12 +493,12 @@ pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
                     Ok(md) if md.is_dir() => {},
                     Ok(_) => {
                         // warn if we happen to find two config files #8323
-                        if let Some(ref found_config_) = found_config {
-                            eprintln!(
-                                "Using config file `{}`\nWarning: `{}` will be ignored.",
-                                found_config_.display(),
-                                config_file.display(),
-                            );
+                        if let Some(ref found_config) = found_config {
+                            warnings.push(format!(
+                                "using config file `{}`, `{}` will be ignored",
+                                found_config.display(),
+                                config_file.display()
+                            ));
                         } else {
                             found_config = Some(config_file);
                         }
@@ -502,12 +508,12 @@ pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
         }
 
         if found_config.is_some() {
-            return Ok(found_config);
+            return Ok((found_config, warnings));
         }
 
         // If the current directory has no parent, we're done searching.
         if !current.pop() {
-            return Ok(None);
+            return Ok((None, warnings));
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
index be56b842b98..09fcb82c37c 100644
--- a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
@@ -1,7 +1,12 @@
 use clippy_utils::macros::collect_ast_format_args;
-use rustc_ast::{Expr, ExprKind};
+use clippy_utils::source::snippet_opt;
+use itertools::Itertools;
+use rustc_ast::{Expr, ExprKind, FormatArgs};
+use rustc_lexer::{tokenize, TokenKind};
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::hygiene;
+use std::iter::once;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -15,9 +20,79 @@ declare_clippy_lint! {
 declare_lint_pass!(FormatArgsCollector => [FORMAT_ARGS_COLLECTOR]);
 
 impl EarlyLintPass for FormatArgsCollector {
-    fn check_expr(&mut self, _: &EarlyContext<'_>, expr: &Expr) {
+    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
         if let ExprKind::FormatArgs(args) = &expr.kind {
+            if has_span_from_proc_macro(cx, args) {
+                return;
+            }
+
             collect_ast_format_args(expr.span, args);
         }
     }
 }
+
+/// Detects if the format string or an argument has its span set by a proc macro to something inside
+/// a macro callsite, e.g.
+///
+/// ```ignore
+/// println!(some_proc_macro!("input {}"), a);
+/// ```
+///
+/// Where `some_proc_macro` expands to
+///
+/// ```ignore
+/// println!("output {}", a);
+/// ```
+///
+/// But with the span of `"output {}"` set to the macro input
+///
+/// ```ignore
+/// println!(some_proc_macro!("input {}"), a);
+/// //                        ^^^^^^^^^^
+/// ```
+fn has_span_from_proc_macro(cx: &EarlyContext<'_>, args: &FormatArgs) -> bool {
+    let ctxt = args.span.ctxt();
+
+    // `format!("{} {} {c}", "one", "two", c = "three")`
+    //                       ^^^^^  ^^^^^      ^^^^^^^
+    let argument_span = args
+        .arguments
+        .explicit_args()
+        .iter()
+        .map(|argument| hygiene::walk_chain(argument.expr.span, ctxt));
+
+    // `format!("{} {} {c}", "one", "two", c = "three")`
+    //                     ^^     ^^     ^^^^^^
+    let between_spans = once(args.span)
+        .chain(argument_span)
+        .tuple_windows()
+        .map(|(start, end)| start.between(end));
+
+    for between_span in between_spans {
+        let mut seen_comma = false;
+
+        let Some(snippet) = snippet_opt(cx, between_span) else { return true };
+        for token in tokenize(&snippet) {
+            match token.kind {
+                TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace => {},
+                TokenKind::Comma if !seen_comma => seen_comma = true,
+                // named arguments, `start_val, name = end_val`
+                //                            ^^^^^^^^^ between_span
+                TokenKind::Ident | TokenKind::Eq if seen_comma => {},
+                // An unexpected token usually indicates that we crossed a macro boundary
+                //
+                // `println!(some_proc_macro!("input {}"), a)`
+                //                                      ^^^ between_span
+                // `println!("{}", val!(x))`
+                //               ^^^^^^^ between_span
+                _ => return true,
+            }
+        }
+
+        if !seen_comma {
+            return true;
+        }
+    }
+
+    false
+}
diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs
index 8114a8463fa..d7c94b909bd 100644
--- a/src/tools/clippy/clippy_lints/src/write.rs
+++ b/src/tools/clippy/clippy_lints/src/write.rs
@@ -463,12 +463,18 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
             && let Some(value_string) = snippet_opt(cx, arg.expr.span)
     {
             let (replacement, replace_raw) = match lit.kind {
-                LitKind::Str | LitKind::StrRaw(_)  => extract_str_literal(&value_string),
+                LitKind::Str | LitKind::StrRaw(_)  => match extract_str_literal(&value_string) {
+                    Some(extracted) => extracted,
+                    None => return,
+                },
                 LitKind::Char => (
                     match lit.symbol.as_str() {
                         "\"" => "\\\"",
                         "\\'" => "'",
-                        _ => &value_string[1..value_string.len() - 1],
+                        _ => match value_string.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) {
+                            Some(stripped) => stripped,
+                            None => return,
+                        },
                     }
                     .to_string(),
                     false,
@@ -533,13 +539,13 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
 /// `r#"a"#` -> (`a`, true)
 ///
 /// `"b"` -> (`b`, false)
-fn extract_str_literal(literal: &str) -> (String, bool) {
+fn extract_str_literal(literal: &str) -> Option<(String, bool)> {
     let (literal, raw) = match literal.strip_prefix('r') {
         Some(stripped) => (stripped.trim_matches('#'), true),
         None => (literal, false),
     };
 
-    (literal[1..literal.len() - 1].to_string(), raw)
+    Some((literal.strip_prefix('"')?.strip_suffix('"')?.to_string(), raw))
 }
 
 enum UnescapeErr {
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs
index c5b58b0c060..1f15598db36 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs
@@ -286,8 +286,30 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
     match (l, r) {
         (ExternCrate(l), ExternCrate(r)) => l == r,
         (Use(l), Use(r)) => eq_use_tree(l, r),
-        (Static(box ast::StaticItem { ty: lt, mutability: lm, expr: le}), Static(box ast::StaticItem { ty: rt, mutability: rm, expr: re})) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re),
-        (Const(box ast::ConstItem { defaultness: ld, ty: lt, expr: le}), Const(box ast::ConstItem { defaultness: rd, ty: rt, expr: re} )) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
+        (
+            Static(box ast::StaticItem {
+                ty: lt,
+                mutability: lm,
+                expr: le,
+            }),
+            Static(box ast::StaticItem {
+                ty: rt,
+                mutability: rm,
+                expr: re,
+            }),
+        ) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re),
+        (
+            Const(box ast::ConstItem {
+                defaultness: ld,
+                ty: lt,
+                expr: le,
+            }),
+            Const(box ast::ConstItem {
+                defaultness: rd,
+                ty: rt,
+                expr: re,
+            }),
+        ) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
         (
             Fn(box ast::Fn {
                 defaultness: ld,
@@ -451,7 +473,18 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool {
 pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
     use AssocItemKind::*;
     match (l, r) {
-        (Const(box ast::ConstItem { defaultness: ld, ty: lt, expr: le}), Const(box ast::ConstItem { defaultness: rd, ty: rt, expr: re})) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
+        (
+            Const(box ast::ConstItem {
+                defaultness: ld,
+                ty: lt,
+                expr: le,
+            }),
+            Const(box ast::ConstItem {
+                defaultness: rd,
+                ty: rt,
+                expr: re,
+            }),
+        ) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
         (
             Fn(box ast::Fn {
                 defaultness: ld,
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 9051cf51658..6b677df4641 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -32,7 +32,6 @@ extern crate rustc_lexer;
 extern crate rustc_lint;
 extern crate rustc_middle;
 extern crate rustc_mir_dataflow;
-extern crate rustc_parse_format;
 extern crate rustc_session;
 extern crate rustc_span;
 extern crate rustc_target;
@@ -77,7 +76,7 @@ use std::sync::OnceLock;
 use std::sync::{Mutex, MutexGuard};
 
 use if_chain::if_chain;
-use rustc_ast::ast::{self, LitKind};
+use rustc_ast::ast::{self, LitKind, RangeLimits};
 use rustc_ast::Attribute;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::unhash::UnhashMap;
@@ -95,6 +94,7 @@ use rustc_hir::{
 use rustc_lexer::{tokenize, TokenKind};
 use rustc_lint::{LateContext, Level, Lint, LintContext};
 use rustc_middle::hir::place::PlaceBase;
+use rustc_middle::mir::ConstantKind;
 use rustc_middle::ty as rustc_ty;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
 use rustc_middle::ty::binding::BindingMode;
@@ -113,7 +113,8 @@ use rustc_span::symbol::{kw, Ident, Symbol};
 use rustc_span::Span;
 use rustc_target::abi::Integer;
 
-use crate::consts::{constant, Constant};
+use crate::consts::{constant, miri_to_const, Constant};
+use crate::higher::Range;
 use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param};
 use crate::visitors::for_each_expr;
 
@@ -1490,6 +1491,68 @@ pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
     }
 }
 
+/// Checks whether the given `Expr` is a range equivalent to a `RangeFull`.
+/// For the lower bound, this means that:
+/// - either there is none
+/// - or it is the smallest value that can be represented by the range's integer type
+/// For the upper bound, this means that:
+/// - either there is none
+/// - or it is the largest value that can be represented by the range's integer type and is
+///   inclusive
+/// - or it is a call to some container's `len` method and is exclusive, and the range is passed to
+///   a method call on that same container (e.g. `v.drain(..v.len())`)
+/// If the given `Expr` is not some kind of range, the function returns `false`.
+pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool {
+    let ty = cx.typeck_results().expr_ty(expr);
+    if let Some(Range { start, end, limits }) = Range::hir(expr) {
+        let start_is_none_or_min = start.map_or(true, |start| {
+            if let rustc_ty::Adt(_, subst) = ty.kind()
+                && let bnd_ty = subst.type_at(0)
+                && let Some(min_val) = bnd_ty.numeric_min_val(cx.tcx)
+                && let const_val = cx.tcx.valtree_to_const_val((bnd_ty, min_val.to_valtree()))
+                && let min_const_kind = ConstantKind::from_value(const_val, bnd_ty)
+                && let Some(min_const) = miri_to_const(cx.tcx, min_const_kind)
+                && let Some((start_const, _)) = constant(cx, cx.typeck_results(), start)
+            {
+                start_const == min_const
+            } else {
+                false
+            }
+        });
+        let end_is_none_or_max = end.map_or(true, |end| {
+            match limits {
+                RangeLimits::Closed => {
+                    if let rustc_ty::Adt(_, subst) = ty.kind()
+                        && let bnd_ty = subst.type_at(0)
+                        && let Some(max_val) = bnd_ty.numeric_max_val(cx.tcx)
+                        && let const_val = cx.tcx.valtree_to_const_val((bnd_ty, max_val.to_valtree()))
+                        && let max_const_kind = ConstantKind::from_value(const_val, bnd_ty)
+                        && let Some(max_const) = miri_to_const(cx.tcx, max_const_kind)
+                        && let Some((end_const, _)) = constant(cx, cx.typeck_results(), end)
+                    {
+                        end_const == max_const
+                    } else {
+                        false
+                    }
+                },
+                RangeLimits::HalfOpen => {
+                    if let Some(container_path) = container_path
+                        && let ExprKind::MethodCall(name, self_arg, [], _) = end.kind
+                        && name.ident.name == sym::len
+                        && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind
+                    {
+                        container_path.res == path.res
+                    } else {
+                        false
+                    }
+                },
+            }
+        });
+        return start_is_none_or_min && end_is_none_or_max;
+    }
+    false
+}
+
 /// Checks whether the given expression is a constant integer of the given value.
 /// unlike `is_integer_literal`, this version does const folding
 pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
@@ -2104,8 +2167,7 @@ pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
         .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
     traits::impossible_predicates(
         cx.tcx,
-        traits::elaborate(cx.tcx, predicates)
-            .collect::<Vec<_>>(),
+        traits::elaborate(cx.tcx, predicates).collect::<Vec<_>>(),
     )
 }
 
diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs
index c0e32068eca..62d388a5ece 100644
--- a/src/tools/clippy/clippy_utils/src/macros.rs
+++ b/src/tools/clippy/clippy_utils/src/macros.rs
@@ -1,24 +1,16 @@
 #![allow(clippy::similar_names)] // `expr` and `expn`
 
-use crate::source::snippet_opt;
 use crate::visitors::{for_each_expr, Descend};
 
 use arrayvec::ArrayVec;
-use itertools::{izip, Either, Itertools};
-use rustc_ast::ast::LitKind;
-use rustc_ast::FormatArgs;
+use rustc_ast::{FormatArgs, FormatArgument, FormatPlaceholder};
 use rustc_data_structures::fx::FxHashMap;
-use rustc_hir::intravisit::{walk_expr, Visitor};
-use rustc_hir::{self as hir, Expr, ExprField, ExprKind, HirId, LangItem, Node, QPath, TyKind};
-use rustc_lexer::unescape::unescape_literal;
-use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
+use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath};
 use rustc_lint::LateContext;
-use rustc_parse_format::{self as rpf, Alignment};
 use rustc_span::def_id::DefId;
 use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
-use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Pos, Span, SpanData, Symbol};
+use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Span, Symbol};
 use std::cell::RefCell;
-use std::iter::{once, zip};
 use std::ops::ControlFlow;
 use std::sync::atomic::{AtomicBool, Ordering};
 
@@ -226,11 +218,11 @@ pub enum PanicExpn<'a> {
     /// A single argument that implements `Display` - `panic!("{}", object)`
     Display(&'a Expr<'a>),
     /// Anything else - `panic!("error {}: {}", a, b)`
-    Format(FormatArgsExpn<'a>),
+    Format(&'a Expr<'a>),
 }
 
 impl<'a> PanicExpn<'a> {
-    pub fn parse(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Self> {
+    pub fn parse(expr: &'a Expr<'a>) -> Option<Self> {
         let ExprKind::Call(callee, [arg, rest @ ..]) = &expr.kind else { return None };
         let ExprKind::Path(QPath::Resolved(_, path)) = &callee.kind else { return None };
         let result = match path.segments.last().unwrap().ident.as_str() {
@@ -240,7 +232,7 @@ impl<'a> PanicExpn<'a> {
                 let ExprKind::AddrOf(_, _, e) = &arg.kind else { return None };
                 Self::Display(e)
             },
-            "panic_fmt" => Self::Format(FormatArgsExpn::parse(cx, arg)?),
+            "panic_fmt" => Self::Format(arg),
             // Since Rust 1.52, `assert_{eq,ne}` macros expand to use:
             // `core::panicking::assert_failed(.., left_val, right_val, None | Some(format_args!(..)));`
             "assert_failed" => {
@@ -252,7 +244,7 @@ impl<'a> PanicExpn<'a> {
                 // `msg_arg` is either `None` (no custom message) or `Some(format_args!(..))` (custom message)
                 let msg_arg = &rest[2];
                 match msg_arg.kind {
-                    ExprKind::Call(_, [fmt_arg]) => Self::Format(FormatArgsExpn::parse(cx, fmt_arg)?),
+                    ExprKind::Call(_, [fmt_arg]) => Self::Format(fmt_arg),
                     _ => Self::Empty,
                 }
             },
@@ -304,7 +296,7 @@ fn find_assert_args_inner<'a, const N: usize>(
     let mut args = ArrayVec::new();
     let panic_expn = for_each_expr(expr, |e| {
         if args.is_full() {
-            match PanicExpn::parse(cx, e) {
+            match PanicExpn::parse(e) {
                 Some(expn) => ControlFlow::Break(expn),
                 None => ControlFlow::Continue(Descend::Yes),
             }
@@ -391,30 +383,82 @@ pub fn collect_ast_format_args(span: Span, format_args: &FormatArgs) {
     });
 }
 
-/// Calls `callback` with an AST [`FormatArgs`] node if one is found
+/// Calls `callback` with an AST [`FormatArgs`] node if a `format_args` expansion is found as a
+/// descendant of `expn_id`
 pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId, callback: impl FnOnce(&FormatArgs)) {
     let format_args_expr = for_each_expr(start, |expr| {
         let ctxt = expr.span.ctxt();
-        if ctxt == start.span.ctxt() {
-            ControlFlow::Continue(Descend::Yes)
-        } else if ctxt.outer_expn().is_descendant_of(expn_id)
-            && macro_backtrace(expr.span)
+        if ctxt.outer_expn().is_descendant_of(expn_id) {
+            if macro_backtrace(expr.span)
                 .map(|macro_call| cx.tcx.item_name(macro_call.def_id))
                 .any(|name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl))
-        {
-            ControlFlow::Break(expr)
+            {
+                ControlFlow::Break(expr)
+            } else {
+                ControlFlow::Continue(Descend::Yes)
+            }
         } else {
             ControlFlow::Continue(Descend::No)
         }
     });
 
-    if let Some(format_args_expr) = format_args_expr {
+    if let Some(expr) = format_args_expr {
         AST_FORMAT_ARGS.with(|ast_format_args| {
-            ast_format_args.borrow().get(&format_args_expr.span).map(callback);
+            ast_format_args.borrow().get(&expr.span).map(callback);
         });
     }
 }
 
+/// Attempt to find the [`rustc_hir::Expr`] that corresponds to the [`FormatArgument`]'s value, if
+/// it cannot be found it will return the [`rustc_ast::Expr`].
+pub fn find_format_arg_expr<'hir, 'ast>(
+    start: &'hir Expr<'hir>,
+    target: &'ast FormatArgument,
+) -> Result<&'hir rustc_hir::Expr<'hir>, &'ast rustc_ast::Expr> {
+    for_each_expr(start, |expr| {
+        if expr.span == target.expr.span {
+            ControlFlow::Break(expr)
+        } else {
+            ControlFlow::Continue(())
+        }
+    })
+    .ok_or(&target.expr)
+}
+
+/// Span of the `:` and format specifiers
+///
+/// ```ignore
+/// format!("{:.}"), format!("{foo:.}")
+///           ^^                  ^^
+/// ```
+pub fn format_placeholder_format_span(placeholder: &FormatPlaceholder) -> Option<Span> {
+    let base = placeholder.span?.data();
+
+    // `base.hi` is `{...}|`, subtract 1 byte (the length of '}') so that it points before the closing
+    // brace `{...|}`
+    Some(Span::new(
+        placeholder.argument.span?.hi(),
+        base.hi - BytePos(1),
+        base.ctxt,
+        base.parent,
+    ))
+}
+
+/// Span covering the format string and values
+///
+/// ```ignore
+/// format("{}.{}", 10, 11)
+/// //     ^^^^^^^^^^^^^^^
+/// ```
+pub fn format_args_inputs_span(format_args: &FormatArgs) -> Span {
+    match format_args.arguments.explicit_args() {
+        [] => format_args.span,
+        [.., last] => format_args
+            .span
+            .to(hygiene::walk_chain(last.expr.span, format_args.span.ctxt())),
+    }
+}
+
 /// Returns the [`Span`] of the value at `index` extended to the previous comma, e.g. for the value
 /// `10`
 ///
@@ -436,251 +480,6 @@ pub fn format_arg_removal_span(format_args: &FormatArgs, index: usize) -> Option
     Some(current.with_lo(prev.hi()))
 }
 
-/// The format string doesn't exist in the HIR, so we reassemble it from source code
-#[derive(Debug)]
-pub struct FormatString {
-    /// Span of the whole format string literal, including `[r#]"`.
-    pub span: Span,
-    /// Snippet of the whole format string literal, including `[r#]"`.
-    pub snippet: String,
-    /// If the string is raw `r"..."`/`r#""#`, how many `#`s does it have on each side.
-    pub style: Option<usize>,
-    /// The unescaped value of the format string, e.g. `"val – {}"` for the literal
-    /// `"val \u{2013} {}"`.
-    pub unescaped: String,
-    /// The format string split by format args like `{..}`.
-    pub parts: Vec<Symbol>,
-}
-
-impl FormatString {
-    fn new(cx: &LateContext<'_>, pieces: &Expr<'_>) -> Option<Self> {
-        // format_args!(r"a {} b \", 1);
-        //
-        // expands to
-        //
-        // ::core::fmt::Arguments::new_v1(&["a ", " b \\"],
-        //      &[::core::fmt::ArgumentV1::new_display(&1)]);
-        //
-        // where `pieces` is the expression `&["a ", " b \\"]`. It has the span of `r"a {} b \"`
-        let span = pieces.span;
-        let snippet = snippet_opt(cx, span)?;
-
-        let (inner, style) = match tokenize(&snippet).next()?.kind {
-            TokenKind::Literal { kind, .. } => {
-                let style = match kind {
-                    LiteralKind::Str { .. } => None,
-                    LiteralKind::RawStr { n_hashes: Some(n), .. } => Some(n.into()),
-                    _ => return None,
-                };
-
-                let start = style.map_or(1, |n| 2 + n);
-                let end = snippet.len() - style.map_or(1, |n| 1 + n);
-
-                (&snippet[start..end], style)
-            },
-            _ => return None,
-        };
-
-        let mode = if style.is_some() {
-            unescape::Mode::RawStr
-        } else {
-            unescape::Mode::Str
-        };
-
-        let mut unescaped = String::with_capacity(inner.len());
-        // Sometimes the original string comes from a macro which accepts a malformed string, such as in a
-        // #[display(""somestring)] attribute (accepted by the `displaythis` crate). Reconstructing the
-        // string from the span will not be possible, so we will just return None here.
-        let mut unparsable = false;
-        unescape_literal(inner, mode, &mut |_, ch| match ch {
-            Ok(ch) => unescaped.push(ch),
-            Err(e) if !e.is_fatal() => (),
-            Err(_) => unparsable = true,
-        });
-        if unparsable {
-            return None;
-        }
-
-        let mut parts = Vec::new();
-        let _: Option<!> = for_each_expr(pieces, |expr| {
-            if let ExprKind::Lit(lit) = &expr.kind
-                && let LitKind::Str(symbol, _) = lit.node
-            {
-                parts.push(symbol);
-            }
-            ControlFlow::Continue(())
-        });
-
-        Some(Self {
-            span,
-            snippet,
-            style,
-            unescaped,
-            parts,
-        })
-    }
-}
-
-struct FormatArgsValues<'tcx> {
-    /// Values passed after the format string and implicit captures. `[1, z + 2, x]` for
-    /// `format!("{x} {} {}", 1, z + 2)`.
-    value_args: Vec<&'tcx Expr<'tcx>>,
-    /// Maps an `rt::v1::Argument::position` or an `rt::v1::Count::Param` to its index in
-    /// `value_args`
-    pos_to_value_index: Vec<usize>,
-    /// Used to check if a value is declared inline & to resolve `InnerSpan`s.
-    format_string_span: SpanData,
-}
-
-impl<'tcx> FormatArgsValues<'tcx> {
-    fn new_empty(format_string_span: SpanData) -> Self {
-        Self {
-            value_args: Vec::new(),
-            pos_to_value_index: Vec::new(),
-            format_string_span,
-        }
-    }
-
-    fn new(args: &'tcx Expr<'tcx>, format_string_span: SpanData) -> Self {
-        let mut pos_to_value_index = Vec::new();
-        let mut value_args = Vec::new();
-        let _: Option<!> = for_each_expr(args, |expr| {
-            if expr.span.ctxt() == args.span.ctxt() {
-                // ArgumentV1::new_<format_trait>(<val>)
-                // ArgumentV1::from_usize(<val>)
-                if let ExprKind::Call(callee, [val]) = expr.kind
-                    && let ExprKind::Path(QPath::TypeRelative(ty, _)) = callee.kind
-                    && let TyKind::Path(QPath::LangItem(LangItem::FormatArgument, _, _)) = ty.kind
-                {
-                    let val_idx = if val.span.ctxt() == expr.span.ctxt()
-                        && let ExprKind::Field(_, field) = val.kind
-                        && let Ok(idx) = field.name.as_str().parse()
-                    {
-                        // tuple index
-                        idx
-                    } else {
-                        // assume the value expression is passed directly
-                        pos_to_value_index.len()
-                    };
-
-                    pos_to_value_index.push(val_idx);
-                }
-                ControlFlow::Continue(Descend::Yes)
-            } else {
-                // assume that any expr with a differing span is a value
-                value_args.push(expr);
-                ControlFlow::Continue(Descend::No)
-            }
-        });
-
-        Self {
-            value_args,
-            pos_to_value_index,
-            format_string_span,
-        }
-    }
-}
-
-/// The positions of a format argument's value, precision and width
-///
-/// A position is an index into the second argument of `Arguments::new_v1[_formatted]`
-#[derive(Debug, Default, Copy, Clone)]
-struct ParamPosition {
-    /// The position stored in `rt::v1::Argument::position`.
-    value: usize,
-    /// The position stored in `rt::v1::FormatSpec::width` if it is a `Count::Param`.
-    width: Option<usize>,
-    /// The position stored in `rt::v1::FormatSpec::precision` if it is a `Count::Param`.
-    precision: Option<usize>,
-}
-
-impl<'tcx> Visitor<'tcx> for ParamPosition {
-    fn visit_expr_field(&mut self, field: &'tcx ExprField<'tcx>) {
-        match field.ident.name {
-            sym::position => {
-                if let ExprKind::Lit(lit) = &field.expr.kind
-                    && let LitKind::Int(pos, _) = lit.node
-                {
-                    self.value = pos as usize;
-                }
-            },
-            sym::precision => {
-                self.precision = parse_count(field.expr);
-            },
-            sym::width => {
-                self.width = parse_count(field.expr);
-            },
-            _ => walk_expr(self, field.expr),
-        }
-    }
-}
-
-fn parse_count(expr: &Expr<'_>) -> Option<usize> {
-    // <::core::fmt::rt::v1::Count>::Param(1usize),
-    if let ExprKind::Call(ctor, [val]) = expr.kind
-        && let ExprKind::Path(QPath::TypeRelative(_, path)) = ctor.kind
-            && path.ident.name == sym::Param
-            && let ExprKind::Lit(lit) = &val.kind
-            && let LitKind::Int(pos, _) = lit.node
-    {
-        Some(pos as usize)
-    } else {
-        None
-    }
-}
-
-/// Parses the `fmt` arg of `Arguments::new_v1_formatted(pieces, args, fmt, _)`
-fn parse_rt_fmt<'tcx>(fmt_arg: &'tcx Expr<'tcx>) -> Option<impl Iterator<Item = ParamPosition> + 'tcx> {
-    if let ExprKind::AddrOf(.., array) = fmt_arg.kind
-        && let ExprKind::Array(specs) = array.kind
-    {
-        Some(specs.iter().map(|spec| {
-            if let ExprKind::Call(f, args) = spec.kind
-                && let ExprKind::Path(QPath::TypeRelative(ty, f)) = f.kind
-                && let TyKind::Path(QPath::LangItem(LangItem::FormatPlaceholder, _, _)) = ty.kind
-                && f.ident.name == sym::new
-                && let [position, _fill, _align, _flags, precision, width] = args
-                && let ExprKind::Lit(position) = &position.kind
-                && let LitKind::Int(position, _) = position.node {
-                    ParamPosition {
-                        value: position as usize,
-                        width: parse_count(width),
-                        precision: parse_count(precision),
-                    }
-            } else {
-                ParamPosition::default()
-            }
-        }))
-    } else {
-        None
-    }
-}
-
-/// `Span::from_inner`, but for `rustc_parse_format`'s `InnerSpan`
-fn span_from_inner(base: SpanData, inner: rpf::InnerSpan) -> Span {
-    Span::new(
-        base.lo + BytePos::from_usize(inner.start),
-        base.lo + BytePos::from_usize(inner.end),
-        base.ctxt,
-        base.parent,
-    )
-}
-
-/// How a format parameter is used in the format string
-#[derive(Debug, Copy, Clone, PartialEq, Eq)]
-pub enum FormatParamKind {
-    /// An implicit parameter , such as `{}` or `{:?}`.
-    Implicit,
-    /// A parameter with an explicit number, e.g. `{1}`, `{0:?}`, or `{:.0$}`
-    Numbered,
-    /// A parameter with an asterisk precision. e.g. `{:.*}`.
-    Starred,
-    /// A named parameter with a named `value_arg`, such as the `x` in `format!("{x}", x = 1)`.
-    Named(Symbol),
-    /// An implicit named parameter, such as the `y` in `format!("{y}")`.
-    NamedInline(Symbol),
-}
-
 /// Where a format parameter is being used in the format string
 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
 pub enum FormatParamUsage {
@@ -692,467 +491,6 @@ pub enum FormatParamUsage {
     Precision,
 }
 
-/// A `FormatParam` is any place in a `FormatArgument` that refers to a supplied value, e.g.
-///
-/// ```
-/// let precision = 2;
-/// format!("{:.precision$}", 0.1234);
-/// ```
-///
-/// has two `FormatParam`s, a [`FormatParamKind::Implicit`] `.kind` with a `.value` of `0.1234`
-/// and a [`FormatParamKind::NamedInline("precision")`] `.kind` with a `.value` of `2`
-#[derive(Debug, Copy, Clone)]
-pub struct FormatParam<'tcx> {
-    /// The expression this parameter refers to.
-    pub value: &'tcx Expr<'tcx>,
-    /// How this parameter refers to its `value`.
-    pub kind: FormatParamKind,
-    /// Where this format param is being used - argument/width/precision
-    pub usage: FormatParamUsage,
-    /// Span of the parameter, may be zero width. Includes the whitespace of implicit parameters.
-    ///
-    /// ```text
-    /// format!("{}, {  }, {0}, {name}", ...);
-    ///          ^    ~~    ~    ~~~~
-    /// ```
-    pub span: Span,
-}
-
-impl<'tcx> FormatParam<'tcx> {
-    fn new(
-        mut kind: FormatParamKind,
-        usage: FormatParamUsage,
-        position: usize,
-        inner: rpf::InnerSpan,
-        values: &FormatArgsValues<'tcx>,
-    ) -> Option<Self> {
-        let value_index = *values.pos_to_value_index.get(position)?;
-        let value = *values.value_args.get(value_index)?;
-        let span = span_from_inner(values.format_string_span, inner);
-
-        // if a param is declared inline, e.g. `format!("{x}")`, the generated expr's span points
-        // into the format string
-        if let FormatParamKind::Named(name) = kind && values.format_string_span.contains(value.span.data()) {
-            kind = FormatParamKind::NamedInline(name);
-        }
-
-        Some(Self {
-            value,
-            kind,
-            usage,
-            span,
-        })
-    }
-}
-
-/// Used by [width](https://doc.rust-lang.org/std/fmt/#width) and
-/// [precision](https://doc.rust-lang.org/std/fmt/#precision) specifiers.
-#[derive(Debug, Copy, Clone)]
-pub enum Count<'tcx> {
-    /// Specified with a literal number, stores the value.
-    Is(usize, Span),
-    /// Specified using `$` and `*` syntaxes. The `*` format is still considered to be
-    /// `FormatParamKind::Numbered`.
-    Param(FormatParam<'tcx>),
-    /// Not specified.
-    Implied(Option<Span>),
-}
-
-impl<'tcx> Count<'tcx> {
-    fn new(
-        usage: FormatParamUsage,
-        count: rpf::Count<'_>,
-        position: Option<usize>,
-        inner: Option<rpf::InnerSpan>,
-        values: &FormatArgsValues<'tcx>,
-    ) -> Option<Self> {
-        let span = inner.map(|inner| span_from_inner(values.format_string_span, inner));
-
-        Some(match count {
-            rpf::Count::CountIs(val) => Self::Is(val, span?),
-            rpf::Count::CountIsName(name, _) => Self::Param(FormatParam::new(
-                FormatParamKind::Named(Symbol::intern(name)),
-                usage,
-                position?,
-                inner?,
-                values,
-            )?),
-            rpf::Count::CountIsParam(_) => Self::Param(FormatParam::new(
-                FormatParamKind::Numbered,
-                usage,
-                position?,
-                inner?,
-                values,
-            )?),
-            rpf::Count::CountIsStar(_) => Self::Param(FormatParam::new(
-                FormatParamKind::Starred,
-                usage,
-                position?,
-                inner?,
-                values,
-            )?),
-            rpf::Count::CountImplied => Self::Implied(span),
-        })
-    }
-
-    pub fn is_implied(self) -> bool {
-        matches!(self, Count::Implied(_))
-    }
-
-    pub fn param(self) -> Option<FormatParam<'tcx>> {
-        match self {
-            Count::Param(param) => Some(param),
-            _ => None,
-        }
-    }
-
-    pub fn span(self) -> Option<Span> {
-        match self {
-            Count::Is(_, span) => Some(span),
-            Count::Param(param) => Some(param.span),
-            Count::Implied(span) => span,
-        }
-    }
-}
-
-/// Specification for the formatting of an argument in the format string. See
-/// <https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters> for the precise meanings.
-#[derive(Debug)]
-pub struct FormatSpec<'tcx> {
-    /// Optionally specified character to fill alignment with.
-    pub fill: Option<char>,
-    /// Optionally specified alignment.
-    pub align: Alignment,
-    /// Whether all flag options are set to default (no flags specified).
-    pub no_flags: bool,
-    /// Represents either the maximum width or the integer precision.
-    pub precision: Count<'tcx>,
-    /// The minimum width, will be padded according to `width`/`align`
-    pub width: Count<'tcx>,
-    /// The formatting trait used by the argument, e.g. `sym::Display` for `{}`, `sym::Debug` for
-    /// `{:?}`.
-    pub r#trait: Symbol,
-    pub trait_span: Option<Span>,
-}
-
-impl<'tcx> FormatSpec<'tcx> {
-    fn new(spec: rpf::FormatSpec<'_>, positions: ParamPosition, values: &FormatArgsValues<'tcx>) -> Option<Self> {
-        Some(Self {
-            fill: spec.fill,
-            align: spec.align,
-            no_flags: spec.sign.is_none() && !spec.alternate && !spec.zero_pad && spec.debug_hex.is_none(),
-            precision: Count::new(
-                FormatParamUsage::Precision,
-                spec.precision,
-                positions.precision,
-                spec.precision_span,
-                values,
-            )?,
-            width: Count::new(
-                FormatParamUsage::Width,
-                spec.width,
-                positions.width,
-                spec.width_span,
-                values,
-            )?,
-            r#trait: match spec.ty {
-                "" => sym::Display,
-                "?" => sym::Debug,
-                "o" => sym!(Octal),
-                "x" => sym!(LowerHex),
-                "X" => sym!(UpperHex),
-                "p" => sym::Pointer,
-                "b" => sym!(Binary),
-                "e" => sym!(LowerExp),
-                "E" => sym!(UpperExp),
-                _ => return None,
-            },
-            trait_span: spec
-                .ty_span
-                .map(|span| span_from_inner(values.format_string_span, span)),
-        })
-    }
-
-    /// Returns true if this format spec is unchanged from the default. e.g. returns true for `{}`,
-    /// `{foo}` and `{2}`, but false for `{:?}`, `{foo:5}` and `{3:.5}`
-    pub fn is_default(&self) -> bool {
-        self.r#trait == sym::Display && self.is_default_for_trait()
-    }
-
-    /// Has no other formatting specifiers than setting the format trait. returns true for `{}`,
-    /// `{foo}`, `{:?}`, but false for `{foo:5}`, `{3:.5?}`
-    pub fn is_default_for_trait(&self) -> bool {
-        self.width.is_implied() && self.precision.is_implied() && self.align == Alignment::AlignUnknown && self.no_flags
-    }
-}
-
-/// A format argument, such as `{}`, `{foo:?}`.
-#[derive(Debug)]
-pub struct FormatArg<'tcx> {
-    /// The parameter the argument refers to.
-    pub param: FormatParam<'tcx>,
-    /// How to format `param`.
-    pub format: FormatSpec<'tcx>,
-    /// span of the whole argument, `{..}`.
-    pub span: Span,
-}
-
-impl<'tcx> FormatArg<'tcx> {
-    /// Span of the `:` and format specifiers
-    ///
-    /// ```ignore
-    /// format!("{:.}"), format!("{foo:.}")
-    ///           ^^                  ^^
-    /// ```
-    pub fn format_span(&self) -> Span {
-        let base = self.span.data();
-
-        // `base.hi` is `{...}|`, subtract 1 byte (the length of '}') so that it points before the closing
-        // brace `{...|}`
-        Span::new(self.param.span.hi(), base.hi - BytePos(1), base.ctxt, base.parent)
-    }
-}
-
-/// A parsed `format_args!` expansion.
-#[derive(Debug)]
-pub struct FormatArgsExpn<'tcx> {
-    /// The format string literal.
-    pub format_string: FormatString,
-    /// The format arguments, such as `{:?}`.
-    pub args: Vec<FormatArg<'tcx>>,
-    /// Has an added newline due to `println!()`/`writeln!()`/etc. The last format string part will
-    /// include this added newline.
-    pub newline: bool,
-    /// Spans of the commas between the format string and explicit values, excluding any trailing
-    /// comma
-    ///
-    /// ```ignore
-    /// format!("..", 1, 2, 3,)
-    /// //          ^  ^  ^
-    /// ```
-    comma_spans: Vec<Span>,
-    /// Explicit values passed after the format string, ignoring implicit captures. `[1, z + 2]` for
-    /// `format!("{x} {} {y}", 1, z + 2)`.
-    explicit_values: Vec<&'tcx Expr<'tcx>>,
-}
-
-impl<'tcx> FormatArgsExpn<'tcx> {
-    /// Gets the spans of the commas inbetween the format string and explicit args, not including
-    /// any trailing comma
-    ///
-    /// ```ignore
-    /// format!("{} {}", a, b)
-    /// //             ^  ^
-    /// ```
-    ///
-    /// Ensures that the format string and values aren't coming from a proc macro that sets the
-    /// output span to that of its input
-    fn comma_spans(cx: &LateContext<'_>, explicit_values: &[&Expr<'_>], fmt_span: Span) -> Option<Vec<Span>> {
-        // `format!("{} {} {c}", "one", "two", c = "three")`
-        //                       ^^^^^  ^^^^^      ^^^^^^^
-        let value_spans = explicit_values
-            .iter()
-            .map(|val| hygiene::walk_chain(val.span, fmt_span.ctxt()));
-
-        // `format!("{} {} {c}", "one", "two", c = "three")`
-        //                     ^^     ^^     ^^^^^^
-        let between_spans = once(fmt_span)
-            .chain(value_spans)
-            .tuple_windows()
-            .map(|(start, end)| start.between(end));
-
-        let mut comma_spans = Vec::new();
-        for between_span in between_spans {
-            let mut offset = 0;
-            let mut seen_comma = false;
-
-            for token in tokenize(&snippet_opt(cx, between_span)?) {
-                match token.kind {
-                    TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace => {},
-                    TokenKind::Comma if !seen_comma => {
-                        seen_comma = true;
-
-                        let base = between_span.data();
-                        comma_spans.push(Span::new(
-                            base.lo + BytePos(offset),
-                            base.lo + BytePos(offset + 1),
-                            base.ctxt,
-                            base.parent,
-                        ));
-                    },
-                    // named arguments, `start_val, name = end_val`
-                    //                            ^^^^^^^^^ between_span
-                    TokenKind::Ident | TokenKind::Eq if seen_comma => {},
-                    // An unexpected token usually indicates the format string or a value came from a proc macro output
-                    // that sets the span of its output to an input, e.g. `println!(some_proc_macro!("input"), ..)` that
-                    // emits a string literal with the span set to that of `"input"`
-                    _ => return None,
-                }
-                offset += token.len;
-            }
-
-            if !seen_comma {
-                return None;
-            }
-        }
-
-        Some(comma_spans)
-    }
-
-    pub fn parse(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<Self> {
-        let macro_name = macro_backtrace(expr.span)
-            .map(|macro_call| cx.tcx.item_name(macro_call.def_id))
-            .find(|&name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl))?;
-        let newline = macro_name == sym::format_args_nl;
-
-        // ::core::fmt::Arguments::new_const(pieces)
-        // ::core::fmt::Arguments::new_v1(pieces, args)
-        // ::core::fmt::Arguments::new_v1_formatted(pieces, args, fmt, _unsafe_arg)
-        if let ExprKind::Call(callee, [pieces, rest @ ..]) = expr.kind
-            && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind
-            && let TyKind::Path(QPath::LangItem(LangItem::FormatArguments, _, _)) = ty.kind
-            && matches!(seg.ident.as_str(), "new_const" | "new_v1" | "new_v1_formatted")
-        {
-            let format_string = FormatString::new(cx, pieces)?;
-
-            let mut parser = rpf::Parser::new(
-                &format_string.unescaped,
-                format_string.style,
-                Some(format_string.snippet.clone()),
-                // `format_string.unescaped` does not contain the appended newline
-                false,
-                rpf::ParseMode::Format,
-            );
-
-            let parsed_args = parser
-                .by_ref()
-                .filter_map(|piece| match piece {
-                    rpf::Piece::NextArgument(a) => Some(a),
-                    rpf::Piece::String(_) => None,
-                })
-                .collect_vec();
-            if !parser.errors.is_empty() {
-                return None;
-            }
-
-            let positions = if let Some(fmt_arg) = rest.get(1) {
-                // If the argument contains format specs, `new_v1_formatted(_, _, fmt, _)`, parse
-                // them.
-
-                Either::Left(parse_rt_fmt(fmt_arg)?)
-            } else {
-                // If no format specs are given, the positions are in the given order and there are
-                // no `precision`/`width`s to consider.
-
-                Either::Right((0..).map(|n| ParamPosition {
-                    value: n,
-                    width: None,
-                    precision: None,
-                }))
-            };
-
-            let values = if let Some(args) = rest.first() {
-                FormatArgsValues::new(args, format_string.span.data())
-            } else {
-                FormatArgsValues::new_empty(format_string.span.data())
-            };
-
-            let args = izip!(positions, parsed_args, parser.arg_places)
-                .map(|(position, parsed_arg, arg_span)| {
-                    Some(FormatArg {
-                        param: FormatParam::new(
-                            match parsed_arg.position {
-                                rpf::Position::ArgumentImplicitlyIs(_) => FormatParamKind::Implicit,
-                                rpf::Position::ArgumentIs(_) => FormatParamKind::Numbered,
-                                // NamedInline is handled by `FormatParam::new()`
-                                rpf::Position::ArgumentNamed(name) => FormatParamKind::Named(Symbol::intern(name)),
-                            },
-                            FormatParamUsage::Argument,
-                            position.value,
-                            parsed_arg.position_span,
-                            &values,
-                        )?,
-                        format: FormatSpec::new(parsed_arg.format, position, &values)?,
-                        span: span_from_inner(values.format_string_span, arg_span),
-                    })
-                })
-                .collect::<Option<Vec<_>>>()?;
-
-            let mut explicit_values = values.value_args;
-            // remove values generated for implicitly captured vars
-            let len = explicit_values
-                .iter()
-                .take_while(|val| !format_string.span.contains(val.span))
-                .count();
-            explicit_values.truncate(len);
-
-            let comma_spans = Self::comma_spans(cx, &explicit_values, format_string.span)?;
-
-            Some(Self {
-                format_string,
-                args,
-                newline,
-                comma_spans,
-                explicit_values,
-            })
-        } else {
-            None
-        }
-    }
-
-    pub fn find_nested(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expn_id: ExpnId) -> Option<Self> {
-        for_each_expr(expr, |e| {
-            let e_ctxt = e.span.ctxt();
-            if e_ctxt == expr.span.ctxt() {
-                ControlFlow::Continue(Descend::Yes)
-            } else if e_ctxt.outer_expn().is_descendant_of(expn_id) {
-                if let Some(args) = FormatArgsExpn::parse(cx, e) {
-                    ControlFlow::Break(args)
-                } else {
-                    ControlFlow::Continue(Descend::No)
-                }
-            } else {
-                ControlFlow::Continue(Descend::No)
-            }
-        })
-    }
-
-    /// Source callsite span of all inputs
-    pub fn inputs_span(&self) -> Span {
-        match *self.explicit_values {
-            [] => self.format_string.span,
-            [.., last] => self
-                .format_string
-                .span
-                .to(hygiene::walk_chain(last.span, self.format_string.span.ctxt())),
-        }
-    }
-
-    /// Get the span of a value expanded to the previous comma, e.g. for the value `10`
-    ///
-    /// ```ignore
-    /// format("{}.{}", 10, 11)
-    /// //            ^^^^
-    /// ```
-    pub fn value_with_prev_comma_span(&self, value_id: HirId) -> Option<Span> {
-        for (comma_span, value) in zip(&self.comma_spans, &self.explicit_values) {
-            if value.hir_id == value_id {
-                return Some(comma_span.to(hygiene::walk_chain(value.span, comma_span.ctxt())));
-            }
-        }
-
-        None
-    }
-
-    /// Iterator of all format params, both values and those referenced by `width`/`precision`s.
-    pub fn params(&'tcx self) -> impl Iterator<Item = FormatParam<'tcx>> {
-        self.args
-            .iter()
-            .flat_map(|arg| [Some(arg.param), arg.format.precision.param(), arg.format.width.param()])
-            .flatten()
-    }
-}
-
 /// A node with a `HirId` and a `Span`
 pub trait HirNode {
     fn hir_id(&self) -> HirId;
diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs
index c919575bfe9..9be2d0eae80 100644
--- a/src/tools/clippy/clippy_utils/src/paths.rs
+++ b/src/tools/clippy/clippy_utils/src/paths.rs
@@ -23,6 +23,7 @@ pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
 pub const CORE_ITER_CLONED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "cloned"];
 pub const CORE_ITER_COPIED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "copied"];
 pub const CORE_ITER_FILTER: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "filter"];
+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 DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"];
 pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"];
@@ -113,6 +114,7 @@ pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"];
 pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"];
 pub const CONVERT_IDENTITY: [&str; 3] = ["core", "convert", "identity"];
 pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
+pub const STD_IO_LINES: [&str; 3] = ["std", "io", "Lines"];
 pub const STD_IO_SEEK: [&str; 3] = ["std", "io", "Seek"];
 pub const STD_IO_SEEK_FROM_CURRENT: [&str; 4] = ["std", "io", "SeekFrom", "Current"];
 pub const STD_IO_SEEKFROM_START: [&str; 4] = ["std", "io", "SeekFrom", "Start"];
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index 0b47234647f..9449f0b5567 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -541,9 +541,25 @@ pub fn same_type_and_consts<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
 pub fn is_uninit_value_valid_for_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
     cx.tcx
         .check_validity_requirement((ValidityRequirement::Uninit, cx.param_env.and(ty)))
-        // For types containing generic parameters we cannot get a layout to check.
-        // Therefore, we are conservative and assume that they don't allow uninit.
-        .unwrap_or(false)
+        .unwrap_or_else(|_| is_uninit_value_valid_for_ty_fallback(cx, ty))
+}
+
+/// A fallback for polymorphic types, which are not supported by `check_validity_requirement`.
+fn is_uninit_value_valid_for_ty_fallback<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+    match *ty.kind() {
+        // The array length may be polymorphic, let's try the inner type.
+        ty::Array(component, _) => is_uninit_value_valid_for_ty(cx, component),
+        // Peek through tuples and try their fallbacks.
+        ty::Tuple(types) => types.iter().all(|ty| is_uninit_value_valid_for_ty(cx, ty)),
+        // Unions are always fine right now.
+        // This includes MaybeUninit, the main way people use uninitialized memory.
+        // For ADTs, we could look at all fields just like for tuples, but that's potentially
+        // exponential, so let's avoid doing that for now. Code doing that is sketchy enough to
+        // just use an `#[allow()]`.
+        ty::Adt(adt, _) => adt.is_union(),
+        // For the rest, conservatively assume that they cannot be uninit.
+        _ => false,
+    }
 }
 
 /// Gets an iterator over all predicates which apply to the given item.
diff --git a/src/tools/clippy/etc/relicense/RELICENSE_DOCUMENTATION.md b/src/tools/clippy/etc/relicense/RELICENSE_DOCUMENTATION.md
index fcd7abbf3f1..ffb99cde4f8 100644
--- a/src/tools/clippy/etc/relicense/RELICENSE_DOCUMENTATION.md
+++ b/src/tools/clippy/etc/relicense/RELICENSE_DOCUMENTATION.md
@@ -35,7 +35,7 @@ relicensing are archived on GitHub. We also have saved Wayback Machine copies of
 
 The usernames of commenters on these issues can be found in relicense_comments.txt
 
-There are a couple people in relicense_comments.txt who are not found in contributors.txt:
+There are a few people in relicense_comments.txt who are not found in contributors.txt:
 
 - @EpocSquadron has [made minor text contributions to the
   README](https://github.com/rust-lang/rust-clippy/commits?author=EpocSquadron) which have since been overwritten, and
@@ -55,7 +55,7 @@ There are a couple people in relicense_comments.txt who are not found in contrib
   we rewrote (see below)
 
 
-Two of these contributors had nonminor contributions (#2184, #427) requiring a rewrite, carried out in #3251
+Two of these contributors had non-minor contributions (#2184, #427) requiring a rewrite, carried out in #3251
 ([archive](http://web.archive.org/web/20181005192411/https://github.com/rust-lang-nursery/rust-clippy/pull/3251),
 [screenshot](https://user-images.githubusercontent.com/1617736/46573515-5cb69580-c94b-11e8-86e5-b456452121b2.png))
 
diff --git a/src/tools/clippy/lintcheck/README.md b/src/tools/clippy/lintcheck/README.md
index e997eb47e32..faf3ce9093a 100644
--- a/src/tools/clippy/lintcheck/README.md
+++ b/src/tools/clippy/lintcheck/README.md
@@ -16,7 +16,7 @@ or
 cargo lintcheck
 ```
 
-By default the logs will be saved into
+By default, the logs will be saved into
 `lintcheck-logs/lintcheck_crates_logs.txt`.
 
 You can set a custom sources.toml by adding `--crates-toml custom.toml` or using
diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain
index 0b2458ea007..91e8ccea1f4 100644
--- a/src/tools/clippy/rust-toolchain
+++ b/src/tools/clippy/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2023-03-24"
+channel = "nightly-2023-04-06"
 components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs
index 9e0822404b6..718bc41fb99 100644
--- a/src/tools/clippy/src/driver.rs
+++ b/src/tools/clippy/src/driver.rs
@@ -130,7 +130,7 @@ impl rustc_driver::Callbacks for ClippyCallbacks {
     #[allow(rustc::bad_opt_access)]
     fn config(&mut self, config: &mut interface::Config) {
         let conf_path = clippy_lints::lookup_conf_file();
-        let conf_path_string = if let Ok(Some(path)) = &conf_path {
+        let conf_path_string = if let Ok((Some(path), _)) = &conf_path {
             path.to_str().map(String::from)
         } else {
             None
diff --git a/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/src/main.stderr b/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/src/main.stderr
index 98697e001f9..aa1b3c638a0 100644
--- a/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/src/main.stderr
+++ b/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/src/main.stderr
@@ -1,2 +1,4 @@
-Using config file `$SRC_DIR/.clippy.toml`
-Warning: `$SRC_DIR/clippy.toml` will be ignored.
+warning: using config file `$SRC_DIR/.clippy.toml`, `$SRC_DIR/clippy.toml` will be ignored
+
+warning: 1 warning emitted
+
diff --git a/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr b/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr
index ee941762196..1be0cda12fc 100644
--- a/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr
+++ b/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr
@@ -11,29 +11,29 @@ LL -     println!("val='{}'", local_i32);
 LL +     println!("val='{local_i32}'");
    |
 
-error: literal with an empty format string
-  --> $DIR/uninlined_format_args.rs:10:35
+error: variables can be used directly in the `format!` string
+  --> $DIR/uninlined_format_args.rs:10:5
    |
 LL |     println!("Hello {} is {:.*}", "x", local_i32, local_f64);
-   |                                   ^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: `-D clippy::print-literal` implied by `-D warnings`
-help: try this
+help: change this to
    |
 LL -     println!("Hello {} is {:.*}", "x", local_i32, local_f64);
-LL +     println!("Hello x is {:.*}", local_i32, local_f64);
+LL +     println!("Hello {} is {local_f64:.local_i32$}", "x");
    |
 
-error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:10:5
+error: literal with an empty format string
+  --> $DIR/uninlined_format_args.rs:10:35
    |
 LL |     println!("Hello {} is {:.*}", "x", local_i32, local_f64);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                                   ^^^
    |
-help: change this to
+   = note: `-D clippy::print-literal` implied by `-D warnings`
+help: try this
    |
 LL -     println!("Hello {} is {:.*}", "x", local_i32, local_f64);
-LL +     println!("Hello {} is {local_f64:.local_i32$}", "x");
+LL +     println!("Hello x is {:.*}", local_i32, local_f64);
    |
 
 error: variables can be used directly in the `format!` string
diff --git a/src/tools/clippy/tests/ui-toml/extra_unused_type_parameters/clippy.toml b/src/tools/clippy/tests/ui-toml/extra_unused_type_parameters/clippy.toml
new file mode 100644
index 00000000000..5f304987aa9
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/extra_unused_type_parameters/clippy.toml
@@ -0,0 +1 @@
+avoid-breaking-exported-api = true
diff --git a/src/tools/clippy/tests/ui-toml/extra_unused_type_parameters/extra_unused_type_parameters.rs b/src/tools/clippy/tests/ui-toml/extra_unused_type_parameters/extra_unused_type_parameters.rs
new file mode 100644
index 00000000000..5655232455c
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/extra_unused_type_parameters/extra_unused_type_parameters.rs
@@ -0,0 +1,9 @@
+pub struct S;
+
+impl S {
+    pub fn exported_fn<T>() {
+        unimplemented!();
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/large_futures/clippy.toml b/src/tools/clippy/tests/ui-toml/large_futures/clippy.toml
new file mode 100644
index 00000000000..61bb17fdf6b
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/large_futures/clippy.toml
@@ -0,0 +1 @@
+future-size-threshold = 1024
diff --git a/src/tools/clippy/tests/ui-toml/large_futures/large_futures.rs b/src/tools/clippy/tests/ui-toml/large_futures/large_futures.rs
new file mode 100644
index 00000000000..4158df8b5ff
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/large_futures/large_futures.rs
@@ -0,0 +1,27 @@
+#![warn(clippy::large_futures)]
+
+fn main() {}
+
+pub async fn should_warn() {
+    let x = [0u8; 1024];
+    async {}.await;
+    dbg!(x);
+}
+
+pub async fn should_not_warn() {
+    let x = [0u8; 1020];
+    async {}.await;
+    dbg!(x);
+}
+
+pub async fn bar() {
+    should_warn().await;
+
+    async {
+        let x = [0u8; 1024];
+        dbg!(x);
+    }
+    .await;
+
+    should_not_warn().await;
+}
diff --git a/src/tools/clippy/tests/ui-toml/large_futures/large_futures.stderr b/src/tools/clippy/tests/ui-toml/large_futures/large_futures.stderr
new file mode 100644
index 00000000000..b92734de2f0
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/large_futures/large_futures.stderr
@@ -0,0 +1,10 @@
+error: large future with a size of 1026 bytes
+  --> $DIR/large_futures.rs:18:5
+   |
+LL |     should_warn().await;
+   |     ^^^^^^^^^^^^^ help: consider `Box::pin` on it: `Box::pin(should_warn())`
+   |
+   = note: `-D clippy::large-futures` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
index 6a246afac76..8447c31722d 100644
--- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
+++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
@@ -24,6 +24,7 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown fie
            enforced-import-renames
            enum-variant-name-threshold
            enum-variant-size-threshold
+           future-size-threshold
            ignore-interior-mutability
            large-error-threshold
            literal-representation-threshold
diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs
index ee7d2ba444b..3c06676d722 100644
--- a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs
+++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs
@@ -425,4 +425,8 @@ pub fn integer_arithmetic() {
     i ^= i;
 }
 
+pub fn issue_10583(a: u16) -> u16 {
+    10 / a
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr
index 3895f08964c..2c8ee2884e7 100644
--- a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr
+++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr
@@ -577,6 +577,12 @@ LL |     i * 2;
    |     ^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:394:5
+   |
+LL |     1 % i / 2;
+   |     ^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
   --> $DIR/arithmetic_side_effects.rs:395:5
    |
 LL |     i - 2 + 2 - i;
@@ -642,5 +648,11 @@ error: arithmetic operation that can potentially result in unexpected side-effec
 LL |     i %= var2;
    |     ^^^^^^^^^
 
-error: aborting due to 107 previous errors
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:429:5
+   |
+LL |     10 / a
+   |     ^^^^^^
+
+error: aborting due to 109 previous errors
 
diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs
index 325be83a0d7..3d5beab1eff 100644
--- a/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs
+++ b/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs
@@ -63,7 +63,7 @@ fn group_with_span(delimiter: Delimiter, stream: TokenStream, span: Span) -> Gro
 /// Token used to escape the following token from the macro's span rules.
 const ESCAPE_CHAR: char = '$';
 
-/// Takes a single token followed by a sequence tokens. Returns the sequence of tokens with their
+/// Takes a single token followed by a sequence of tokens. Returns the sequence of tokens with their
 /// span set to that of the first token. Tokens may be escaped with either `#ident` or `#(tokens)`.
 #[proc_macro]
 pub fn with_span(input: TokenStream) -> TokenStream {
diff --git a/src/tools/clippy/tests/ui/cast.rs b/src/tools/clippy/tests/ui/cast.rs
index 8b2673c2a7f..a86b85706a3 100644
--- a/src/tools/clippy/tests/ui/cast.rs
+++ b/src/tools/clippy/tests/ui/cast.rs
@@ -29,6 +29,12 @@ fn main() {
     1f64 as isize;
     1f64 as usize;
     1f32 as u32 as u16;
+    {
+        let _x: i8 = 1i32 as _;
+        1f32 as i32;
+        1f64 as i32;
+        1f32 as u8;
+    }
     // Test clippy::cast_possible_wrap
     1u8 as i8;
     1u16 as i16;
diff --git a/src/tools/clippy/tests/ui/cast.stderr b/src/tools/clippy/tests/ui/cast.stderr
index 451078de23b..65ecf1aa37a 100644
--- a/src/tools/clippy/tests/ui/cast.stderr
+++ b/src/tools/clippy/tests/ui/cast.stderr
@@ -44,10 +44,6 @@ LL |     1f32 as i32;
    |
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
    = note: `-D clippy::cast-possible-truncation` implied by `-D warnings`
-help: ... or use `try_from` and handle the error accordingly
-   |
-LL |     i32::try_from(1f32);
-   |     ~~~~~~~~~~~~~~~~~~~
 
 error: casting `f32` to `u32` may truncate the value
   --> $DIR/cast.rs:25:5
@@ -56,10 +52,6 @@ LL |     1f32 as u32;
    |     ^^^^^^^^^^^
    |
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
-help: ... or use `try_from` and handle the error accordingly
-   |
-LL |     u32::try_from(1f32);
-   |     ~~~~~~~~~~~~~~~~~~~
 
 error: casting `f32` to `u32` may lose the sign of the value
   --> $DIR/cast.rs:25:5
@@ -76,10 +68,6 @@ LL |     1f64 as f32;
    |     ^^^^^^^^^^^
    |
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
-help: ... or use `try_from` and handle the error accordingly
-   |
-LL |     f32::try_from(1f64);
-   |     ~~~~~~~~~~~~~~~~~~~
 
 error: casting `i32` to `i8` may truncate the value
   --> $DIR/cast.rs:27:5
@@ -112,10 +100,6 @@ LL |     1f64 as isize;
    |     ^^^^^^^^^^^^^
    |
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
-help: ... or use `try_from` and handle the error accordingly
-   |
-LL |     isize::try_from(1f64);
-   |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `f64` to `usize` may truncate the value
   --> $DIR/cast.rs:30:5
@@ -124,10 +108,6 @@ LL |     1f64 as usize;
    |     ^^^^^^^^^^^^^
    |
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
-help: ... or use `try_from` and handle the error accordingly
-   |
-LL |     usize::try_from(1f64);
-   |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `f64` to `usize` may lose the sign of the value
   --> $DIR/cast.rs:30:5
@@ -154,10 +134,6 @@ LL |     1f32 as u32 as u16;
    |     ^^^^^^^^^^^
    |
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
-help: ... or use `try_from` and handle the error accordingly
-   |
-LL |     u32::try_from(1f32) as u16;
-   |     ~~~~~~~~~~~~~~~~~~~
 
 error: casting `f32` to `u32` may lose the sign of the value
   --> $DIR/cast.rs:31:5
@@ -165,8 +141,50 @@ error: casting `f32` to `u32` may lose the sign of the value
 LL |     1f32 as u32 as u16;
    |     ^^^^^^^^^^^
 
+error: casting `i32` to `i8` may truncate the value
+  --> $DIR/cast.rs:33:22
+   |
+LL |         let _x: i8 = 1i32 as _;
+   |                      ^^^^^^^^^
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
+help: ... or use `try_from` and handle the error accordingly
+   |
+LL |         let _x: i8 = 1i32.try_into();
+   |                      ~~~~~~~~~~~~~~~
+
+error: casting `f32` to `i32` may truncate the value
+  --> $DIR/cast.rs:34:9
+   |
+LL |         1f32 as i32;
+   |         ^^^^^^^^^^^
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
+
+error: casting `f64` to `i32` may truncate the value
+  --> $DIR/cast.rs:35:9
+   |
+LL |         1f64 as i32;
+   |         ^^^^^^^^^^^
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
+
+error: casting `f32` to `u8` may truncate the value
+  --> $DIR/cast.rs:36:9
+   |
+LL |         1f32 as u8;
+   |         ^^^^^^^^^^
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
+
+error: casting `f32` to `u8` may lose the sign of the value
+  --> $DIR/cast.rs:36:9
+   |
+LL |         1f32 as u8;
+   |         ^^^^^^^^^^
+
 error: casting `u8` to `i8` may wrap around the value
-  --> $DIR/cast.rs:33:5
+  --> $DIR/cast.rs:39:5
    |
 LL |     1u8 as i8;
    |     ^^^^^^^^^
@@ -174,43 +192,43 @@ LL |     1u8 as i8;
    = note: `-D clippy::cast-possible-wrap` implied by `-D warnings`
 
 error: casting `u16` to `i16` may wrap around the value
-  --> $DIR/cast.rs:34:5
+  --> $DIR/cast.rs:40:5
    |
 LL |     1u16 as i16;
    |     ^^^^^^^^^^^
 
 error: casting `u32` to `i32` may wrap around the value
-  --> $DIR/cast.rs:35:5
+  --> $DIR/cast.rs:41:5
    |
 LL |     1u32 as i32;
    |     ^^^^^^^^^^^
 
 error: casting `u64` to `i64` may wrap around the value
-  --> $DIR/cast.rs:36:5
+  --> $DIR/cast.rs:42:5
    |
 LL |     1u64 as i64;
    |     ^^^^^^^^^^^
 
 error: casting `usize` to `isize` may wrap around the value
-  --> $DIR/cast.rs:37:5
+  --> $DIR/cast.rs:43:5
    |
 LL |     1usize as isize;
    |     ^^^^^^^^^^^^^^^
 
 error: casting `i32` to `u32` may lose the sign of the value
-  --> $DIR/cast.rs:40:5
+  --> $DIR/cast.rs:46:5
    |
 LL |     -1i32 as u32;
    |     ^^^^^^^^^^^^
 
 error: casting `isize` to `usize` may lose the sign of the value
-  --> $DIR/cast.rs:42:5
+  --> $DIR/cast.rs:48:5
    |
 LL |     -1isize as usize;
    |     ^^^^^^^^^^^^^^^^
 
 error: casting `i64` to `i8` may truncate the value
-  --> $DIR/cast.rs:109:5
+  --> $DIR/cast.rs:115:5
    |
 LL |     (-99999999999i64).min(1) as i8; // should be linted because signed
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -222,7 +240,7 @@ LL |     i8::try_from((-99999999999i64).min(1)); // should be linted because sig
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `u64` to `u8` may truncate the value
-  --> $DIR/cast.rs:121:5
+  --> $DIR/cast.rs:127:5
    |
 LL |     999999u64.clamp(0, 256) as u8; // should still be linted
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -234,7 +252,7 @@ LL |     u8::try_from(999999u64.clamp(0, 256)); // should still be linted
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `main::E2` to `u8` may truncate the value
-  --> $DIR/cast.rs:142:21
+  --> $DIR/cast.rs:148:21
    |
 LL |             let _ = self as u8;
    |                     ^^^^^^^^^^
@@ -246,7 +264,7 @@ LL |             let _ = u8::try_from(self);
    |                     ~~~~~~~~~~~~~~~~~~
 
 error: casting `main::E2::B` to `u8` will truncate the value
-  --> $DIR/cast.rs:143:21
+  --> $DIR/cast.rs:149:21
    |
 LL |             let _ = Self::B as u8;
    |                     ^^^^^^^^^^^^^
@@ -254,7 +272,7 @@ LL |             let _ = Self::B as u8;
    = note: `-D clippy::cast-enum-truncation` implied by `-D warnings`
 
 error: casting `main::E5` to `i8` may truncate the value
-  --> $DIR/cast.rs:179:21
+  --> $DIR/cast.rs:185:21
    |
 LL |             let _ = self as i8;
    |                     ^^^^^^^^^^
@@ -266,13 +284,13 @@ LL |             let _ = i8::try_from(self);
    |                     ~~~~~~~~~~~~~~~~~~
 
 error: casting `main::E5::A` to `i8` will truncate the value
-  --> $DIR/cast.rs:180:21
+  --> $DIR/cast.rs:186:21
    |
 LL |             let _ = Self::A as i8;
    |                     ^^^^^^^^^^^^^
 
 error: casting `main::E6` to `i16` may truncate the value
-  --> $DIR/cast.rs:194:21
+  --> $DIR/cast.rs:200:21
    |
 LL |             let _ = self as i16;
    |                     ^^^^^^^^^^^
@@ -284,7 +302,7 @@ LL |             let _ = i16::try_from(self);
    |                     ~~~~~~~~~~~~~~~~~~~
 
 error: casting `main::E7` to `usize` may truncate the value on targets with 32-bit wide pointers
-  --> $DIR/cast.rs:209:21
+  --> $DIR/cast.rs:215:21
    |
 LL |             let _ = self as usize;
    |                     ^^^^^^^^^^^^^
@@ -296,7 +314,7 @@ LL |             let _ = usize::try_from(self);
    |                     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `main::E10` to `u16` may truncate the value
-  --> $DIR/cast.rs:250:21
+  --> $DIR/cast.rs:256:21
    |
 LL |             let _ = self as u16;
    |                     ^^^^^^^^^^^
@@ -308,7 +326,7 @@ LL |             let _ = u16::try_from(self);
    |                     ~~~~~~~~~~~~~~~~~~~
 
 error: casting `u32` to `u8` may truncate the value
-  --> $DIR/cast.rs:258:13
+  --> $DIR/cast.rs:264:13
    |
 LL |     let c = (q >> 16) as u8;
    |             ^^^^^^^^^^^^^^^
@@ -316,11 +334,11 @@ LL |     let c = (q >> 16) as u8;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |     let c = u8::try_from((q >> 16));
-   |             ~~~~~~~~~~~~~~~~~~~~~~~
+LL |     let c = u8::try_from(q >> 16);
+   |             ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `u32` to `u8` may truncate the value
-  --> $DIR/cast.rs:261:13
+  --> $DIR/cast.rs:267:13
    |
 LL |     let c = (q / 1000) as u8;
    |             ^^^^^^^^^^^^^^^^
@@ -328,8 +346,8 @@ LL |     let c = (q / 1000) as u8;
    = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
 help: ... or use `try_from` and handle the error accordingly
    |
-LL |     let c = u8::try_from((q / 1000));
-   |             ~~~~~~~~~~~~~~~~~~~~~~~~
+LL |     let c = u8::try_from(q / 1000);
+   |             ~~~~~~~~~~~~~~~~~~~~~~
 
-error: aborting due to 36 previous errors
+error: aborting due to 41 previous errors
 
diff --git a/src/tools/clippy/tests/ui/clear_with_drain.fixed b/src/tools/clippy/tests/ui/clear_with_drain.fixed
new file mode 100644
index 00000000000..2d9545eeed1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/clear_with_drain.fixed
@@ -0,0 +1,358 @@
+// run-rustfix
+#![allow(unused)]
+#![warn(clippy::clear_with_drain)]
+
+use std::collections::{BinaryHeap, HashMap, HashSet, VecDeque};
+
+fn vec_range() {
+    // Do not lint because iterator is assigned
+    let mut v = vec![1, 2, 3];
+    let iter = v.drain(0..v.len());
+
+    // Do not lint because iterator is used
+    let mut v = vec![1, 2, 3];
+    let n = v.drain(0..v.len()).count();
+
+    // Do not lint because iterator is assigned and used
+    let mut v = vec![1, 2, 3];
+    let iter = v.drain(usize::MIN..v.len());
+    let n = iter.count();
+
+    // Do lint
+    let mut v = vec![1, 2, 3];
+    v.clear();
+
+    // Do lint
+    let mut v = vec![1, 2, 3];
+    v.clear();
+}
+
+fn vec_range_from() {
+    // Do not lint because iterator is assigned
+    let mut v = vec![1, 2, 3];
+    let iter = v.drain(0..);
+
+    // Do not lint because iterator is assigned and used
+    let mut v = vec![1, 2, 3];
+    let mut iter = v.drain(0..);
+    let next = iter.next();
+
+    // Do not lint because iterator is used
+    let mut v = vec![1, 2, 3];
+    let next = v.drain(usize::MIN..).next();
+
+    // Do lint
+    let mut v = vec![1, 2, 3];
+    v.clear();
+
+    // Do lint
+    let mut v = vec![1, 2, 3];
+    v.clear();
+}
+
+fn vec_range_full() {
+    // Do not lint because iterator is assigned
+    let mut v = vec![1, 2, 3];
+    let iter = v.drain(..);
+
+    // Do not lint because iterator is used
+    let mut v = vec![1, 2, 3];
+    for x in v.drain(..) {
+        let y = format!("x = {x}");
+    }
+
+    // Do lint
+    let mut v = vec![1, 2, 3];
+    v.clear();
+}
+
+fn vec_range_to() {
+    // Do not lint because iterator is assigned
+    let mut v = vec![1, 2, 3];
+    let iter = v.drain(..v.len());
+
+    // Do not lint because iterator is assigned and used
+    let mut v = vec![1, 2, 3];
+    let iter = v.drain(..v.len());
+    for x in iter {
+        let y = format!("x = {x}");
+    }
+
+    // Do lint
+    let mut v = vec![1, 2, 3];
+    v.clear();
+}
+
+fn vec_partial_drains() {
+    // Do not lint any of these because the ranges are not full
+
+    let mut v = vec![1, 2, 3];
+    v.drain(1..);
+    let mut v = vec![1, 2, 3];
+    v.drain(1..).max();
+
+    let mut v = vec![1, 2, 3];
+    v.drain(..v.len() - 1);
+    let mut v = vec![1, 2, 3];
+    v.drain(..v.len() - 1).min();
+
+    let mut v = vec![1, 2, 3];
+    v.drain(1..v.len() - 1);
+    let mut v = vec![1, 2, 3];
+    let w: Vec<i8> = v.drain(1..v.len() - 1).collect();
+}
+
+fn vec_deque_range() {
+    // Do not lint because iterator is assigned
+    let mut deque = VecDeque::from([1, 2, 3]);
+    let iter = deque.drain(0..deque.len());
+
+    // Do not lint because iterator is used
+    let mut deque = VecDeque::from([1, 2, 3]);
+    let n = deque.drain(0..deque.len()).count();
+
+    // Do not lint because iterator is assigned and used
+    let mut deque = VecDeque::from([1, 2, 3]);
+    let iter = deque.drain(usize::MIN..deque.len());
+    let n = iter.count();
+
+    // Do lint
+    let mut deque = VecDeque::from([1, 2, 3]);
+    deque.clear();
+
+    // Do lint
+    let mut deque = VecDeque::from([1, 2, 3]);
+    deque.clear();
+}
+
+fn vec_deque_range_from() {
+    // Do not lint because iterator is assigned
+    let mut deque = VecDeque::from([1, 2, 3]);
+    let iter = deque.drain(0..);
+
+    // Do not lint because iterator is assigned and used
+    let mut deque = VecDeque::from([1, 2, 3]);
+    let mut iter = deque.drain(0..);
+    let next = iter.next();
+
+    // Do not lint because iterator is used
+    let mut deque = VecDeque::from([1, 2, 3]);
+    let next = deque.drain(usize::MIN..).next();
+
+    // Do lint
+    let mut deque = VecDeque::from([1, 2, 3]);
+    deque.clear();
+
+    // Do lint
+    let mut deque = VecDeque::from([1, 2, 3]);
+    deque.clear();
+}
+
+fn vec_deque_range_full() {
+    // Do not lint because iterator is assigned
+    let mut deque = VecDeque::from([1, 2, 3]);
+    let iter = deque.drain(..);
+
+    // Do not lint because iterator is used
+    let mut deque = VecDeque::from([1, 2, 3]);
+    for x in deque.drain(..) {
+        let y = format!("x = {x}");
+    }
+
+    // Do lint
+    let mut deque = VecDeque::from([1, 2, 3]);
+    deque.clear();
+}
+
+fn vec_deque_range_to() {
+    // Do not lint because iterator is assigned
+    let mut deque = VecDeque::from([1, 2, 3]);
+    let iter = deque.drain(..deque.len());
+
+    // Do not lint because iterator is assigned and used
+    let mut deque = VecDeque::from([1, 2, 3]);
+    let iter = deque.drain(..deque.len());
+    for x in iter {
+        let y = format!("x = {x}");
+    }
+
+    // Do lint
+    let mut deque = VecDeque::from([1, 2, 3]);
+    deque.clear();
+}
+
+fn vec_deque_partial_drains() {
+    // Do not lint any of these because the ranges are not full
+
+    let mut deque = VecDeque::from([1, 2, 3]);
+    deque.drain(1..);
+    let mut deque = VecDeque::from([1, 2, 3]);
+    deque.drain(1..).max();
+
+    let mut deque = VecDeque::from([1, 2, 3]);
+    deque.drain(..deque.len() - 1);
+    let mut deque = VecDeque::from([1, 2, 3]);
+    deque.drain(..deque.len() - 1).min();
+
+    let mut deque = VecDeque::from([1, 2, 3]);
+    deque.drain(1..deque.len() - 1);
+    let mut deque = VecDeque::from([1, 2, 3]);
+    let w: Vec<i8> = deque.drain(1..deque.len() - 1).collect();
+}
+
+fn string_range() {
+    // Do not lint because iterator is assigned
+    let mut s = String::from("Hello, world!");
+    let iter = s.drain(0..s.len());
+
+    // Do not lint because iterator is used
+    let mut s = String::from("Hello, world!");
+    let n = s.drain(0..s.len()).count();
+
+    // Do not lint because iterator is assigned and used
+    let mut s = String::from("Hello, world!");
+    let iter = s.drain(usize::MIN..s.len());
+    let n = iter.count();
+
+    // Do lint
+    let mut s = String::from("Hello, world!");
+    s.clear();
+
+    // Do lint
+    let mut s = String::from("Hello, world!");
+    s.clear();
+}
+
+fn string_range_from() {
+    // Do not lint because iterator is assigned
+    let mut s = String::from("Hello, world!");
+    let iter = s.drain(0..);
+
+    // Do not lint because iterator is assigned and used
+    let mut s = String::from("Hello, world!");
+    let mut iter = s.drain(0..);
+    let next = iter.next();
+
+    // Do not lint because iterator is used
+    let mut s = String::from("Hello, world!");
+    let next = s.drain(usize::MIN..).next();
+
+    // Do lint
+    let mut s = String::from("Hello, world!");
+    s.clear();
+
+    // Do lint
+    let mut s = String::from("Hello, world!");
+    s.clear();
+}
+
+fn string_range_full() {
+    // Do not lint because iterator is assigned
+    let mut s = String::from("Hello, world!");
+    let iter = s.drain(..);
+
+    // Do not lint because iterator is used
+    let mut s = String::from("Hello, world!");
+    for x in s.drain(..) {
+        let y = format!("x = {x}");
+    }
+
+    // Do lint
+    let mut s = String::from("Hello, world!");
+    s.clear();
+}
+
+fn string_range_to() {
+    // Do not lint because iterator is assigned
+    let mut s = String::from("Hello, world!");
+    let iter = s.drain(..s.len());
+
+    // Do not lint because iterator is assigned and used
+    let mut s = String::from("Hello, world!");
+    let iter = s.drain(..s.len());
+    for x in iter {
+        let y = format!("x = {x}");
+    }
+
+    // Do lint
+    let mut s = String::from("Hello, world!");
+    s.clear();
+}
+
+fn string_partial_drains() {
+    // Do not lint any of these because the ranges are not full
+
+    let mut s = String::from("Hello, world!");
+    s.drain(1..);
+    let mut s = String::from("Hello, world!");
+    s.drain(1..).max();
+
+    let mut s = String::from("Hello, world!");
+    s.drain(..s.len() - 1);
+    let mut s = String::from("Hello, world!");
+    s.drain(..s.len() - 1).min();
+
+    let mut s = String::from("Hello, world!");
+    s.drain(1..s.len() - 1);
+    let mut s = String::from("Hello, world!");
+    let w: String = s.drain(1..s.len() - 1).collect();
+}
+
+fn hash_set() {
+    // Do not lint because iterator is assigned
+    let mut set = HashSet::from([1, 2, 3]);
+    let iter = set.drain();
+
+    // Do not lint because iterator is assigned and used
+    let mut set = HashSet::from([1, 2, 3]);
+    let mut iter = set.drain();
+    let next = iter.next();
+
+    // Do not lint because iterator is used
+    let mut set = HashSet::from([1, 2, 3]);
+    let next = set.drain().next();
+
+    // Do lint
+    let mut set = HashSet::from([1, 2, 3]);
+    set.clear();
+}
+
+fn hash_map() {
+    // Do not lint because iterator is assigned
+    let mut map = HashMap::from([(1, "a"), (2, "b")]);
+    let iter = map.drain();
+
+    // Do not lint because iterator is assigned and used
+    let mut map = HashMap::from([(1, "a"), (2, "b")]);
+    let mut iter = map.drain();
+    let next = iter.next();
+
+    // Do not lint because iterator is used
+    let mut map = HashMap::from([(1, "a"), (2, "b")]);
+    let next = map.drain().next();
+
+    // Do lint
+    let mut map = HashMap::from([(1, "a"), (2, "b")]);
+    map.clear();
+}
+
+fn binary_heap() {
+    // Do not lint because iterator is assigned
+    let mut heap = BinaryHeap::from([1, 2]);
+    let iter = heap.drain();
+
+    // Do not lint because iterator is assigned and used
+    let mut heap = BinaryHeap::from([1, 2]);
+    let mut iter = heap.drain();
+    let next = iter.next();
+
+    // Do not lint because iterator is used
+    let mut heap = BinaryHeap::from([1, 2]);
+    let next = heap.drain().next();
+
+    // Do lint
+    let mut heap = BinaryHeap::from([1, 2]);
+    heap.clear();
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/clear_with_drain.rs b/src/tools/clippy/tests/ui/clear_with_drain.rs
new file mode 100644
index 00000000000..4d60ee46e18
--- /dev/null
+++ b/src/tools/clippy/tests/ui/clear_with_drain.rs
@@ -0,0 +1,358 @@
+// run-rustfix
+#![allow(unused)]
+#![warn(clippy::clear_with_drain)]
+
+use std::collections::{BinaryHeap, HashMap, HashSet, VecDeque};
+
+fn vec_range() {
+    // Do not lint because iterator is assigned
+    let mut v = vec![1, 2, 3];
+    let iter = v.drain(0..v.len());
+
+    // Do not lint because iterator is used
+    let mut v = vec![1, 2, 3];
+    let n = v.drain(0..v.len()).count();
+
+    // Do not lint because iterator is assigned and used
+    let mut v = vec![1, 2, 3];
+    let iter = v.drain(usize::MIN..v.len());
+    let n = iter.count();
+
+    // Do lint
+    let mut v = vec![1, 2, 3];
+    v.drain(0..v.len());
+
+    // Do lint
+    let mut v = vec![1, 2, 3];
+    v.drain(usize::MIN..v.len());
+}
+
+fn vec_range_from() {
+    // Do not lint because iterator is assigned
+    let mut v = vec![1, 2, 3];
+    let iter = v.drain(0..);
+
+    // Do not lint because iterator is assigned and used
+    let mut v = vec![1, 2, 3];
+    let mut iter = v.drain(0..);
+    let next = iter.next();
+
+    // Do not lint because iterator is used
+    let mut v = vec![1, 2, 3];
+    let next = v.drain(usize::MIN..).next();
+
+    // Do lint
+    let mut v = vec![1, 2, 3];
+    v.drain(0..);
+
+    // Do lint
+    let mut v = vec![1, 2, 3];
+    v.drain(usize::MIN..);
+}
+
+fn vec_range_full() {
+    // Do not lint because iterator is assigned
+    let mut v = vec![1, 2, 3];
+    let iter = v.drain(..);
+
+    // Do not lint because iterator is used
+    let mut v = vec![1, 2, 3];
+    for x in v.drain(..) {
+        let y = format!("x = {x}");
+    }
+
+    // Do lint
+    let mut v = vec![1, 2, 3];
+    v.drain(..);
+}
+
+fn vec_range_to() {
+    // Do not lint because iterator is assigned
+    let mut v = vec![1, 2, 3];
+    let iter = v.drain(..v.len());
+
+    // Do not lint because iterator is assigned and used
+    let mut v = vec![1, 2, 3];
+    let iter = v.drain(..v.len());
+    for x in iter {
+        let y = format!("x = {x}");
+    }
+
+    // Do lint
+    let mut v = vec![1, 2, 3];
+    v.drain(..v.len());
+}
+
+fn vec_partial_drains() {
+    // Do not lint any of these because the ranges are not full
+
+    let mut v = vec![1, 2, 3];
+    v.drain(1..);
+    let mut v = vec![1, 2, 3];
+    v.drain(1..).max();
+
+    let mut v = vec![1, 2, 3];
+    v.drain(..v.len() - 1);
+    let mut v = vec![1, 2, 3];
+    v.drain(..v.len() - 1).min();
+
+    let mut v = vec![1, 2, 3];
+    v.drain(1..v.len() - 1);
+    let mut v = vec![1, 2, 3];
+    let w: Vec<i8> = v.drain(1..v.len() - 1).collect();
+}
+
+fn vec_deque_range() {
+    // Do not lint because iterator is assigned
+    let mut deque = VecDeque::from([1, 2, 3]);
+    let iter = deque.drain(0..deque.len());
+
+    // Do not lint because iterator is used
+    let mut deque = VecDeque::from([1, 2, 3]);
+    let n = deque.drain(0..deque.len()).count();
+
+    // Do not lint because iterator is assigned and used
+    let mut deque = VecDeque::from([1, 2, 3]);
+    let iter = deque.drain(usize::MIN..deque.len());
+    let n = iter.count();
+
+    // Do lint
+    let mut deque = VecDeque::from([1, 2, 3]);
+    deque.drain(0..deque.len());
+
+    // Do lint
+    let mut deque = VecDeque::from([1, 2, 3]);
+    deque.drain(usize::MIN..deque.len());
+}
+
+fn vec_deque_range_from() {
+    // Do not lint because iterator is assigned
+    let mut deque = VecDeque::from([1, 2, 3]);
+    let iter = deque.drain(0..);
+
+    // Do not lint because iterator is assigned and used
+    let mut deque = VecDeque::from([1, 2, 3]);
+    let mut iter = deque.drain(0..);
+    let next = iter.next();
+
+    // Do not lint because iterator is used
+    let mut deque = VecDeque::from([1, 2, 3]);
+    let next = deque.drain(usize::MIN..).next();
+
+    // Do lint
+    let mut deque = VecDeque::from([1, 2, 3]);
+    deque.drain(0..);
+
+    // Do lint
+    let mut deque = VecDeque::from([1, 2, 3]);
+    deque.drain(usize::MIN..);
+}
+
+fn vec_deque_range_full() {
+    // Do not lint because iterator is assigned
+    let mut deque = VecDeque::from([1, 2, 3]);
+    let iter = deque.drain(..);
+
+    // Do not lint because iterator is used
+    let mut deque = VecDeque::from([1, 2, 3]);
+    for x in deque.drain(..) {
+        let y = format!("x = {x}");
+    }
+
+    // Do lint
+    let mut deque = VecDeque::from([1, 2, 3]);
+    deque.drain(..);
+}
+
+fn vec_deque_range_to() {
+    // Do not lint because iterator is assigned
+    let mut deque = VecDeque::from([1, 2, 3]);
+    let iter = deque.drain(..deque.len());
+
+    // Do not lint because iterator is assigned and used
+    let mut deque = VecDeque::from([1, 2, 3]);
+    let iter = deque.drain(..deque.len());
+    for x in iter {
+        let y = format!("x = {x}");
+    }
+
+    // Do lint
+    let mut deque = VecDeque::from([1, 2, 3]);
+    deque.drain(..deque.len());
+}
+
+fn vec_deque_partial_drains() {
+    // Do not lint any of these because the ranges are not full
+
+    let mut deque = VecDeque::from([1, 2, 3]);
+    deque.drain(1..);
+    let mut deque = VecDeque::from([1, 2, 3]);
+    deque.drain(1..).max();
+
+    let mut deque = VecDeque::from([1, 2, 3]);
+    deque.drain(..deque.len() - 1);
+    let mut deque = VecDeque::from([1, 2, 3]);
+    deque.drain(..deque.len() - 1).min();
+
+    let mut deque = VecDeque::from([1, 2, 3]);
+    deque.drain(1..deque.len() - 1);
+    let mut deque = VecDeque::from([1, 2, 3]);
+    let w: Vec<i8> = deque.drain(1..deque.len() - 1).collect();
+}
+
+fn string_range() {
+    // Do not lint because iterator is assigned
+    let mut s = String::from("Hello, world!");
+    let iter = s.drain(0..s.len());
+
+    // Do not lint because iterator is used
+    let mut s = String::from("Hello, world!");
+    let n = s.drain(0..s.len()).count();
+
+    // Do not lint because iterator is assigned and used
+    let mut s = String::from("Hello, world!");
+    let iter = s.drain(usize::MIN..s.len());
+    let n = iter.count();
+
+    // Do lint
+    let mut s = String::from("Hello, world!");
+    s.drain(0..s.len());
+
+    // Do lint
+    let mut s = String::from("Hello, world!");
+    s.drain(usize::MIN..s.len());
+}
+
+fn string_range_from() {
+    // Do not lint because iterator is assigned
+    let mut s = String::from("Hello, world!");
+    let iter = s.drain(0..);
+
+    // Do not lint because iterator is assigned and used
+    let mut s = String::from("Hello, world!");
+    let mut iter = s.drain(0..);
+    let next = iter.next();
+
+    // Do not lint because iterator is used
+    let mut s = String::from("Hello, world!");
+    let next = s.drain(usize::MIN..).next();
+
+    // Do lint
+    let mut s = String::from("Hello, world!");
+    s.drain(0..);
+
+    // Do lint
+    let mut s = String::from("Hello, world!");
+    s.drain(usize::MIN..);
+}
+
+fn string_range_full() {
+    // Do not lint because iterator is assigned
+    let mut s = String::from("Hello, world!");
+    let iter = s.drain(..);
+
+    // Do not lint because iterator is used
+    let mut s = String::from("Hello, world!");
+    for x in s.drain(..) {
+        let y = format!("x = {x}");
+    }
+
+    // Do lint
+    let mut s = String::from("Hello, world!");
+    s.drain(..);
+}
+
+fn string_range_to() {
+    // Do not lint because iterator is assigned
+    let mut s = String::from("Hello, world!");
+    let iter = s.drain(..s.len());
+
+    // Do not lint because iterator is assigned and used
+    let mut s = String::from("Hello, world!");
+    let iter = s.drain(..s.len());
+    for x in iter {
+        let y = format!("x = {x}");
+    }
+
+    // Do lint
+    let mut s = String::from("Hello, world!");
+    s.drain(..s.len());
+}
+
+fn string_partial_drains() {
+    // Do not lint any of these because the ranges are not full
+
+    let mut s = String::from("Hello, world!");
+    s.drain(1..);
+    let mut s = String::from("Hello, world!");
+    s.drain(1..).max();
+
+    let mut s = String::from("Hello, world!");
+    s.drain(..s.len() - 1);
+    let mut s = String::from("Hello, world!");
+    s.drain(..s.len() - 1).min();
+
+    let mut s = String::from("Hello, world!");
+    s.drain(1..s.len() - 1);
+    let mut s = String::from("Hello, world!");
+    let w: String = s.drain(1..s.len() - 1).collect();
+}
+
+fn hash_set() {
+    // Do not lint because iterator is assigned
+    let mut set = HashSet::from([1, 2, 3]);
+    let iter = set.drain();
+
+    // Do not lint because iterator is assigned and used
+    let mut set = HashSet::from([1, 2, 3]);
+    let mut iter = set.drain();
+    let next = iter.next();
+
+    // Do not lint because iterator is used
+    let mut set = HashSet::from([1, 2, 3]);
+    let next = set.drain().next();
+
+    // Do lint
+    let mut set = HashSet::from([1, 2, 3]);
+    set.drain();
+}
+
+fn hash_map() {
+    // Do not lint because iterator is assigned
+    let mut map = HashMap::from([(1, "a"), (2, "b")]);
+    let iter = map.drain();
+
+    // Do not lint because iterator is assigned and used
+    let mut map = HashMap::from([(1, "a"), (2, "b")]);
+    let mut iter = map.drain();
+    let next = iter.next();
+
+    // Do not lint because iterator is used
+    let mut map = HashMap::from([(1, "a"), (2, "b")]);
+    let next = map.drain().next();
+
+    // Do lint
+    let mut map = HashMap::from([(1, "a"), (2, "b")]);
+    map.drain();
+}
+
+fn binary_heap() {
+    // Do not lint because iterator is assigned
+    let mut heap = BinaryHeap::from([1, 2]);
+    let iter = heap.drain();
+
+    // Do not lint because iterator is assigned and used
+    let mut heap = BinaryHeap::from([1, 2]);
+    let mut iter = heap.drain();
+    let next = iter.next();
+
+    // Do not lint because iterator is used
+    let mut heap = BinaryHeap::from([1, 2]);
+    let next = heap.drain().next();
+
+    // Do lint
+    let mut heap = BinaryHeap::from([1, 2]);
+    heap.drain();
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/clear_with_drain.stderr b/src/tools/clippy/tests/ui/clear_with_drain.stderr
new file mode 100644
index 00000000000..20158da1121
--- /dev/null
+++ b/src/tools/clippy/tests/ui/clear_with_drain.stderr
@@ -0,0 +1,130 @@
+error: `drain` used to clear a `Vec`
+  --> $DIR/clear_with_drain.rs:23:7
+   |
+LL |     v.drain(0..v.len());
+   |       ^^^^^^^^^^^^^^^^^ help: try: `clear()`
+   |
+   = note: `-D clippy::clear-with-drain` implied by `-D warnings`
+
+error: `drain` used to clear a `Vec`
+  --> $DIR/clear_with_drain.rs:27:7
+   |
+LL |     v.drain(usize::MIN..v.len());
+   |       ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `Vec`
+  --> $DIR/clear_with_drain.rs:46:7
+   |
+LL |     v.drain(0..);
+   |       ^^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `Vec`
+  --> $DIR/clear_with_drain.rs:50:7
+   |
+LL |     v.drain(usize::MIN..);
+   |       ^^^^^^^^^^^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `Vec`
+  --> $DIR/clear_with_drain.rs:66:7
+   |
+LL |     v.drain(..);
+   |       ^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `Vec`
+  --> $DIR/clear_with_drain.rs:83:7
+   |
+LL |     v.drain(..v.len());
+   |       ^^^^^^^^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `VecDeque`
+  --> $DIR/clear_with_drain.rs:121:11
+   |
+LL |     deque.drain(0..deque.len());
+   |           ^^^^^^^^^^^^^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `VecDeque`
+  --> $DIR/clear_with_drain.rs:125:11
+   |
+LL |     deque.drain(usize::MIN..deque.len());
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `VecDeque`
+  --> $DIR/clear_with_drain.rs:144:11
+   |
+LL |     deque.drain(0..);
+   |           ^^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `VecDeque`
+  --> $DIR/clear_with_drain.rs:148:11
+   |
+LL |     deque.drain(usize::MIN..);
+   |           ^^^^^^^^^^^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `VecDeque`
+  --> $DIR/clear_with_drain.rs:164:11
+   |
+LL |     deque.drain(..);
+   |           ^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `VecDeque`
+  --> $DIR/clear_with_drain.rs:181:11
+   |
+LL |     deque.drain(..deque.len());
+   |           ^^^^^^^^^^^^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `String`
+  --> $DIR/clear_with_drain.rs:219:7
+   |
+LL |     s.drain(0..s.len());
+   |       ^^^^^^^^^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `String`
+  --> $DIR/clear_with_drain.rs:223:7
+   |
+LL |     s.drain(usize::MIN..s.len());
+   |       ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `String`
+  --> $DIR/clear_with_drain.rs:242:7
+   |
+LL |     s.drain(0..);
+   |       ^^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `String`
+  --> $DIR/clear_with_drain.rs:246:7
+   |
+LL |     s.drain(usize::MIN..);
+   |       ^^^^^^^^^^^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `String`
+  --> $DIR/clear_with_drain.rs:262:7
+   |
+LL |     s.drain(..);
+   |       ^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `String`
+  --> $DIR/clear_with_drain.rs:279:7
+   |
+LL |     s.drain(..s.len());
+   |       ^^^^^^^^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `HashSet`
+  --> $DIR/clear_with_drain.rs:317:9
+   |
+LL |     set.drain();
+   |         ^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `HashMap`
+  --> $DIR/clear_with_drain.rs:336:9
+   |
+LL |     map.drain();
+   |         ^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `BinaryHeap`
+  --> $DIR/clear_with_drain.rs:355:10
+   |
+LL |     heap.drain();
+   |          ^^^^^^^ help: try: `clear()`
+
+error: aborting due to 21 previous errors
+
diff --git a/src/tools/clippy/tests/ui/collection_is_never_read.rs b/src/tools/clippy/tests/ui/collection_is_never_read.rs
index 068a49486cf..01259a983ab 100644
--- a/src/tools/clippy/tests/ui/collection_is_never_read.rs
+++ b/src/tools/clippy/tests/ui/collection_is_never_read.rs
@@ -84,13 +84,18 @@ fn shadowing_2() {
 }
 
 #[allow(clippy::let_unit_value)]
-fn fake_read() {
-    let mut x = vec![1, 2, 3]; // Ok
+fn fake_read_1() {
+    let mut x = vec![1, 2, 3]; // WARNING
     x.reverse();
-    // `collection_is_never_read` gets fooled, but other lints should catch this.
     let _: () = x.clear();
 }
 
+fn fake_read_2() {
+    let mut x = vec![1, 2, 3]; // WARNING
+    x.reverse();
+    println!("{:?}", x.push(5));
+}
+
 fn assignment() {
     let mut x = vec![1, 2, 3]; // WARNING
     let y = vec![4, 5, 6]; // Ok
@@ -163,3 +168,23 @@ fn function_argument() {
     let x = vec![1, 2, 3]; // Ok
     foo(&x);
 }
+
+fn string() {
+    // Do lint (write without read)
+    let mut s = String::new();
+    s.push_str("Hello, World!");
+
+    // Do not lint (read without write)
+    let mut s = String::from("Hello, World!");
+    let _ = s.len();
+
+    // Do not lint (write and read)
+    let mut s = String::from("Hello, World!");
+    s.push_str("foo, bar");
+    let _ = s.len();
+
+    // Do lint the first line, but not the second
+    let mut s = String::from("Hello, World!");
+    let t = String::from("foo, bar");
+    s = t;
+}
diff --git a/src/tools/clippy/tests/ui/collection_is_never_read.stderr b/src/tools/clippy/tests/ui/collection_is_never_read.stderr
index 7654b74be3d..cf51a53686f 100644
--- a/src/tools/clippy/tests/ui/collection_is_never_read.stderr
+++ b/src/tools/clippy/tests/ui/collection_is_never_read.stderr
@@ -25,28 +25,52 @@ LL |     let mut x = HashMap::new(); // WARNING
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: collection is never read
-  --> $DIR/collection_is_never_read.rs:95:5
+  --> $DIR/collection_is_never_read.rs:88:5
    |
 LL |     let mut x = vec![1, 2, 3]; // WARNING
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: collection is never read
-  --> $DIR/collection_is_never_read.rs:102:5
+  --> $DIR/collection_is_never_read.rs:94:5
    |
 LL |     let mut x = vec![1, 2, 3]; // WARNING
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: collection is never read
-  --> $DIR/collection_is_never_read.rs:119:5
+  --> $DIR/collection_is_never_read.rs:100:5
+   |
+LL |     let mut x = vec![1, 2, 3]; // WARNING
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+  --> $DIR/collection_is_never_read.rs:107:5
+   |
+LL |     let mut x = vec![1, 2, 3]; // WARNING
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+  --> $DIR/collection_is_never_read.rs:124:5
    |
 LL |     let mut x = HashSet::new(); // WARNING
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: collection is never read
-  --> $DIR/collection_is_never_read.rs:133:5
+  --> $DIR/collection_is_never_read.rs:138:5
    |
 LL |     let x = vec![1, 2, 3]; // WARNING
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 8 previous errors
+error: collection is never read
+  --> $DIR/collection_is_never_read.rs:174:5
+   |
+LL |     let mut s = String::new();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+  --> $DIR/collection_is_never_read.rs:187:5
+   |
+LL |     let mut s = String::from("Hello, World!");
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 12 previous errors
 
diff --git a/src/tools/clippy/tests/ui/double_must_use.rs b/src/tools/clippy/tests/ui/double_must_use.rs
index 05e087b08bc..26a387b3cf0 100644
--- a/src/tools/clippy/tests/ui/double_must_use.rs
+++ b/src/tools/clippy/tests/ui/double_must_use.rs
@@ -21,6 +21,17 @@ pub fn must_use_with_note() -> Result<(), ()> {
     unimplemented!();
 }
 
+// vvvv Should not lint (#10486)
+#[must_use]
+async fn async_must_use() -> usize {
+    unimplemented!();
+}
+
+#[must_use]
+async fn async_must_use_result() -> Result<(), ()> {
+    Ok(())
+}
+
 fn main() {
     must_use_result();
     must_use_tuple();
diff --git a/src/tools/clippy/tests/ui/double_must_use.stderr b/src/tools/clippy/tests/ui/double_must_use.stderr
index 3d34557a881..49ab2ea3e12 100644
--- a/src/tools/clippy/tests/ui/double_must_use.stderr
+++ b/src/tools/clippy/tests/ui/double_must_use.stderr
@@ -23,5 +23,13 @@ LL | pub fn must_use_array() -> [Result<(), ()>; 1] {
    |
    = help: either add some descriptive text or remove the attribute
 
-error: aborting due to 3 previous errors
+error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`
+  --> $DIR/double_must_use.rs:31:1
+   |
+LL | async fn async_must_use_result() -> Result<(), ()> {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: either add some descriptive text or remove the attribute
+
+error: aborting due to 4 previous errors
 
diff --git a/src/tools/clippy/tests/ui/extra_unused_type_parameters.fixed b/src/tools/clippy/tests/ui/extra_unused_type_parameters.fixed
new file mode 100644
index 00000000000..19e71862558
--- /dev/null
+++ b/src/tools/clippy/tests/ui/extra_unused_type_parameters.fixed
@@ -0,0 +1,105 @@
+// run-rustfix
+
+#![allow(unused, clippy::needless_lifetimes)]
+#![warn(clippy::extra_unused_type_parameters)]
+
+fn unused_ty(x: u8) {
+    unimplemented!()
+}
+
+fn unused_multi(x: u8) {
+    unimplemented!()
+}
+
+fn unused_with_lt<'a>(x: &'a u8) {
+    unimplemented!()
+}
+
+fn used_ty<T>(x: T, y: u8) {}
+
+fn used_ref<'a, T>(x: &'a T) {}
+
+fn used_ret<T: Default>(x: u8) -> T {
+    T::default()
+}
+
+fn unused_bounded<U>(x: U) {
+    unimplemented!();
+}
+
+fn some_unused<B, C>(b: B, c: C) {
+    unimplemented!();
+}
+
+fn used_opaque<A>(iter: impl Iterator<Item = A>) -> usize {
+    iter.count()
+}
+
+fn used_ret_opaque<A>() -> impl Iterator<Item = A> {
+    std::iter::empty()
+}
+
+fn used_vec_box<T>(x: Vec<Box<T>>) {}
+
+fn used_body<T: Default + ToString>() -> String {
+    T::default().to_string()
+}
+
+fn used_closure<T: Default + ToString>() -> impl Fn() {
+    || println!("{}", T::default().to_string())
+}
+
+struct S;
+
+impl S {
+    fn unused_ty_impl(&self) {
+        unimplemented!()
+    }
+}
+
+// Don't lint on trait methods
+trait Foo {
+    fn bar<T>(&self);
+}
+
+impl Foo for S {
+    fn bar<T>(&self) {}
+}
+
+fn skip_index<A, Iter>(iter: Iter, index: usize) -> impl Iterator<Item = A>
+where
+    Iter: Iterator<Item = A>,
+{
+    iter.enumerate()
+        .filter_map(move |(i, a)| if i == index { None } else { Some(a) })
+}
+
+fn unused_opaque(dummy: impl Default) {
+    unimplemented!()
+}
+
+mod unexported_trait_bounds {
+    mod private {
+        pub trait Private {}
+    }
+
+    fn priv_trait_bound<T: private::Private>() {
+        unimplemented!();
+    }
+
+    fn unused_with_priv_trait_bound<T: private::Private>() {
+        unimplemented!();
+    }
+}
+
+mod issue10319 {
+    fn assert_send<T: Send>() {}
+
+    fn assert_send_where<T>()
+    where
+        T: Send,
+    {
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/extra_unused_type_parameters.rs b/src/tools/clippy/tests/ui/extra_unused_type_parameters.rs
index 48017434276..e53bb587e89 100644
--- a/src/tools/clippy/tests/ui/extra_unused_type_parameters.rs
+++ b/src/tools/clippy/tests/ui/extra_unused_type_parameters.rs
@@ -1,3 +1,5 @@
+// run-rustfix
+
 #![allow(unused, clippy::needless_lifetimes)]
 #![warn(clippy::extra_unused_type_parameters)]
 
@@ -21,14 +23,7 @@ fn used_ret<T: Default>(x: u8) -> T {
     T::default()
 }
 
-fn unused_bounded<T: Default, U>(x: U) {
-    unimplemented!();
-}
-
-fn unused_where_clause<T, U>(x: U)
-where
-    T: Default,
-{
+fn unused_bounded<T: Default, U, V: Default>(x: U) {
     unimplemented!();
 }
 
diff --git a/src/tools/clippy/tests/ui/extra_unused_type_parameters.stderr b/src/tools/clippy/tests/ui/extra_unused_type_parameters.stderr
index 86c88fc9bf0..c042a5a2290 100644
--- a/src/tools/clippy/tests/ui/extra_unused_type_parameters.stderr
+++ b/src/tools/clippy/tests/ui/extra_unused_type_parameters.stderr
@@ -1,75 +1,64 @@
-error: type parameter goes unused in function definition
-  --> $DIR/extra_unused_type_parameters.rs:4:13
+error: type parameter `T` goes unused in function definition
+  --> $DIR/extra_unused_type_parameters.rs:6:13
    |
 LL | fn unused_ty<T>(x: u8) {
-   |             ^^^
+   |             ^^^ help: consider removing the parameter
    |
-   = help: consider removing the parameter
    = note: `-D clippy::extra-unused-type-parameters` implied by `-D warnings`
 
-error: type parameters go unused in function definition
-  --> $DIR/extra_unused_type_parameters.rs:8:16
+error: type parameters go unused in function definition: T, U
+  --> $DIR/extra_unused_type_parameters.rs:10:16
    |
 LL | fn unused_multi<T, U>(x: u8) {
-   |                ^^^^^^
-   |
-   = help: consider removing the parameters
+   |                ^^^^^^ help: consider removing the parameters
 
-error: type parameter goes unused in function definition
-  --> $DIR/extra_unused_type_parameters.rs:12:23
+error: type parameter `T` goes unused in function definition
+  --> $DIR/extra_unused_type_parameters.rs:14:21
    |
 LL | fn unused_with_lt<'a, T>(x: &'a u8) {
-   |                       ^
-   |
-   = help: consider removing the parameter
+   |                     ^^^ help: consider removing the parameter
 
-error: type parameter goes unused in function definition
-  --> $DIR/extra_unused_type_parameters.rs:24:19
+error: type parameters go unused in function definition: T, V
+  --> $DIR/extra_unused_type_parameters.rs:26:19
    |
-LL | fn unused_bounded<T: Default, U>(x: U) {
-   |                   ^^^^^^^^^^^
+LL | fn unused_bounded<T: Default, U, V: Default>(x: U) {
+   |                   ^^^^^^^^^^^^ ^^^^^^^^^^^^
    |
-   = help: consider removing the parameter
-
-error: type parameter goes unused in function definition
-  --> $DIR/extra_unused_type_parameters.rs:28:24
+help: consider removing the parameters
    |
-LL | fn unused_where_clause<T, U>(x: U)
-   |                        ^^
+LL - fn unused_bounded<T: Default, U, V: Default>(x: U) {
+LL + fn unused_bounded<U>(x: U) {
    |
-   = help: consider removing the parameter
 
-error: type parameters go unused in function definition
-  --> $DIR/extra_unused_type_parameters.rs:35:16
+error: type parameters go unused in function definition: A, D, E
+  --> $DIR/extra_unused_type_parameters.rs:30:16
    |
 LL | fn some_unused<A, B, C, D: Iterator<Item = (B, C)>, E>(b: B, c: C) {
-   |                ^^       ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^
+   |                ^^^    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider removing the parameters
+   |
+LL - fn some_unused<A, B, C, D: Iterator<Item = (B, C)>, E>(b: B, c: C) {
+LL + fn some_unused<B, C>(b: B, c: C) {
    |
-   = help: consider removing the parameters
 
-error: type parameter goes unused in function definition
-  --> $DIR/extra_unused_type_parameters.rs:60:22
+error: type parameter `T` goes unused in function definition
+  --> $DIR/extra_unused_type_parameters.rs:55:22
    |
 LL |     fn unused_ty_impl<T>(&self) {
-   |                      ^^^
-   |
-   = help: consider removing the parameter
+   |                      ^^^ help: consider removing the parameter
 
-error: type parameters go unused in function definition
-  --> $DIR/extra_unused_type_parameters.rs:82:17
+error: type parameters go unused in function definition: A, B
+  --> $DIR/extra_unused_type_parameters.rs:77:17
    |
 LL | fn unused_opaque<A, B>(dummy: impl Default) {
-   |                 ^^^^^^
-   |
-   = help: consider removing the parameters
+   |                 ^^^^^^ help: consider removing the parameters
 
-error: type parameter goes unused in function definition
-  --> $DIR/extra_unused_type_parameters.rs:95:58
+error: type parameter `U` goes unused in function definition
+  --> $DIR/extra_unused_type_parameters.rs:90:56
    |
 LL |     fn unused_with_priv_trait_bound<T: private::Private, U>() {
-   |                                                          ^
-   |
-   = help: consider removing the parameter
+   |                                                        ^^^ help: consider removing the parameter
 
-error: aborting due to 9 previous errors
+error: aborting due to 8 previous errors
 
diff --git a/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.rs b/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.rs
new file mode 100644
index 00000000000..10b39aa8f2c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.rs
@@ -0,0 +1,24 @@
+#![warn(clippy::extra_unused_type_parameters)]
+
+fn unused_where_clause<T, U>(x: U)
+where
+    T: Default,
+{
+    unimplemented!();
+}
+
+fn unused_multi_where_clause<T, U, V: Default>(x: U)
+where
+    T: Default,
+{
+    unimplemented!();
+}
+
+fn unused_all_where_clause<T, U: Default, V: Default>()
+where
+    T: Default,
+{
+    unimplemented!();
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.stderr b/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.stderr
new file mode 100644
index 00000000000..a9580cc894f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.stderr
@@ -0,0 +1,27 @@
+error: type parameter `T` goes unused in function definition
+  --> $DIR/extra_unused_type_parameters_unfixable.rs:3:24
+   |
+LL | fn unused_where_clause<T, U>(x: U)
+   |                        ^
+   |
+   = help: consider removing the parameter
+   = note: `-D clippy::extra-unused-type-parameters` implied by `-D warnings`
+
+error: type parameters go unused in function definition: T, V
+  --> $DIR/extra_unused_type_parameters_unfixable.rs:10:30
+   |
+LL | fn unused_multi_where_clause<T, U, V: Default>(x: U)
+   |                              ^     ^^^^^^^^^^
+   |
+   = help: consider removing the parameters
+
+error: type parameters go unused in function definition: T, U, V
+  --> $DIR/extra_unused_type_parameters_unfixable.rs:17:28
+   |
+LL | fn unused_all_where_clause<T, U: Default, V: Default>()
+   |                            ^  ^^^^^^^^^^  ^^^^^^^^^^
+   |
+   = help: consider removing the parameters
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/format_args_unfixable.rs b/src/tools/clippy/tests/ui/format_args_unfixable.rs
index eb0ac15bfbf..423bfaf9796 100644
--- a/src/tools/clippy/tests/ui/format_args_unfixable.rs
+++ b/src/tools/clippy/tests/ui/format_args_unfixable.rs
@@ -1,4 +1,5 @@
 #![warn(clippy::format_in_format_args, clippy::to_string_in_format_args)]
+#![allow(unused)]
 #![allow(clippy::assertions_on_constants, clippy::eq_op, clippy::uninlined_format_args)]
 
 use std::io::{stdout, Error, ErrorKind, Write};
@@ -57,3 +58,46 @@ fn main() {
     my_macro!();
     println!("error: {}", my_other_macro!());
 }
+
+macro_rules! _internal {
+    ($($args:tt)*) => {
+        println!("{}", format_args!($($args)*))
+    };
+}
+
+macro_rules! my_println2 {
+   ($target:expr, $($args:tt)+) => {{
+       if $target {
+           _internal!($($args)+)
+       }
+    }};
+}
+
+macro_rules! my_println2_args {
+    ($target:expr, $($args:tt)+) => {{
+       if $target {
+           _internal!("foo: {}", format_args!($($args)+))
+       }
+    }};
+}
+
+fn test2() {
+    let error = Error::new(ErrorKind::Other, "bad thing");
+
+    // None of these should be linted without the config change
+    my_println2!(true, "error: {}", format!("something failed at {}", Location::caller()));
+    my_println2!(
+        true,
+        "{}: {}",
+        error,
+        format!("something failed at {}", Location::caller())
+    );
+
+    my_println2_args!(true, "error: {}", format!("something failed at {}", Location::caller()));
+    my_println2_args!(
+        true,
+        "{}: {}",
+        error,
+        format!("something failed at {}", Location::caller())
+    );
+}
diff --git a/src/tools/clippy/tests/ui/format_args_unfixable.stderr b/src/tools/clippy/tests/ui/format_args_unfixable.stderr
index b291d475ad9..c1be48c3b72 100644
--- a/src/tools/clippy/tests/ui/format_args_unfixable.stderr
+++ b/src/tools/clippy/tests/ui/format_args_unfixable.stderr
@@ -1,5 +1,5 @@
 error: `format!` in `println!` args
-  --> $DIR/format_args_unfixable.rs:25:5
+  --> $DIR/format_args_unfixable.rs:26:5
    |
 LL |     println!("error: {}", format!("something failed at {}", Location::caller()));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -9,7 +9,7 @@ LL |     println!("error: {}", format!("something failed at {}", Location::calle
    = note: `-D clippy::format-in-format-args` implied by `-D warnings`
 
 error: `format!` in `println!` args
-  --> $DIR/format_args_unfixable.rs:26:5
+  --> $DIR/format_args_unfixable.rs:27:5
    |
 LL |     println!("{}: {}", error, format!("something failed at {}", Location::caller()));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -18,7 +18,7 @@ LL |     println!("{}: {}", error, format!("something failed at {}", Location::c
    = help: or consider changing `format!` to `format_args!`
 
 error: `format!` in `println!` args
-  --> $DIR/format_args_unfixable.rs:27:5
+  --> $DIR/format_args_unfixable.rs:28:5
    |
 LL |     println!("{:?}: {}", error, format!("something failed at {}", Location::caller()));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -27,7 +27,7 @@ LL |     println!("{:?}: {}", error, format!("something failed at {}", Location:
    = help: or consider changing `format!` to `format_args!`
 
 error: `format!` in `println!` args
-  --> $DIR/format_args_unfixable.rs:28:5
+  --> $DIR/format_args_unfixable.rs:29:5
    |
 LL |     println!("{{}}: {}", format!("something failed at {}", Location::caller()));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -36,7 +36,7 @@ LL |     println!("{{}}: {}", format!("something failed at {}", Location::caller
    = help: or consider changing `format!` to `format_args!`
 
 error: `format!` in `println!` args
-  --> $DIR/format_args_unfixable.rs:29:5
+  --> $DIR/format_args_unfixable.rs:30:5
    |
 LL |     println!(r#"error: "{}""#, format!("something failed at {}", Location::caller()));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -45,7 +45,7 @@ LL |     println!(r#"error: "{}""#, format!("something failed at {}", Location::
    = help: or consider changing `format!` to `format_args!`
 
 error: `format!` in `println!` args
-  --> $DIR/format_args_unfixable.rs:30:5
+  --> $DIR/format_args_unfixable.rs:31:5
    |
 LL |     println!("error: {}", format!(r#"something failed at "{}""#, Location::caller()));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -54,7 +54,7 @@ LL |     println!("error: {}", format!(r#"something failed at "{}""#, Location::
    = help: or consider changing `format!` to `format_args!`
 
 error: `format!` in `println!` args
-  --> $DIR/format_args_unfixable.rs:31:5
+  --> $DIR/format_args_unfixable.rs:32:5
    |
 LL |     println!("error: {}", format!("something failed at {} {0}", Location::caller()));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -63,7 +63,7 @@ LL |     println!("error: {}", format!("something failed at {} {0}", Location::c
    = help: or consider changing `format!` to `format_args!`
 
 error: `format!` in `format!` args
-  --> $DIR/format_args_unfixable.rs:32:13
+  --> $DIR/format_args_unfixable.rs:33:13
    |
 LL |     let _ = format!("error: {}", format!("something failed at {}", Location::caller()));
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -72,7 +72,7 @@ LL |     let _ = format!("error: {}", format!("something failed at {}", Location
    = help: or consider changing `format!` to `format_args!`
 
 error: `format!` in `write!` args
-  --> $DIR/format_args_unfixable.rs:33:13
+  --> $DIR/format_args_unfixable.rs:34:13
    |
 LL |       let _ = write!(
    |  _____________^
@@ -86,7 +86,7 @@ LL | |     );
    = help: or consider changing `format!` to `format_args!`
 
 error: `format!` in `writeln!` args
-  --> $DIR/format_args_unfixable.rs:38:13
+  --> $DIR/format_args_unfixable.rs:39:13
    |
 LL |       let _ = writeln!(
    |  _____________^
@@ -100,7 +100,7 @@ LL | |     );
    = help: or consider changing `format!` to `format_args!`
 
 error: `format!` in `print!` args
-  --> $DIR/format_args_unfixable.rs:43:5
+  --> $DIR/format_args_unfixable.rs:44:5
    |
 LL |     print!("error: {}", format!("something failed at {}", Location::caller()));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -109,7 +109,7 @@ LL |     print!("error: {}", format!("something failed at {}", Location::caller(
    = help: or consider changing `format!` to `format_args!`
 
 error: `format!` in `eprint!` args
-  --> $DIR/format_args_unfixable.rs:44:5
+  --> $DIR/format_args_unfixable.rs:45:5
    |
 LL |     eprint!("error: {}", format!("something failed at {}", Location::caller()));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -118,7 +118,7 @@ LL |     eprint!("error: {}", format!("something failed at {}", Location::caller
    = help: or consider changing `format!` to `format_args!`
 
 error: `format!` in `eprintln!` args
-  --> $DIR/format_args_unfixable.rs:45:5
+  --> $DIR/format_args_unfixable.rs:46:5
    |
 LL |     eprintln!("error: {}", format!("something failed at {}", Location::caller()));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -127,7 +127,7 @@ LL |     eprintln!("error: {}", format!("something failed at {}", Location::call
    = help: or consider changing `format!` to `format_args!`
 
 error: `format!` in `format_args!` args
-  --> $DIR/format_args_unfixable.rs:46:13
+  --> $DIR/format_args_unfixable.rs:47:13
    |
 LL |     let _ = format_args!("error: {}", format!("something failed at {}", Location::caller()));
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -136,7 +136,7 @@ LL |     let _ = format_args!("error: {}", format!("something failed at {}", Loc
    = help: or consider changing `format!` to `format_args!`
 
 error: `format!` in `assert!` args
-  --> $DIR/format_args_unfixable.rs:47:5
+  --> $DIR/format_args_unfixable.rs:48:5
    |
 LL |     assert!(true, "error: {}", format!("something failed at {}", Location::caller()));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -145,7 +145,7 @@ LL |     assert!(true, "error: {}", format!("something failed at {}", Location::
    = help: or consider changing `format!` to `format_args!`
 
 error: `format!` in `assert_eq!` args
-  --> $DIR/format_args_unfixable.rs:48:5
+  --> $DIR/format_args_unfixable.rs:49:5
    |
 LL |     assert_eq!(0, 0, "error: {}", format!("something failed at {}", Location::caller()));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -154,7 +154,7 @@ LL |     assert_eq!(0, 0, "error: {}", format!("something failed at {}", Locatio
    = help: or consider changing `format!` to `format_args!`
 
 error: `format!` in `assert_ne!` args
-  --> $DIR/format_args_unfixable.rs:49:5
+  --> $DIR/format_args_unfixable.rs:50:5
    |
 LL |     assert_ne!(0, 0, "error: {}", format!("something failed at {}", Location::caller()));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -163,7 +163,7 @@ LL |     assert_ne!(0, 0, "error: {}", format!("something failed at {}", Locatio
    = help: or consider changing `format!` to `format_args!`
 
 error: `format!` in `panic!` args
-  --> $DIR/format_args_unfixable.rs:50:5
+  --> $DIR/format_args_unfixable.rs:51:5
    |
 LL |     panic!("error: {}", format!("something failed at {}", Location::caller()));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/item_after_statement.rs b/src/tools/clippy/tests/ui/items_after_statement.rs
index 5e92dcab1f5..f12cb8f22e2 100644
--- a/src/tools/clippy/tests/ui/item_after_statement.rs
+++ b/src/tools/clippy/tests/ui/items_after_statement.rs
@@ -51,3 +51,20 @@ fn semicolon() {
 
     let _ = S::new(3);
 }
+
+fn item_from_macro() {
+    macro_rules! static_assert_size {
+        ($ty:ty, $size:expr) => {
+            const _: [(); $size] = [(); ::std::mem::size_of::<$ty>()];
+        };
+    }
+
+    let _ = 1;
+    static_assert_size!(u32, 4);
+}
+
+fn allow_attribute() {
+    let _ = 1;
+    #[allow(clippy::items_after_statements)]
+    const _: usize = 1;
+}
diff --git a/src/tools/clippy/tests/ui/item_after_statement.stderr b/src/tools/clippy/tests/ui/items_after_statement.stderr
index 2523c53ac53..f69635a977b 100644
--- a/src/tools/clippy/tests/ui/item_after_statement.stderr
+++ b/src/tools/clippy/tests/ui/items_after_statement.stderr
@@ -1,5 +1,5 @@
 error: adding items after statements is confusing, since items exist from the start of the scope
-  --> $DIR/item_after_statement.rs:13:5
+  --> $DIR/items_after_statement.rs:13:5
    |
 LL | /     fn foo() {
 LL | |         println!("foo");
@@ -9,7 +9,7 @@ LL | |     }
    = note: `-D clippy::items-after-statements` implied by `-D warnings`
 
 error: adding items after statements is confusing, since items exist from the start of the scope
-  --> $DIR/item_after_statement.rs:20:5
+  --> $DIR/items_after_statement.rs:20:5
    |
 LL | /     fn foo() {
 LL | |         println!("foo");
@@ -17,7 +17,7 @@ LL | |     }
    | |_____^
 
 error: adding items after statements is confusing, since items exist from the start of the scope
-  --> $DIR/item_after_statement.rs:33:13
+  --> $DIR/items_after_statement.rs:33:13
    |
 LL | /             fn say_something() {
 LL | |                 println!("something");
diff --git a/src/tools/clippy/tests/ui/large_futures.rs b/src/tools/clippy/tests/ui/large_futures.rs
new file mode 100644
index 00000000000..4a8ba995da5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/large_futures.rs
@@ -0,0 +1,61 @@
+#![feature(generators)]
+#![warn(clippy::large_futures)]
+#![allow(clippy::future_not_send)]
+#![allow(clippy::manual_async_fn)]
+
+async fn big_fut(_arg: [u8; 1024 * 16]) {}
+
+async fn wait() {
+    let f = async {
+        big_fut([0u8; 1024 * 16]).await;
+    };
+    f.await
+}
+async fn calls_fut(fut: impl std::future::Future<Output = ()>) {
+    loop {
+        wait().await;
+        if true {
+            return fut.await;
+        } else {
+            wait().await;
+        }
+    }
+}
+
+pub async fn test() {
+    let fut = big_fut([0u8; 1024 * 16]);
+    foo().await;
+    calls_fut(fut).await;
+}
+
+pub fn foo() -> impl std::future::Future<Output = ()> {
+    async {
+        let x = [0i32; 1024 * 16];
+        async {}.await;
+        dbg!(x);
+    }
+}
+
+pub async fn lines() {
+    async {
+        let x = [0i32; 1024 * 16];
+        async {}.await;
+        println!("{:?}", x);
+    }
+    .await;
+}
+
+pub async fn macro_expn() {
+    macro_rules! macro_ {
+        () => {
+            async {
+                let x = [0i32; 1024 * 16];
+                async {}.await;
+                println!("macro: {:?}", x);
+            }
+        };
+    }
+    macro_!().await
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/large_futures.stderr b/src/tools/clippy/tests/ui/large_futures.stderr
new file mode 100644
index 00000000000..67e0fceff6e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/large_futures.stderr
@@ -0,0 +1,82 @@
+error: large future with a size of 16385 bytes
+  --> $DIR/large_futures.rs:10:9
+   |
+LL |         big_fut([0u8; 1024 * 16]).await;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Box::pin` on it: `Box::pin(big_fut([0u8; 1024 * 16]))`
+   |
+   = note: `-D clippy::large-futures` implied by `-D warnings`
+
+error: large future with a size of 16386 bytes
+  --> $DIR/large_futures.rs:12:5
+   |
+LL |     f.await
+   |     ^ help: consider `Box::pin` on it: `Box::pin(f)`
+
+error: large future with a size of 16387 bytes
+  --> $DIR/large_futures.rs:16:9
+   |
+LL |         wait().await;
+   |         ^^^^^^ help: consider `Box::pin` on it: `Box::pin(wait())`
+
+error: large future with a size of 16387 bytes
+  --> $DIR/large_futures.rs:20:13
+   |
+LL |             wait().await;
+   |             ^^^^^^ help: consider `Box::pin` on it: `Box::pin(wait())`
+
+error: large future with a size of 65540 bytes
+  --> $DIR/large_futures.rs:27:5
+   |
+LL |     foo().await;
+   |     ^^^^^ help: consider `Box::pin` on it: `Box::pin(foo())`
+
+error: large future with a size of 49159 bytes
+  --> $DIR/large_futures.rs:28:5
+   |
+LL |     calls_fut(fut).await;
+   |     ^^^^^^^^^^^^^^ help: consider `Box::pin` on it: `Box::pin(calls_fut(fut))`
+
+error: large future with a size of 65540 bytes
+  --> $DIR/large_futures.rs:40:5
+   |
+LL | /     async {
+LL | |         let x = [0i32; 1024 * 16];
+LL | |         async {}.await;
+LL | |         println!("{:?}", x);
+LL | |     }
+   | |_____^
+   |
+help: consider `Box::pin` on it
+   |
+LL ~     Box::pin(async {
+LL +         let x = [0i32; 1024 * 16];
+LL +         async {}.await;
+LL +         println!("{:?}", x);
+LL +     })
+   |
+
+error: large future with a size of 65540 bytes
+  --> $DIR/large_futures.rs:51:13
+   |
+LL | /             async {
+LL | |                 let x = [0i32; 1024 * 16];
+LL | |                 async {}.await;
+LL | |                 println!("macro: {:?}", x);
+LL | |             }
+   | |_____________^
+...
+LL |       macro_!().await
+   |       --------- in this macro invocation
+   |
+   = note: this error originates in the macro `macro_` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider `Box::pin` on it
+   |
+LL ~             Box::pin(async {
+LL +                 let x = [0i32; 1024 * 16];
+LL +                 async {}.await;
+LL +                 println!("macro: {:?}", x);
+LL +             })
+   |
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/lines_filter_map_ok.fixed b/src/tools/clippy/tests/ui/lines_filter_map_ok.fixed
new file mode 100644
index 00000000000..f4033cd8ed8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/lines_filter_map_ok.fixed
@@ -0,0 +1,29 @@
+// run-rustfix
+
+#![allow(unused, clippy::map_identity)]
+#![warn(clippy::lines_filter_map_ok)]
+
+use std::io::{self, BufRead, BufReader};
+
+fn main() -> io::Result<()> {
+    let f = std::fs::File::open("/")?;
+    // Lint
+    BufReader::new(f).lines().map_while(Result::ok).for_each(|_| ());
+    // Lint
+    let f = std::fs::File::open("/")?;
+    BufReader::new(f).lines().map_while(Result::ok).for_each(|_| ());
+    let s = "foo\nbar\nbaz\n";
+    // Lint
+    io::stdin().lines().map_while(Result::ok).for_each(|_| ());
+    // Lint
+    io::stdin().lines().map_while(Result::ok).for_each(|_| ());
+    // Do not lint (not a `Lines` iterator)
+    io::stdin()
+        .lines()
+        .map(std::convert::identity)
+        .filter_map(|x| x.ok())
+        .for_each(|_| ());
+    // Do not lint (not a `Result::ok()` extractor)
+    io::stdin().lines().filter_map(|x| x.err()).for_each(|_| ());
+    Ok(())
+}
diff --git a/src/tools/clippy/tests/ui/lines_filter_map_ok.rs b/src/tools/clippy/tests/ui/lines_filter_map_ok.rs
new file mode 100644
index 00000000000..7e11816b2ac
--- /dev/null
+++ b/src/tools/clippy/tests/ui/lines_filter_map_ok.rs
@@ -0,0 +1,29 @@
+// run-rustfix
+
+#![allow(unused, clippy::map_identity)]
+#![warn(clippy::lines_filter_map_ok)]
+
+use std::io::{self, BufRead, BufReader};
+
+fn main() -> io::Result<()> {
+    let f = std::fs::File::open("/")?;
+    // Lint
+    BufReader::new(f).lines().filter_map(Result::ok).for_each(|_| ());
+    // Lint
+    let f = std::fs::File::open("/")?;
+    BufReader::new(f).lines().flat_map(Result::ok).for_each(|_| ());
+    let s = "foo\nbar\nbaz\n";
+    // Lint
+    io::stdin().lines().filter_map(Result::ok).for_each(|_| ());
+    // Lint
+    io::stdin().lines().filter_map(|x| x.ok()).for_each(|_| ());
+    // Do not lint (not a `Lines` iterator)
+    io::stdin()
+        .lines()
+        .map(std::convert::identity)
+        .filter_map(|x| x.ok())
+        .for_each(|_| ());
+    // Do not lint (not a `Result::ok()` extractor)
+    io::stdin().lines().filter_map(|x| x.err()).for_each(|_| ());
+    Ok(())
+}
diff --git a/src/tools/clippy/tests/ui/lines_filter_map_ok.stderr b/src/tools/clippy/tests/ui/lines_filter_map_ok.stderr
new file mode 100644
index 00000000000..cddd403d589
--- /dev/null
+++ b/src/tools/clippy/tests/ui/lines_filter_map_ok.stderr
@@ -0,0 +1,51 @@
+error: `filter_map()` will run forever if the iterator repeatedly produces an `Err`
+  --> $DIR/lines_filter_map_ok.rs:11:31
+   |
+LL |     BufReader::new(f).lines().filter_map(Result::ok).for_each(|_| ());
+   |                               ^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `map_while(Result::ok)`
+   |
+note: this expression returning a `std::io::Lines` may produce an infinite number of `Err` in case of a read error
+  --> $DIR/lines_filter_map_ok.rs:11:5
+   |
+LL |     BufReader::new(f).lines().filter_map(Result::ok).for_each(|_| ());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: `-D clippy::lines-filter-map-ok` implied by `-D warnings`
+
+error: `flat_map()` will run forever if the iterator repeatedly produces an `Err`
+  --> $DIR/lines_filter_map_ok.rs:14:31
+   |
+LL |     BufReader::new(f).lines().flat_map(Result::ok).for_each(|_| ());
+   |                               ^^^^^^^^^^^^^^^^^^^^ help: replace with: `map_while(Result::ok)`
+   |
+note: this expression returning a `std::io::Lines` may produce an infinite number of `Err` in case of a read error
+  --> $DIR/lines_filter_map_ok.rs:14:5
+   |
+LL |     BufReader::new(f).lines().flat_map(Result::ok).for_each(|_| ());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `filter_map()` will run forever if the iterator repeatedly produces an `Err`
+  --> $DIR/lines_filter_map_ok.rs:17:25
+   |
+LL |     io::stdin().lines().filter_map(Result::ok).for_each(|_| ());
+   |                         ^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `map_while(Result::ok)`
+   |
+note: this expression returning a `std::io::Lines` may produce an infinite number of `Err` in case of a read error
+  --> $DIR/lines_filter_map_ok.rs:17:5
+   |
+LL |     io::stdin().lines().filter_map(Result::ok).for_each(|_| ());
+   |     ^^^^^^^^^^^^^^^^^^^
+
+error: `filter_map()` will run forever if the iterator repeatedly produces an `Err`
+  --> $DIR/lines_filter_map_ok.rs:19:25
+   |
+LL |     io::stdin().lines().filter_map(|x| x.ok()).for_each(|_| ());
+   |                         ^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `map_while(Result::ok)`
+   |
+note: this expression returning a `std::io::Lines` may produce an infinite number of `Err` in case of a read error
+  --> $DIR/lines_filter_map_ok.rs:19:5
+   |
+LL |     io::stdin().lines().filter_map(|x| x.ok()).for_each(|_| ());
+   |     ^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs b/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs
new file mode 100644
index 00000000000..5082f931f3c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs
@@ -0,0 +1,36 @@
+#![allow(unused)]
+#![warn(clippy::manual_slice_size_calculation)]
+
+use core::mem::{align_of, size_of};
+
+fn main() {
+    let v_i32 = Vec::<i32>::new();
+    let s_i32 = v_i32.as_slice();
+
+    // True positives:
+    let _ = s_i32.len() * size_of::<i32>(); // WARNING
+    let _ = size_of::<i32>() * s_i32.len(); // WARNING
+    let _ = size_of::<i32>() * s_i32.len() * 5; // WARNING
+
+    let len = s_i32.len();
+    let size = size_of::<i32>();
+    let _ = len * size_of::<i32>(); // WARNING
+    let _ = s_i32.len() * size; // WARNING
+    let _ = len * size; // WARNING
+
+    // True negatives:
+    let _ = size_of::<i32>() + s_i32.len(); // Ok, not a multiplication
+    let _ = size_of::<i32>() * s_i32.partition_point(|_| true); // Ok, not len()
+    let _ = size_of::<i32>() * v_i32.len(); // Ok, not a slice
+    let _ = align_of::<i32>() * s_i32.len(); // Ok, not size_of()
+    let _ = size_of::<u32>() * s_i32.len(); // Ok, different types
+
+    // False negatives:
+    let _ = 5 * size_of::<i32>() * s_i32.len(); // Ok (MISSED OPPORTUNITY)
+    let _ = size_of::<i32>() * 5 * s_i32.len(); // Ok (MISSED OPPORTUNITY)
+}
+
+const fn _const(s_i32: &[i32]) {
+    // True negative:
+    let _ = s_i32.len() * size_of::<i32>(); // Ok, can't use size_of_val in const
+}
diff --git a/src/tools/clippy/tests/ui/manual_slice_size_calculation.stderr b/src/tools/clippy/tests/ui/manual_slice_size_calculation.stderr
new file mode 100644
index 00000000000..4a24fc60a0f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_slice_size_calculation.stderr
@@ -0,0 +1,51 @@
+error: manual slice size calculation
+  --> $DIR/manual_slice_size_calculation.rs:11:13
+   |
+LL |     let _ = s_i32.len() * size_of::<i32>(); // WARNING
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using std::mem::size_of_value instead
+   = note: `-D clippy::manual-slice-size-calculation` implied by `-D warnings`
+
+error: manual slice size calculation
+  --> $DIR/manual_slice_size_calculation.rs:12:13
+   |
+LL |     let _ = size_of::<i32>() * s_i32.len(); // WARNING
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using std::mem::size_of_value instead
+
+error: manual slice size calculation
+  --> $DIR/manual_slice_size_calculation.rs:13:13
+   |
+LL |     let _ = size_of::<i32>() * s_i32.len() * 5; // WARNING
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using std::mem::size_of_value instead
+
+error: manual slice size calculation
+  --> $DIR/manual_slice_size_calculation.rs:17:13
+   |
+LL |     let _ = len * size_of::<i32>(); // WARNING
+   |             ^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using std::mem::size_of_value instead
+
+error: manual slice size calculation
+  --> $DIR/manual_slice_size_calculation.rs:18:13
+   |
+LL |     let _ = s_i32.len() * size; // WARNING
+   |             ^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using std::mem::size_of_value instead
+
+error: manual slice size calculation
+  --> $DIR/manual_slice_size_calculation.rs:19:13
+   |
+LL |     let _ = len * size; // WARNING
+   |             ^^^^^^^^^^
+   |
+   = help: consider using std::mem::size_of_value instead
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/mem_replace.fixed b/src/tools/clippy/tests/ui/mem_replace.fixed
index 874d5584330..7fd340173af 100644
--- a/src/tools/clippy/tests/ui/mem_replace.fixed
+++ b/src/tools/clippy/tests/ui/mem_replace.fixed
@@ -90,3 +90,37 @@ fn msrv_1_40() {
     let mut s = String::from("foo");
     let _ = std::mem::take(&mut s);
 }
+
+fn issue9824() {
+    struct Foo<'a>(Option<&'a str>);
+    impl<'a> std::ops::Deref for Foo<'a> {
+        type Target = Option<&'a str>;
+
+        fn deref(&self) -> &Self::Target {
+            &self.0
+        }
+    }
+    impl<'a> std::ops::DerefMut for Foo<'a> {
+        fn deref_mut(&mut self) -> &mut Self::Target {
+            &mut self.0
+        }
+    }
+
+    struct Bar {
+        opt: Option<u8>,
+        val: String,
+    }
+
+    let mut f = Foo(Some("foo"));
+    let mut b = Bar {
+        opt: Some(1),
+        val: String::from("bar"),
+    };
+
+    // replace option with none
+    let _ = f.0.take();
+    let _ = (*f).take();
+    let _ = b.opt.take();
+    // replace with default
+    let _ = std::mem::take(&mut b.val);
+}
diff --git a/src/tools/clippy/tests/ui/mem_replace.rs b/src/tools/clippy/tests/ui/mem_replace.rs
index f4f3bff5144..fa2903addbc 100644
--- a/src/tools/clippy/tests/ui/mem_replace.rs
+++ b/src/tools/clippy/tests/ui/mem_replace.rs
@@ -90,3 +90,37 @@ fn msrv_1_40() {
     let mut s = String::from("foo");
     let _ = std::mem::replace(&mut s, String::default());
 }
+
+fn issue9824() {
+    struct Foo<'a>(Option<&'a str>);
+    impl<'a> std::ops::Deref for Foo<'a> {
+        type Target = Option<&'a str>;
+
+        fn deref(&self) -> &Self::Target {
+            &self.0
+        }
+    }
+    impl<'a> std::ops::DerefMut for Foo<'a> {
+        fn deref_mut(&mut self) -> &mut Self::Target {
+            &mut self.0
+        }
+    }
+
+    struct Bar {
+        opt: Option<u8>,
+        val: String,
+    }
+
+    let mut f = Foo(Some("foo"));
+    let mut b = Bar {
+        opt: Some(1),
+        val: String::from("bar"),
+    };
+
+    // replace option with none
+    let _ = std::mem::replace(&mut f.0, None);
+    let _ = std::mem::replace(&mut *f, None);
+    let _ = std::mem::replace(&mut b.opt, None);
+    // replace with default
+    let _ = std::mem::replace(&mut b.val, String::default());
+}
diff --git a/src/tools/clippy/tests/ui/mem_replace.stderr b/src/tools/clippy/tests/ui/mem_replace.stderr
index caa127f76ee..58b57be7507 100644
--- a/src/tools/clippy/tests/ui/mem_replace.stderr
+++ b/src/tools/clippy/tests/ui/mem_replace.stderr
@@ -122,5 +122,29 @@ error: replacing a value of type `T` with `T::default()` is better expressed usi
 LL |     let _ = std::mem::replace(&mut s, String::default());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)`
 
-error: aborting due to 20 previous errors
+error: replacing an `Option` with `None`
+  --> $DIR/mem_replace.rs:121:13
+   |
+LL |     let _ = std::mem::replace(&mut f.0, None);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `f.0.take()`
+
+error: replacing an `Option` with `None`
+  --> $DIR/mem_replace.rs:122:13
+   |
+LL |     let _ = std::mem::replace(&mut *f, None);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `(*f).take()`
+
+error: replacing an `Option` with `None`
+  --> $DIR/mem_replace.rs:123:13
+   |
+LL |     let _ = std::mem::replace(&mut b.opt, None);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `b.opt.take()`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+  --> $DIR/mem_replace.rs:125:13
+   |
+LL |     let _ = std::mem::replace(&mut b.val, String::default());
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut b.val)`
+
+error: aborting due to 24 previous errors
 
diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed
index 0f525dd294c..57c08996ce2 100644
--- a/src/tools/clippy/tests/ui/needless_return.fixed
+++ b/src/tools/clippy/tests/ui/needless_return.fixed
@@ -307,4 +307,13 @@ mod issue10049 {
     }
 }
 
+fn test_match_as_stmt() {
+    let x = 9;
+    match x {
+        1 => 2,
+        2 => return,
+        _ => 0,
+    };
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs
index a1db8375d95..7c1feefbe32 100644
--- a/src/tools/clippy/tests/ui/needless_return.rs
+++ b/src/tools/clippy/tests/ui/needless_return.rs
@@ -317,4 +317,13 @@ mod issue10049 {
     }
 }
 
+fn test_match_as_stmt() {
+    let x = 9;
+    match x {
+        1 => 2,
+        2 => return,
+        _ => 0,
+    };
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/nonminimal_bool.rs b/src/tools/clippy/tests/ui/nonminimal_bool.rs
index 3b5a374b4a7..80cc7c60f56 100644
--- a/src/tools/clippy/tests/ui/nonminimal_bool.rs
+++ b/src/tools/clippy/tests/ui/nonminimal_bool.rs
@@ -92,3 +92,21 @@ fn issue_10523_2() {
     }
     if a!() {}
 }
+
+fn issue_10435() {
+    let x = vec![0];
+    let y = vec![1];
+    let z = vec![2];
+
+    // vvv Should not lint
+    #[allow(clippy::nonminimal_bool)]
+    if !x.is_empty() && !(y.is_empty() || z.is_empty()) {
+        println!("{}", line!());
+    }
+
+    // vvv Should not lint (#10435 talks about a bug where it lints)
+    #[allow(clippy::nonminimal_bool)]
+    if !(x == [0]) {
+        println!("{}", line!());
+    }
+}
diff --git a/src/tools/clippy/tests/ui/print_literal.rs b/src/tools/clippy/tests/ui/print_literal.rs
index 86f908f66b8..538513e9156 100644
--- a/src/tools/clippy/tests/ui/print_literal.rs
+++ b/src/tools/clippy/tests/ui/print_literal.rs
@@ -38,4 +38,8 @@ fn main() {
     // named args shouldn't change anything either
     println!("{foo} {bar}", foo = "hello", bar = "world");
     println!("{bar} {foo}", foo = "hello", bar = "world");
+
+    // The string literal from `file!()` has a callsite span that isn't marked as coming from an
+    // expansion
+    println!("file: {}", file!());
 }
diff --git a/src/tools/clippy/tests/ui/redundant_async_block.fixed b/src/tools/clippy/tests/ui/redundant_async_block.fixed
index d26b7a332cb..ad96993c4a7 100644
--- a/src/tools/clippy/tests/ui/redundant_async_block.fixed
+++ b/src/tools/clippy/tests/ui/redundant_async_block.fixed
@@ -1,6 +1,6 @@
 // run-rustfix
 
-#![allow(unused)]
+#![allow(unused, clippy::manual_async_fn)]
 #![warn(clippy::redundant_async_block)]
 
 use std::future::Future;
@@ -16,40 +16,16 @@ async fn func2() -> String {
     x.await
 }
 
-macro_rules! await_in_macro {
-    ($e:expr) => {
-        std::convert::identity($e).await
-    };
-}
-
-async fn func3(n: usize) -> usize {
-    // Do not lint (suggestion would be `std::convert::identity(func1(n))`
-    // which copies code from inside the macro)
-    async move { await_in_macro!(func1(n)) }.await
-}
-
-// This macro should never be linted as `$e` might contain `.await`
-macro_rules! async_await_parameter_in_macro {
-    ($e:expr) => {
-        async { $e.await }
-    };
-}
-
-// MISSED OPPORTUNITY: this macro could be linted as the `async` block does not
-// contain code coming from the parameters
-macro_rules! async_await_in_macro {
-    ($f:expr) => {
-        ($f)(async { func2().await })
-    };
-}
-
 fn main() {
     let fut1 = async { 17 };
+    // Lint
     let fut2 = fut1;
 
     let fut1 = async { 25 };
+    // Lint
     let fut2 = fut1;
 
+    // Lint
     let fut = async { 42 };
 
     // Do not lint: not a single expression
@@ -60,15 +36,12 @@ fn main() {
 
     // Do not lint: expression contains `.await`
     let fut = async { func1(func2().await.len()).await };
-
-    let fut = async_await_parameter_in_macro!(func2());
-    let fut = async_await_in_macro!(std::convert::identity);
 }
 
 #[allow(clippy::let_and_return)]
 fn capture_local() -> impl Future<Output = i32> {
-    // Lint
     let fut = async { 17 };
+    // Lint
     fut
 }
 
@@ -80,11 +53,39 @@ fn capture_local_closure(s: &str) -> impl Future<Output = &str> {
 
 #[allow(clippy::let_and_return)]
 fn capture_arg(s: &str) -> impl Future<Output = &str> {
-    // Lint
     let fut = async move { s };
+    // Lint
     fut
 }
 
+fn capture_future_arg<T>(f: impl Future<Output = T>) -> impl Future<Output = T> {
+    // Lint
+    f
+}
+
+fn capture_func_result<FN, F, T>(f: FN) -> impl Future<Output = T>
+where
+    F: Future<Output = T>,
+    FN: FnOnce() -> F,
+{
+    // Do not lint, as f() would be evaluated prematurely
+    async { f().await }
+}
+
+fn double_future(f: impl Future<Output = impl Future<Output = u32>>) -> impl Future<Output = u32> {
+    // Do not lint, we will get a `.await` outside a `.async`
+    async { f.await.await }
+}
+
+fn await_in_async<F, R>(f: F) -> impl Future<Output = u32>
+where
+    F: FnOnce() -> R,
+    R: Future<Output = u32>,
+{
+    // Lint
+    async { f().await + 1 }
+}
+
 #[derive(Debug, Clone)]
 struct F {}
 
@@ -109,3 +110,84 @@ fn capture() {
     // Do not lint: `val` would not live long enough
     spawn(async { work(&{ val }).await });
 }
+
+fn await_from_macro() -> impl Future<Output = u32> {
+    macro_rules! mac {
+        ($e:expr) => {
+            $e.await
+        };
+    }
+    // Do not lint: the macro may change in the future
+    // or return different things depending on its argument
+    async { mac!(async { 42 }) }
+}
+
+fn async_expr_from_macro() -> impl Future<Output = u32> {
+    macro_rules! mac {
+        () => {
+            async { 42 }
+        };
+    }
+    // Do not lint: the macro may change in the future
+    async { mac!().await }
+}
+
+fn async_expr_from_macro_deep() -> impl Future<Output = u32> {
+    macro_rules! mac {
+        () => {
+            async { 42 }
+        };
+    }
+    // Do not lint: the macro may change in the future
+    async { ({ mac!() }).await }
+}
+
+fn all_from_macro() -> impl Future<Output = u32> {
+    macro_rules! mac {
+        () => {
+            // Lint
+            async { 42 }
+        };
+    }
+    mac!()
+}
+
+fn parts_from_macro() -> impl Future<Output = u32> {
+    macro_rules! mac {
+        ($e: expr) => {
+            // Do not lint: `$e` might not always be side-effect free
+            async { $e.await }
+        };
+    }
+    mac!(async { 42 })
+}
+
+fn safe_parts_from_macro() -> impl Future<Output = u32> {
+    macro_rules! mac {
+        ($e: expr) => {
+            // Lint
+            async { $e }
+        };
+    }
+    mac!(42)
+}
+
+fn parts_from_macro_deep() -> impl Future<Output = u32> {
+    macro_rules! mac {
+        ($e: expr) => {
+            // Do not lint: `$e` might not always be side-effect free
+            async { ($e,).0.await }
+        };
+    }
+    let f = std::future::ready(42);
+    mac!(f)
+}
+
+fn await_from_macro_deep() -> impl Future<Output = u32> {
+    macro_rules! mac {
+        ($e:expr) => {{ $e }.await};
+    }
+    // Do not lint: the macro may change in the future
+    // or return different things depending on its argument
+    async { mac!(async { 42 }) }
+}
diff --git a/src/tools/clippy/tests/ui/redundant_async_block.rs b/src/tools/clippy/tests/ui/redundant_async_block.rs
index 04726e62805..7ae23558369 100644
--- a/src/tools/clippy/tests/ui/redundant_async_block.rs
+++ b/src/tools/clippy/tests/ui/redundant_async_block.rs
@@ -1,6 +1,6 @@
 // run-rustfix
 
-#![allow(unused)]
+#![allow(unused, clippy::manual_async_fn)]
 #![warn(clippy::redundant_async_block)]
 
 use std::future::Future;
@@ -16,40 +16,16 @@ async fn func2() -> String {
     x.await
 }
 
-macro_rules! await_in_macro {
-    ($e:expr) => {
-        std::convert::identity($e).await
-    };
-}
-
-async fn func3(n: usize) -> usize {
-    // Do not lint (suggestion would be `std::convert::identity(func1(n))`
-    // which copies code from inside the macro)
-    async move { await_in_macro!(func1(n)) }.await
-}
-
-// This macro should never be linted as `$e` might contain `.await`
-macro_rules! async_await_parameter_in_macro {
-    ($e:expr) => {
-        async { $e.await }
-    };
-}
-
-// MISSED OPPORTUNITY: this macro could be linted as the `async` block does not
-// contain code coming from the parameters
-macro_rules! async_await_in_macro {
-    ($f:expr) => {
-        ($f)(async { func2().await })
-    };
-}
-
 fn main() {
     let fut1 = async { 17 };
+    // Lint
     let fut2 = async { fut1.await };
 
     let fut1 = async { 25 };
+    // Lint
     let fut2 = async move { fut1.await };
 
+    // Lint
     let fut = async { async { 42 }.await };
 
     // Do not lint: not a single expression
@@ -60,15 +36,12 @@ fn main() {
 
     // Do not lint: expression contains `.await`
     let fut = async { func1(func2().await.len()).await };
-
-    let fut = async_await_parameter_in_macro!(func2());
-    let fut = async_await_in_macro!(std::convert::identity);
 }
 
 #[allow(clippy::let_and_return)]
 fn capture_local() -> impl Future<Output = i32> {
-    // Lint
     let fut = async { 17 };
+    // Lint
     async move { fut.await }
 }
 
@@ -80,11 +53,39 @@ fn capture_local_closure(s: &str) -> impl Future<Output = &str> {
 
 #[allow(clippy::let_and_return)]
 fn capture_arg(s: &str) -> impl Future<Output = &str> {
-    // Lint
     let fut = async move { s };
+    // Lint
     async move { fut.await }
 }
 
+fn capture_future_arg<T>(f: impl Future<Output = T>) -> impl Future<Output = T> {
+    // Lint
+    async { f.await }
+}
+
+fn capture_func_result<FN, F, T>(f: FN) -> impl Future<Output = T>
+where
+    F: Future<Output = T>,
+    FN: FnOnce() -> F,
+{
+    // Do not lint, as f() would be evaluated prematurely
+    async { f().await }
+}
+
+fn double_future(f: impl Future<Output = impl Future<Output = u32>>) -> impl Future<Output = u32> {
+    // Do not lint, we will get a `.await` outside a `.async`
+    async { f.await.await }
+}
+
+fn await_in_async<F, R>(f: F) -> impl Future<Output = u32>
+where
+    F: FnOnce() -> R,
+    R: Future<Output = u32>,
+{
+    // Lint
+    async { async { f().await + 1 }.await }
+}
+
 #[derive(Debug, Clone)]
 struct F {}
 
@@ -109,3 +110,84 @@ fn capture() {
     // Do not lint: `val` would not live long enough
     spawn(async { work(&{ val }).await });
 }
+
+fn await_from_macro() -> impl Future<Output = u32> {
+    macro_rules! mac {
+        ($e:expr) => {
+            $e.await
+        };
+    }
+    // Do not lint: the macro may change in the future
+    // or return different things depending on its argument
+    async { mac!(async { 42 }) }
+}
+
+fn async_expr_from_macro() -> impl Future<Output = u32> {
+    macro_rules! mac {
+        () => {
+            async { 42 }
+        };
+    }
+    // Do not lint: the macro may change in the future
+    async { mac!().await }
+}
+
+fn async_expr_from_macro_deep() -> impl Future<Output = u32> {
+    macro_rules! mac {
+        () => {
+            async { 42 }
+        };
+    }
+    // Do not lint: the macro may change in the future
+    async { ({ mac!() }).await }
+}
+
+fn all_from_macro() -> impl Future<Output = u32> {
+    macro_rules! mac {
+        () => {
+            // Lint
+            async { async { 42 }.await }
+        };
+    }
+    mac!()
+}
+
+fn parts_from_macro() -> impl Future<Output = u32> {
+    macro_rules! mac {
+        ($e: expr) => {
+            // Do not lint: `$e` might not always be side-effect free
+            async { $e.await }
+        };
+    }
+    mac!(async { 42 })
+}
+
+fn safe_parts_from_macro() -> impl Future<Output = u32> {
+    macro_rules! mac {
+        ($e: expr) => {
+            // Lint
+            async { async { $e }.await }
+        };
+    }
+    mac!(42)
+}
+
+fn parts_from_macro_deep() -> impl Future<Output = u32> {
+    macro_rules! mac {
+        ($e: expr) => {
+            // Do not lint: `$e` might not always be side-effect free
+            async { ($e,).0.await }
+        };
+    }
+    let f = std::future::ready(42);
+    mac!(f)
+}
+
+fn await_from_macro_deep() -> impl Future<Output = u32> {
+    macro_rules! mac {
+        ($e:expr) => {{ $e }.await};
+    }
+    // Do not lint: the macro may change in the future
+    // or return different things depending on its argument
+    async { mac!(async { 42 }) }
+}
diff --git a/src/tools/clippy/tests/ui/redundant_async_block.stderr b/src/tools/clippy/tests/ui/redundant_async_block.stderr
index 1a1c1603e08..f3dcb09b444 100644
--- a/src/tools/clippy/tests/ui/redundant_async_block.stderr
+++ b/src/tools/clippy/tests/ui/redundant_async_block.stderr
@@ -7,34 +7,68 @@ LL |     let x = async { f.await };
    = note: `-D clippy::redundant-async-block` implied by `-D warnings`
 
 error: this async expression only awaits a single future
-  --> $DIR/redundant_async_block.rs:48:16
+  --> $DIR/redundant_async_block.rs:22:16
    |
 LL |     let fut2 = async { fut1.await };
    |                ^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `fut1`
 
 error: this async expression only awaits a single future
-  --> $DIR/redundant_async_block.rs:51:16
+  --> $DIR/redundant_async_block.rs:26:16
    |
 LL |     let fut2 = async move { fut1.await };
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `fut1`
 
 error: this async expression only awaits a single future
-  --> $DIR/redundant_async_block.rs:53:15
+  --> $DIR/redundant_async_block.rs:29:15
    |
 LL |     let fut = async { async { 42 }.await };
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `async { 42 }`
 
 error: this async expression only awaits a single future
-  --> $DIR/redundant_async_block.rs:72:5
+  --> $DIR/redundant_async_block.rs:45:5
    |
 LL |     async move { fut.await }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `fut`
 
 error: this async expression only awaits a single future
-  --> $DIR/redundant_async_block.rs:85:5
+  --> $DIR/redundant_async_block.rs:58:5
    |
 LL |     async move { fut.await }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `fut`
 
-error: aborting due to 6 previous errors
+error: this async expression only awaits a single future
+  --> $DIR/redundant_async_block.rs:63:5
+   |
+LL |     async { f.await }
+   |     ^^^^^^^^^^^^^^^^^ help: you can reduce it to: `f`
+
+error: this async expression only awaits a single future
+  --> $DIR/redundant_async_block.rs:86:5
+   |
+LL |     async { async { f().await + 1 }.await }
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `async { f().await + 1 }`
+
+error: this async expression only awaits a single future
+  --> $DIR/redundant_async_block.rs:149:13
+   |
+LL |             async { async { 42 }.await }
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `async { 42 }`
+...
+LL |     mac!()
+   |     ------ in this macro invocation
+   |
+   = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: this async expression only awaits a single future
+  --> $DIR/redundant_async_block.rs:169:13
+   |
+LL |             async { async { $e }.await }
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `async { $e }`
+...
+LL |     mac!(42)
+   |     -------- in this macro invocation
+   |
+   = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 10 previous errors
 
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports.fixed b/src/tools/clippy/tests/ui/single_component_path_imports.fixed
index 4c40739d6f5..8c96c4715d3 100644
--- a/src/tools/clippy/tests/ui/single_component_path_imports.fixed
+++ b/src/tools/clippy/tests/ui/single_component_path_imports.fixed
@@ -2,9 +2,11 @@
 #![warn(clippy::single_component_path_imports)]
 #![allow(unused_imports)]
 
+use core;
 
 use serde as edres;
 pub use serde;
+use std;
 
 macro_rules! m {
     () => {
@@ -17,6 +19,10 @@ fn main() {
 
     // False positive #5154, shouldn't trigger lint.
     m!();
+
+    // False positive #10549
+    let _ = self::std::io::stdout();
+    let _ = 0 as self::core::ffi::c_uint;
 }
 
 mod hello_mod {
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports.rs b/src/tools/clippy/tests/ui/single_component_path_imports.rs
index 9280bab3c71..8434bf7eaf1 100644
--- a/src/tools/clippy/tests/ui/single_component_path_imports.rs
+++ b/src/tools/clippy/tests/ui/single_component_path_imports.rs
@@ -2,9 +2,11 @@
 #![warn(clippy::single_component_path_imports)]
 #![allow(unused_imports)]
 
+use core;
 use regex;
 use serde as edres;
 pub use serde;
+use std;
 
 macro_rules! m {
     () => {
@@ -17,6 +19,10 @@ fn main() {
 
     // False positive #5154, shouldn't trigger lint.
     m!();
+
+    // False positive #10549
+    let _ = self::std::io::stdout();
+    let _ = 0 as self::core::ffi::c_uint;
 }
 
 mod hello_mod {
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports.stderr b/src/tools/clippy/tests/ui/single_component_path_imports.stderr
index 71dcc25d6e5..d69a86470a5 100644
--- a/src/tools/clippy/tests/ui/single_component_path_imports.stderr
+++ b/src/tools/clippy/tests/ui/single_component_path_imports.stderr
@@ -1,5 +1,5 @@
 error: this import is redundant
-  --> $DIR/single_component_path_imports.rs:5:1
+  --> $DIR/single_component_path_imports.rs:6:1
    |
 LL | use regex;
    | ^^^^^^^^^^ help: remove it entirely
@@ -7,7 +7,7 @@ LL | use regex;
    = note: `-D clippy::single-component-path-imports` implied by `-D warnings`
 
 error: this import is redundant
-  --> $DIR/single_component_path_imports.rs:23:5
+  --> $DIR/single_component_path_imports.rs:29:5
    |
 LL |     use regex;
    |     ^^^^^^^^^^ help: remove it entirely
diff --git a/src/tools/clippy/tests/ui/suspicious_doc_comments.fixed b/src/tools/clippy/tests/ui/suspicious_doc_comments.fixed
new file mode 100644
index 00000000000..b404df94d3c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/suspicious_doc_comments.fixed
@@ -0,0 +1,81 @@
+// run-rustfix
+#![allow(unused)]
+#![warn(clippy::suspicious_doc_comments)]
+
+//! Real module documentation.
+//! Fake module documentation.
+fn baz() {}
+
+pub mod singleline_outer_doc {
+    //! This module contains useful functions.
+
+    pub fn bar() {}
+}
+
+pub mod singleline_inner_doc {
+    //! This module contains useful functions.
+
+    pub fn bar() {}
+}
+
+pub mod multiline_outer_doc {
+    /*! This module contains useful functions.
+     */
+
+    pub fn bar() {}
+}
+
+pub mod multiline_inner_doc {
+    /*! This module contains useful functions.
+     */
+
+    pub fn bar() {}
+}
+
+pub mod multiline_outer_doc2 {
+    //! This module
+    //! contains
+    //! useful functions.
+
+    pub fn bar() {}
+}
+
+pub mod multiline_outer_doc3 {
+    //! a
+    //! b
+
+    /// c
+    pub fn bar() {}
+}
+
+pub mod multiline_outer_doc4 {
+    //! a
+    /// b
+    pub fn bar() {}
+}
+
+pub mod multiline_outer_doc_gap {
+    //! a
+
+    //! b
+    pub fn bar() {}
+}
+
+pub mod multiline_outer_doc_commented {
+    /////! This outer doc comment was commented out.
+    pub fn bar() {}
+}
+
+pub mod outer_doc_macro {
+    //! Very cool macro
+    macro_rules! x {
+        () => {};
+    }
+}
+
+pub mod useless_outer_doc {
+    //! Huh.
+    use std::mem;
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/suspicious_doc_comments.rs b/src/tools/clippy/tests/ui/suspicious_doc_comments.rs
new file mode 100644
index 00000000000..46eff51e220
--- /dev/null
+++ b/src/tools/clippy/tests/ui/suspicious_doc_comments.rs
@@ -0,0 +1,81 @@
+// run-rustfix
+#![allow(unused)]
+#![warn(clippy::suspicious_doc_comments)]
+
+//! Real module documentation.
+///! Fake module documentation.
+fn baz() {}
+
+pub mod singleline_outer_doc {
+    ///! This module contains useful functions.
+
+    pub fn bar() {}
+}
+
+pub mod singleline_inner_doc {
+    //! This module contains useful functions.
+
+    pub fn bar() {}
+}
+
+pub mod multiline_outer_doc {
+    /**! This module contains useful functions.
+     */
+
+    pub fn bar() {}
+}
+
+pub mod multiline_inner_doc {
+    /*! This module contains useful functions.
+     */
+
+    pub fn bar() {}
+}
+
+pub mod multiline_outer_doc2 {
+    ///! This module
+    ///! contains
+    ///! useful functions.
+
+    pub fn bar() {}
+}
+
+pub mod multiline_outer_doc3 {
+    ///! a
+    ///! b
+
+    /// c
+    pub fn bar() {}
+}
+
+pub mod multiline_outer_doc4 {
+    ///! a
+    /// b
+    pub fn bar() {}
+}
+
+pub mod multiline_outer_doc_gap {
+    ///! a
+
+    ///! b
+    pub fn bar() {}
+}
+
+pub mod multiline_outer_doc_commented {
+    /////! This outer doc comment was commented out.
+    pub fn bar() {}
+}
+
+pub mod outer_doc_macro {
+    ///! Very cool macro
+    macro_rules! x {
+        () => {};
+    }
+}
+
+pub mod useless_outer_doc {
+    ///! Huh.
+    use std::mem;
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/suspicious_doc_comments.stderr b/src/tools/clippy/tests/ui/suspicious_doc_comments.stderr
new file mode 100644
index 00000000000..6c167df2787
--- /dev/null
+++ b/src/tools/clippy/tests/ui/suspicious_doc_comments.stderr
@@ -0,0 +1,114 @@
+error: this is an outer doc comment and does not apply to the parent module or crate
+  --> $DIR/suspicious_doc_comments.rs:6:1
+   |
+LL | ///! Fake module documentation.
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::suspicious-doc-comments` implied by `-D warnings`
+help: use an inner doc comment to document the parent module or crate
+   |
+LL | //! Fake module documentation.
+   |
+
+error: this is an outer doc comment and does not apply to the parent module or crate
+  --> $DIR/suspicious_doc_comments.rs:10:5
+   |
+LL |     ///! This module contains useful functions.
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use an inner doc comment to document the parent module or crate
+   |
+LL |     //! This module contains useful functions.
+   |
+
+error: this is an outer doc comment and does not apply to the parent module or crate
+  --> $DIR/suspicious_doc_comments.rs:22:5
+   |
+LL | /     /**! This module contains useful functions.
+LL | |      */
+   | |_______^
+   |
+help: use an inner doc comment to document the parent module or crate
+   |
+LL ~     /*! This module contains useful functions.
+LL +      */
+   |
+
+error: this is an outer doc comment and does not apply to the parent module or crate
+  --> $DIR/suspicious_doc_comments.rs:36:5
+   |
+LL | /     ///! This module
+LL | |     ///! contains
+LL | |     ///! useful functions.
+   | |__________________________^
+   |
+help: use an inner doc comment to document the parent module or crate
+   |
+LL ~     //! This module
+LL ~     //! contains
+LL ~     //! useful functions.
+   |
+
+error: this is an outer doc comment and does not apply to the parent module or crate
+  --> $DIR/suspicious_doc_comments.rs:44:5
+   |
+LL | /     ///! a
+LL | |     ///! b
+   | |__________^
+   |
+help: use an inner doc comment to document the parent module or crate
+   |
+LL ~     //! a
+LL ~     //! b
+   |
+
+error: this is an outer doc comment and does not apply to the parent module or crate
+  --> $DIR/suspicious_doc_comments.rs:52:5
+   |
+LL |     ///! a
+   |     ^^^^^^
+   |
+help: use an inner doc comment to document the parent module or crate
+   |
+LL |     //! a
+   |
+
+error: this is an outer doc comment and does not apply to the parent module or crate
+  --> $DIR/suspicious_doc_comments.rs:58:5
+   |
+LL | /     ///! a
+LL | |
+LL | |     ///! b
+   | |__________^
+   |
+help: use an inner doc comment to document the parent module or crate
+   |
+LL ~     //! a
+LL | 
+LL ~     //! b
+   |
+
+error: this is an outer doc comment and does not apply to the parent module or crate
+  --> $DIR/suspicious_doc_comments.rs:70:5
+   |
+LL |     ///! Very cool macro
+   |     ^^^^^^^^^^^^^^^^^^^^
+   |
+help: use an inner doc comment to document the parent module or crate
+   |
+LL |     //! Very cool macro
+   |
+
+error: this is an outer doc comment and does not apply to the parent module or crate
+  --> $DIR/suspicious_doc_comments.rs:77:5
+   |
+LL |     ///! Huh.
+   |     ^^^^^^^^^
+   |
+help: use an inner doc comment to document the parent module or crate
+   |
+LL |     //! Huh.
+   |
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/suspicious_doc_comments_unfixable.rs b/src/tools/clippy/tests/ui/suspicious_doc_comments_unfixable.rs
new file mode 100644
index 00000000000..ad98c7f4966
--- /dev/null
+++ b/src/tools/clippy/tests/ui/suspicious_doc_comments_unfixable.rs
@@ -0,0 +1,16 @@
+#![allow(unused)]
+#![warn(clippy::suspicious_doc_comments)]
+
+///! a
+///! b
+/// c
+///! d
+pub fn foo() {}
+
+///! a
+///! b
+/// c
+///! d
+use std::mem;
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/suspicious_doc_comments_unfixable.stderr b/src/tools/clippy/tests/ui/suspicious_doc_comments_unfixable.stderr
new file mode 100644
index 00000000000..f89146dad36
--- /dev/null
+++ b/src/tools/clippy/tests/ui/suspicious_doc_comments_unfixable.stderr
@@ -0,0 +1,37 @@
+error: this is an outer doc comment and does not apply to the parent module or crate
+  --> $DIR/suspicious_doc_comments_unfixable.rs:4:1
+   |
+LL | / ///! a
+LL | | ///! b
+LL | | /// c
+LL | | ///! d
+   | |______^
+   |
+   = note: `-D clippy::suspicious-doc-comments` implied by `-D warnings`
+help: use an inner doc comment to document the parent module or crate
+   |
+LL + //! a
+LL + //! b
+LL | /// c
+LL + //! d
+   |
+
+error: this is an outer doc comment and does not apply to the parent module or crate
+  --> $DIR/suspicious_doc_comments_unfixable.rs:10:1
+   |
+LL | / ///! a
+LL | | ///! b
+LL | | /// c
+LL | | ///! d
+   | |______^
+   |
+help: use an inner doc comment to document the parent module or crate
+   |
+LL + //! a
+LL + //! b
+LL | /// c
+LL + //! d
+   |
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/tests_outside_test_module.rs b/src/tools/clippy/tests/ui/tests_outside_test_module.rs
new file mode 100644
index 00000000000..1982b1d0107
--- /dev/null
+++ b/src/tools/clippy/tests/ui/tests_outside_test_module.rs
@@ -0,0 +1,18 @@
+// compile-flags: --test
+#![allow(unused)]
+#![warn(clippy::tests_outside_test_module)]
+
+fn main() {
+    // test code goes here
+}
+
+// Should lint
+#[test]
+fn my_test() {}
+
+#[cfg(test)]
+mod tests {
+    // Should not lint
+    #[test]
+    fn my_test() {}
+}
diff --git a/src/tools/clippy/tests/ui/tests_outside_test_module.stderr b/src/tools/clippy/tests/ui/tests_outside_test_module.stderr
new file mode 100644
index 00000000000..125a79d6edf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/tests_outside_test_module.stderr
@@ -0,0 +1,11 @@
+error: this function marked with #[test] is outside a #[cfg(test)] module
+  --> $DIR/tests_outside_test_module.rs:11:1
+   |
+LL | fn my_test() {}
+   | ^^^^^^^^^^^^^^^
+   |
+   = note: move it to a testing module marked with #[cfg(test)]
+   = note: `-D clippy::tests-outside-test-module` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed
index 55307506eb3..cc84ba25bd0 100644
--- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed
+++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed
@@ -4,7 +4,7 @@
 // would otherwise be responsible for
 #![warn(clippy::useless_transmute)]
 #![warn(clippy::transmute_ptr_to_ptr)]
-#![allow(dead_code, unused_unsafe, clippy::borrow_as_ptr)]
+#![allow(unused, clippy::borrow_as_ptr)]
 
 use std::mem::{size_of, transmute};
 
@@ -77,3 +77,9 @@ fn cannot_be_expressed_as_pointer_cast(in_param: Single) -> Pair {
 
     unsafe { transmute::<Single, Pair>(in_param) }
 }
+
+fn issue_10449() {
+    fn f() {}
+
+    let _x: u8 = unsafe { *(f as *const u8) };
+}
diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs
index e7360f3f9dc..aa65ab4dd24 100644
--- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs
+++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs
@@ -4,7 +4,7 @@
 // would otherwise be responsible for
 #![warn(clippy::useless_transmute)]
 #![warn(clippy::transmute_ptr_to_ptr)]
-#![allow(dead_code, unused_unsafe, clippy::borrow_as_ptr)]
+#![allow(unused, clippy::borrow_as_ptr)]
 
 use std::mem::{size_of, transmute};
 
@@ -77,3 +77,9 @@ fn cannot_be_expressed_as_pointer_cast(in_param: Single) -> Pair {
 
     unsafe { transmute::<Single, Pair>(in_param) }
 }
+
+fn issue_10449() {
+    fn f() {}
+
+    let _x: u8 = unsafe { *std::mem::transmute::<fn(), *const u8>(f) };
+}
diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr
index e862fcb67a4..58f5162c78e 100644
--- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr
+++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr
@@ -58,5 +58,11 @@ error: transmute from a reference to a pointer
 LL |     unsafe { transmute::<&[i32; 1], *const u8>(in_param) }
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `in_param as *const [i32; 1] as *const u8`
 
-error: aborting due to 9 previous errors
+error: transmute from `fn()` to `*const u8` which could be expressed as a pointer cast instead
+  --> $DIR/transmutes_expressible_as_ptr_casts.rs:84:28
+   |
+LL |     let _x: u8 = unsafe { *std::mem::transmute::<fn(), *const u8>(f) };
+   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(f as *const u8)`
+
+error: aborting due to 10 previous errors
 
diff --git a/src/tools/clippy/tests/ui/uninit.rs b/src/tools/clippy/tests/ui/uninit.rs
index 412b36b4ee8..c996de89422 100644
--- a/src/tools/clippy/tests/ui/uninit.rs
+++ b/src/tools/clippy/tests/ui/uninit.rs
@@ -1,7 +1,7 @@
 #![feature(stmt_expr_attributes)]
 #![allow(clippy::let_unit_value, invalid_value)]
 
-use std::mem::{self, MaybeUninit};
+use std::mem::MaybeUninit;
 
 union MyOwnMaybeUninit {
     value: u8,
@@ -30,12 +30,24 @@ fn main() {
     let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() };
 
     // Was a false negative.
-    let _: usize = unsafe { mem::MaybeUninit::uninit().assume_init() };
+    let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
 
     polymorphic::<()>();
+    polymorphic_maybe_uninit_array::<10>();
+    polymorphic_maybe_uninit::<u8>();
 
     fn polymorphic<T>() {
         // We are conservative around polymorphic types.
-        let _: T = unsafe { mem::MaybeUninit::uninit().assume_init() };
+        let _: T = unsafe { MaybeUninit::uninit().assume_init() };
+    }
+
+    fn polymorphic_maybe_uninit_array<const N: usize>() {
+        // While the type is polymorphic, MaybeUninit<u8> is not.
+        let _: [MaybeUninit<u8>; N] = unsafe { MaybeUninit::uninit().assume_init() };
+    }
+
+    fn polymorphic_maybe_uninit<T>() {
+        // The entire type is polymorphic, but it's wrapped in a MaybeUninit.
+        let _: MaybeUninit<T> = unsafe { MaybeUninit::uninit().assume_init() };
     }
 }
diff --git a/src/tools/clippy/tests/ui/uninit.stderr b/src/tools/clippy/tests/ui/uninit.stderr
index 9e01b9a4aa8..248de56da76 100644
--- a/src/tools/clippy/tests/ui/uninit.stderr
+++ b/src/tools/clippy/tests/ui/uninit.stderr
@@ -9,14 +9,14 @@ LL |     let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
 error: this call for this type may be undefined behavior
   --> $DIR/uninit.rs:33:29
    |
-LL |     let _: usize = unsafe { mem::MaybeUninit::uninit().assume_init() };
-   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this call for this type may be undefined behavior
-  --> $DIR/uninit.rs:39:29
+  --> $DIR/uninit.rs:41:29
    |
-LL |         let _: T = unsafe { mem::MaybeUninit::uninit().assume_init() };
-   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |         let _: T = unsafe { MaybeUninit::uninit().assume_init() };
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/tests/ui/uninit_vec.rs b/src/tools/clippy/tests/ui/uninit_vec.rs
index 59ec64a7ab1..79effc82fdf 100644
--- a/src/tools/clippy/tests/ui/uninit_vec.rs
+++ b/src/tools/clippy/tests/ui/uninit_vec.rs
@@ -124,4 +124,12 @@ fn main() {
             vec.set_len(10);
         }
     }
+
+    fn poly_maybe_uninit<T>() {
+        // We are conservative around polymorphic types.
+        let mut vec: Vec<MaybeUninit<T>> = Vec::with_capacity(1000);
+        unsafe {
+            vec.set_len(10);
+        }
+    }
 }
diff --git a/src/tools/clippy/tests/ui/uninlined_format_args.fixed b/src/tools/clippy/tests/ui/uninlined_format_args.fixed
index 1475d781c67..3122081a44f 100644
--- a/src/tools/clippy/tests/ui/uninlined_format_args.fixed
+++ b/src/tools/clippy/tests/ui/uninlined_format_args.fixed
@@ -1,7 +1,7 @@
 // aux-build:proc_macros.rs
 // run-rustfix
 #![warn(clippy::uninlined_format_args)]
-#![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)]
+#![allow(named_arguments_used_positionally, unused)]
 #![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)]
 
 extern crate proc_macros;
@@ -119,7 +119,7 @@ fn tester(fn_arg: i32) {
     println!("Width = {local_i32}, value with width = {local_f64:local_i32$}");
     println!("{local_i32:width$.prec$}");
     println!("{width:width$.prec$}");
-    println!("{}", format!("{local_i32}"));
+    println!("{}", format!("{}", local_i32));
     my_println!("{}", local_i32);
     my_println_args!("{}", local_i32);
 
@@ -178,3 +178,87 @@ fn _meets_msrv() {
 fn _do_not_fire() {
     println!("{:?}", None::<()>);
 }
+
+macro_rules! _internal {
+    ($($args:tt)*) => {
+        println!("{}", format_args!($($args)*))
+    };
+}
+
+macro_rules! my_println2 {
+   ($target:expr, $($args:tt)+) => {{
+       if $target {
+           _internal!($($args)+)
+       }
+    }};
+}
+
+macro_rules! my_println2_args {
+    ($target:expr, $($args:tt)+) => {{
+       if $target {
+           _internal!("foo: {}", format_args!($($args)+))
+       }
+    }};
+}
+
+macro_rules! my_concat {
+    ($fmt:literal $(, $e:expr)*) => {
+        println!(concat!("ERROR: ", $fmt), $($e,)*)
+    }
+}
+
+macro_rules! my_good_macro {
+    ($fmt:literal $(, $e:expr)* $(,)?) => {
+        println!($fmt $(, $e)*)
+    }
+}
+
+macro_rules! my_bad_macro {
+    ($fmt:literal, $($e:expr),*) => {
+        println!($fmt, $($e,)*)
+    }
+}
+
+macro_rules! my_bad_macro2 {
+    ($fmt:literal) => {
+        let s = $fmt.clone();
+        println!("{}", s);
+    };
+    ($fmt:literal, $($e:expr)+) => {
+        println!($fmt, $($e,)*)
+    };
+}
+
+// This abomination was suggested by @Alexendoo, may the Rust gods have mercy on their soul...
+// https://github.com/rust-lang/rust-clippy/pull/9948#issuecomment-1327965962
+macro_rules! used_twice {
+    (
+        large = $large:literal,
+        small = $small:literal,
+        $val:expr,
+    ) => {
+        if $val < 5 {
+            println!($small, $val);
+        } else {
+            println!($large, $val);
+        }
+    };
+}
+
+fn tester2() {
+    let local_i32 = 1;
+    my_println2_args!(true, "{}", local_i32);
+    my_println2!(true, "{}", local_i32);
+    my_concat!("{}", local_i32);
+    my_good_macro!("{}", local_i32);
+    my_good_macro!("{}", local_i32,);
+
+    // FIXME: Broken false positives, currently unhandled
+    my_bad_macro!("{}", local_i32);
+    my_bad_macro2!("{}", local_i32);
+    used_twice! {
+        large = "large value: {}",
+        small = "small value: {}",
+        local_i32,
+    };
+}
diff --git a/src/tools/clippy/tests/ui/uninlined_format_args.rs b/src/tools/clippy/tests/ui/uninlined_format_args.rs
index 835afac393f..b153ef256e0 100644
--- a/src/tools/clippy/tests/ui/uninlined_format_args.rs
+++ b/src/tools/clippy/tests/ui/uninlined_format_args.rs
@@ -1,7 +1,7 @@
 // aux-build:proc_macros.rs
 // run-rustfix
 #![warn(clippy::uninlined_format_args)]
-#![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)]
+#![allow(named_arguments_used_positionally, unused)]
 #![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)]
 
 extern crate proc_macros;
@@ -183,3 +183,87 @@ fn _meets_msrv() {
 fn _do_not_fire() {
     println!("{:?}", None::<()>);
 }
+
+macro_rules! _internal {
+    ($($args:tt)*) => {
+        println!("{}", format_args!($($args)*))
+    };
+}
+
+macro_rules! my_println2 {
+   ($target:expr, $($args:tt)+) => {{
+       if $target {
+           _internal!($($args)+)
+       }
+    }};
+}
+
+macro_rules! my_println2_args {
+    ($target:expr, $($args:tt)+) => {{
+       if $target {
+           _internal!("foo: {}", format_args!($($args)+))
+       }
+    }};
+}
+
+macro_rules! my_concat {
+    ($fmt:literal $(, $e:expr)*) => {
+        println!(concat!("ERROR: ", $fmt), $($e,)*)
+    }
+}
+
+macro_rules! my_good_macro {
+    ($fmt:literal $(, $e:expr)* $(,)?) => {
+        println!($fmt $(, $e)*)
+    }
+}
+
+macro_rules! my_bad_macro {
+    ($fmt:literal, $($e:expr),*) => {
+        println!($fmt, $($e,)*)
+    }
+}
+
+macro_rules! my_bad_macro2 {
+    ($fmt:literal) => {
+        let s = $fmt.clone();
+        println!("{}", s);
+    };
+    ($fmt:literal, $($e:expr)+) => {
+        println!($fmt, $($e,)*)
+    };
+}
+
+// This abomination was suggested by @Alexendoo, may the Rust gods have mercy on their soul...
+// https://github.com/rust-lang/rust-clippy/pull/9948#issuecomment-1327965962
+macro_rules! used_twice {
+    (
+        large = $large:literal,
+        small = $small:literal,
+        $val:expr,
+    ) => {
+        if $val < 5 {
+            println!($small, $val);
+        } else {
+            println!($large, $val);
+        }
+    };
+}
+
+fn tester2() {
+    let local_i32 = 1;
+    my_println2_args!(true, "{}", local_i32);
+    my_println2!(true, "{}", local_i32);
+    my_concat!("{}", local_i32);
+    my_good_macro!("{}", local_i32);
+    my_good_macro!("{}", local_i32,);
+
+    // FIXME: Broken false positives, currently unhandled
+    my_bad_macro!("{}", local_i32);
+    my_bad_macro2!("{}", local_i32);
+    used_twice! {
+        large = "large value: {}",
+        small = "small value: {}",
+        local_i32,
+    };
+}
diff --git a/src/tools/clippy/tests/ui/uninlined_format_args.stderr b/src/tools/clippy/tests/ui/uninlined_format_args.stderr
index a12abf8bef8..dc4af6ef42e 100644
--- a/src/tools/clippy/tests/ui/uninlined_format_args.stderr
+++ b/src/tools/clippy/tests/ui/uninlined_format_args.stderr
@@ -775,18 +775,6 @@ LL +     println!("{width:width$.prec$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:125:20
-   |
-LL |     println!("{}", format!("{}", local_i32));
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: change this to
-   |
-LL -     println!("{}", format!("{}", local_i32));
-LL +     println!("{}", format!("{local_i32}"));
-   |
-
-error: variables can be used directly in the `format!` string
   --> $DIR/uninlined_format_args.rs:143:5
    |
 LL | /     println!(
@@ -856,5 +844,5 @@ LL -     println!("expand='{}'", local_i32);
 LL +     println!("expand='{local_i32}'");
    |
 
-error: aborting due to 72 previous errors
+error: aborting due to 71 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_box_returns.rs b/src/tools/clippy/tests/ui/unnecessary_box_returns.rs
new file mode 100644
index 00000000000..fe60d929759
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_box_returns.rs
@@ -0,0 +1,60 @@
+#![warn(clippy::unnecessary_box_returns)]
+
+trait Bar {
+    // lint
+    fn baz(&self) -> Box<usize>;
+}
+
+pub struct Foo {}
+
+impl Bar for Foo {
+    // don't lint: this is a problem with the trait, not the implementation
+    fn baz(&self) -> Box<usize> {
+        Box::new(42)
+    }
+}
+
+impl Foo {
+    fn baz(&self) -> Box<usize> {
+        // lint
+        Box::new(13)
+    }
+}
+
+// lint
+fn bxed_usize() -> Box<usize> {
+    Box::new(5)
+}
+
+// lint
+fn _bxed_foo() -> Box<Foo> {
+    Box::new(Foo {})
+}
+
+// don't lint: this is exported
+pub fn bxed_foo() -> Box<Foo> {
+    Box::new(Foo {})
+}
+
+// don't lint: str is unsized
+fn bxed_str() -> Box<str> {
+    "Hello, world!".to_string().into_boxed_str()
+}
+
+// don't lint: function contains the word, "box"
+fn boxed_usize() -> Box<usize> {
+    Box::new(7)
+}
+
+// don't lint: this has an unspecified return type
+fn default() {}
+
+// don't lint: this doesn't return a Box
+fn string() -> String {
+    String::from("Hello, world")
+}
+
+fn main() {
+    // don't lint: this is a closure
+    let a = || -> Box<usize> { Box::new(5) };
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_box_returns.stderr b/src/tools/clippy/tests/ui/unnecessary_box_returns.stderr
new file mode 100644
index 00000000000..b17512c10a1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_box_returns.stderr
@@ -0,0 +1,35 @@
+error: boxed return of the sized type `usize`
+  --> $DIR/unnecessary_box_returns.rs:5:22
+   |
+LL |     fn baz(&self) -> Box<usize>;
+   |                      ^^^^^^^^^^ help: try: `usize`
+   |
+   = help: changing this also requires a change to the return expressions in this function
+   = note: `-D clippy::unnecessary-box-returns` implied by `-D warnings`
+
+error: boxed return of the sized type `usize`
+  --> $DIR/unnecessary_box_returns.rs:18:22
+   |
+LL |     fn baz(&self) -> Box<usize> {
+   |                      ^^^^^^^^^^ help: try: `usize`
+   |
+   = help: changing this also requires a change to the return expressions in this function
+
+error: boxed return of the sized type `usize`
+  --> $DIR/unnecessary_box_returns.rs:25:20
+   |
+LL | fn bxed_usize() -> Box<usize> {
+   |                    ^^^^^^^^^^ help: try: `usize`
+   |
+   = help: changing this also requires a change to the return expressions in this function
+
+error: boxed return of the sized type `Foo`
+  --> $DIR/unnecessary_box_returns.rs:30:19
+   |
+LL | fn _bxed_foo() -> Box<Foo> {
+   |                   ^^^^^^^^ help: try: `Foo`
+   |
+   = help: changing this also requires a change to the return expressions in this function
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unused_format_specs.fixed b/src/tools/clippy/tests/ui/unused_format_specs.fixed
deleted file mode 100644
index 2930722b42d..00000000000
--- a/src/tools/clippy/tests/ui/unused_format_specs.fixed
+++ /dev/null
@@ -1,18 +0,0 @@
-// run-rustfix
-
-#![warn(clippy::unused_format_specs)]
-#![allow(unused)]
-
-fn main() {
-    let f = 1.0f64;
-    println!("{}", 1.0);
-    println!("{f} {f:?}");
-
-    println!("{}", 1);
-}
-
-fn should_not_lint() {
-    let f = 1.0f64;
-    println!("{:.1}", 1.0);
-    println!("{f:.w$} {f:.*?}", 3, w = 2);
-}
diff --git a/src/tools/clippy/tests/ui/unused_format_specs.rs b/src/tools/clippy/tests/ui/unused_format_specs.rs
deleted file mode 100644
index ee192a000d4..00000000000
--- a/src/tools/clippy/tests/ui/unused_format_specs.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-// run-rustfix
-
-#![warn(clippy::unused_format_specs)]
-#![allow(unused)]
-
-fn main() {
-    let f = 1.0f64;
-    println!("{:.}", 1.0);
-    println!("{f:.} {f:.?}");
-
-    println!("{:.}", 1);
-}
-
-fn should_not_lint() {
-    let f = 1.0f64;
-    println!("{:.1}", 1.0);
-    println!("{f:.w$} {f:.*?}", 3, w = 2);
-}
diff --git a/src/tools/clippy/tests/ui/unused_format_specs.stderr b/src/tools/clippy/tests/ui/unused_format_specs.stderr
deleted file mode 100644
index 7231c17e74c..00000000000
--- a/src/tools/clippy/tests/ui/unused_format_specs.stderr
+++ /dev/null
@@ -1,54 +0,0 @@
-error: empty precision specifier has no effect
-  --> $DIR/unused_format_specs.rs:8:17
-   |
-LL |     println!("{:.}", 1.0);
-   |                 ^
-   |
-   = note: a precision specifier is not required to format floats
-   = note: `-D clippy::unused-format-specs` implied by `-D warnings`
-help: remove the `.`
-   |
-LL -     println!("{:.}", 1.0);
-LL +     println!("{}", 1.0);
-   |
-
-error: empty precision specifier has no effect
-  --> $DIR/unused_format_specs.rs:9:18
-   |
-LL |     println!("{f:.} {f:.?}");
-   |                  ^
-   |
-   = note: a precision specifier is not required to format floats
-help: remove the `.`
-   |
-LL -     println!("{f:.} {f:.?}");
-LL +     println!("{f} {f:.?}");
-   |
-
-error: empty precision specifier has no effect
-  --> $DIR/unused_format_specs.rs:9:24
-   |
-LL |     println!("{f:.} {f:.?}");
-   |                        ^
-   |
-   = note: a precision specifier is not required to format floats
-help: remove the `.`
-   |
-LL -     println!("{f:.} {f:.?}");
-LL +     println!("{f:.} {f:?}");
-   |
-
-error: empty precision specifier has no effect
-  --> $DIR/unused_format_specs.rs:11:17
-   |
-LL |     println!("{:.}", 1);
-   |                 ^
-   |
-help: remove the `.`
-   |
-LL -     println!("{:.}", 1);
-LL +     println!("{}", 1);
-   |
-
-error: aborting due to 4 previous errors
-
diff --git a/src/tools/clippy/tests/ui/unused_format_specs_unfixable.stderr b/src/tools/clippy/tests/ui/unused_format_specs_unfixable.stderr
index 9f1890282e6..cb7156b6baf 100644
--- a/src/tools/clippy/tests/ui/unused_format_specs_unfixable.stderr
+++ b/src/tools/clippy/tests/ui/unused_format_specs_unfixable.stderr
@@ -37,11 +37,7 @@ error: format specifiers have no effect on `format_args!()`
 LL |     println!("{:5}.", format_args_from_macro!());
    |               ^^^^
    |
-help: for the width to apply consider using `format!()`
-  --> $DIR/unused_format_specs_unfixable.rs:16:17
-   |
-LL |     println!("{:5}.", format_args_from_macro!());
-   |                 ^
+   = help: for the width to apply consider using `format!()`
 help: if the current behavior is intentional, remove the format specifiers
    |
 LL -     println!("{:5}.", format_args_from_macro!());
@@ -54,11 +50,7 @@ error: format specifiers have no effect on `format_args!()`
 LL |     println!("{args:5}");
    |               ^^^^^^^^
    |
-help: for the width to apply consider using `format!()`
-  --> $DIR/unused_format_specs_unfixable.rs:19:21
-   |
-LL |     println!("{args:5}");
-   |                     ^
+   = help: for the width to apply consider using `format!()`
 help: if the current behavior is intentional, remove the format specifiers
    |
 LL -     println!("{args:5}");
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index 98b27a5c6b6..d2f494942cf 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -313,7 +313,8 @@ pub struct Config {
     pub cflags: String,
     pub cxxflags: String,
     pub ar: String,
-    pub linker: Option<String>,
+    pub target_linker: Option<String>,
+    pub host_linker: Option<String>,
     pub llvm_components: String,
 
     /// Path to a NodeJS executable. Used for JS doctests, emscripten and WASM tests
diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs
index cfb1ee34f67..6a91d25a824 100644
--- a/src/tools/compiletest/src/main.rs
+++ b/src/tools/compiletest/src/main.rs
@@ -134,7 +134,8 @@ pub fn parse_config(args: Vec<String>) -> Config {
         .reqopt("", "cflags", "flags for the C compiler", "FLAGS")
         .reqopt("", "cxxflags", "flags for the CXX compiler", "FLAGS")
         .optopt("", "ar", "path to an archiver", "PATH")
-        .optopt("", "linker", "path to a linker", "PATH")
+        .optopt("", "target-linker", "path to a linker for the target", "PATH")
+        .optopt("", "host-linker", "path to a linker for the host", "PATH")
         .reqopt("", "llvm-components", "list of LLVM components built in", "LIST")
         .optopt("", "llvm-bin-dir", "Path to LLVM's `bin` directory", "PATH")
         .optopt("", "nodejs", "the name of nodejs", "PATH")
@@ -307,7 +308,8 @@ pub fn parse_config(args: Vec<String>) -> Config {
         cflags: matches.opt_str("cflags").unwrap(),
         cxxflags: matches.opt_str("cxxflags").unwrap(),
         ar: matches.opt_str("ar").unwrap_or_else(|| String::from("ar")),
-        linker: matches.opt_str("linker"),
+        target_linker: matches.opt_str("target-linker"),
+        host_linker: matches.opt_str("host-linker"),
         llvm_components: matches.opt_str("llvm-components").unwrap(),
         nodejs: matches.opt_str("nodejs"),
         npm: matches.opt_str("npm"),
@@ -350,7 +352,8 @@ pub fn log_config(config: &Config) {
     logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir));
     logv(c, format!("adb_device_status: {}", config.adb_device_status));
     logv(c, format!("ar: {}", config.ar));
-    logv(c, format!("linker: {:?}", config.linker));
+    logv(c, format!("target-linker: {:?}", config.target_linker));
+    logv(c, format!("host-linker: {:?}", config.host_linker));
     logv(c, format!("verbose: {}", config.verbose));
     logv(c, format!("format: {:?}", config.format));
     logv(c, "\n".to_string());
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index e55c82c4b63..0fa5c54ae8e 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -1570,7 +1570,7 @@ impl<'test> TestCx<'test> {
             rustdoc.arg("--output-format").arg("json").arg("-Zunstable-options");
         }
 
-        if let Some(ref linker) = self.config.linker {
+        if let Some(ref linker) = self.config.target_linker {
             rustdoc.arg(format!("-Clinker={}", linker));
         }
 
@@ -2083,10 +2083,15 @@ impl<'test> TestCx<'test> {
 
         if self.props.force_host {
             self.maybe_add_external_args(&mut rustc, &self.config.host_rustcflags);
+            if !is_rustdoc {
+                if let Some(ref linker) = self.config.host_linker {
+                    rustc.arg(format!("-Clinker={}", linker));
+                }
+            }
         } else {
             self.maybe_add_external_args(&mut rustc, &self.config.target_rustcflags);
             if !is_rustdoc {
-                if let Some(ref linker) = self.config.linker {
+                if let Some(ref linker) = self.config.target_linker {
                     rustc.arg(format!("-Clinker={}", linker));
                 }
             }
@@ -3039,7 +3044,7 @@ impl<'test> TestCx<'test> {
             cmd.env("NODE", node);
         }
 
-        if let Some(ref linker) = self.config.linker {
+        if let Some(ref linker) = self.config.target_linker {
             cmd.env("RUSTC_LINKER", linker);
         }
 
diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml
index b71f48e4644..da1c2f770ac 100644
--- a/src/tools/miri/.github/workflows/ci.yml
+++ b/src/tools/miri/.github/workflows/ci.yml
@@ -174,7 +174,12 @@ jobs:
           ~/.local/bin/zulip-send --stream miri --subject "Cron Job Failure (miri, $(date -u +%Y-%m))" \
             --message 'Dear @*T-miri*,
 
-          It would appear that the Miri cron job build failed. Would you mind investigating this issue?
+          It would appear that the [Miri cron job build]('"https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"') failed.
+          
+          This likely means that rustc changed the miri directory and
+          we now need to do a [`./miri rustc-pull`](https://github.com/rust-lang/miri/blob/master/CONTRIBUTING.md#importing-changes-from-the-rustc-repo).
+
+          Would you mind investigating this issue?
 
           Thanks in advance!
           Sincerely,
diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock
index d17bb9533b4..46deebf2cdd 100644
--- a/src/tools/miri/Cargo.lock
+++ b/src/tools/miri/Cargo.lock
@@ -282,9 +282,9 @@ checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
 
 [[package]]
 name = "libffi"
-version = "3.0.1"
+version = "3.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e454b3efb16fba3b17810ae5e41df02b649e564ab3c5a34b3b93ed07ad287e6"
+checksum = "ce826c243048e3d5cec441799724de52e2d42f820468431fc3fceee2341871e2"
 dependencies = [
  "libc",
  "libffi-sys",
@@ -292,9 +292,9 @@ dependencies = [
 
 [[package]]
 name = "libffi-sys"
-version = "2.0.1"
+version = "2.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84e78d02e5a8eae9c24c38ce6e6026f80e16dff76adcdae4bc5c6c52c2de4a60"
+checksum = "dc65067b78c0fc069771e8b9a9e02df71e08858bec92c1f101377c67b9dca7c7"
 dependencies = [
  "cc",
 ]
diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md
index b70f7e0e556..4c735187987 100644
--- a/src/tools/miri/README.md
+++ b/src/tools/miri/README.md
@@ -15,7 +15,7 @@ for example:
   or an invalid enum discriminant)
 * **Experimental**: Violations of the [Stacked Borrows] rules governing aliasing
   for reference types
-* **Experimental**: Violations of the Tree Borrows aliasing rules, as an optional
+* **Experimental**: Violations of the [Tree Borrows] aliasing rules, as an optional
   alternative to [Stacked Borrows]
 * **Experimental**: Data races
 
@@ -79,6 +79,7 @@ behavior** in your program, and cannot run all programs:
 [`unreachable_unchecked`]: https://doc.rust-lang.org/stable/std/hint/fn.unreachable_unchecked.html
 [`copy_nonoverlapping`]: https://doc.rust-lang.org/stable/std/ptr/fn.copy_nonoverlapping.html
 [Stacked Borrows]: https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md
+[Tree Borrows]: https://perso.crans.org/vanille/treebor/
 
 
 ## Using Miri
@@ -359,7 +360,7 @@ to Miri failing to detect cases of undefined behavior in a program.
 * `-Zmiri-disable-data-race-detector` disables checking for data races.  Using
   this flag is **unsound**. This implies `-Zmiri-disable-weak-memory-emulation`.
 * `-Zmiri-disable-stacked-borrows` disables checking the experimental
-  aliasing rules to track borrows ([Stacked Borrows] and Tree Borrows).
+  aliasing rules to track borrows ([Stacked Borrows] and [Tree Borrows]).
   This can make Miri run faster, but it also means no aliasing violations will
   be detected. Using this flag is **unsound** (but the affected soundness rules
   are experimental). Later flags take precedence: borrow tracking can be reactivated
@@ -425,7 +426,7 @@ to Miri failing to detect cases of undefined behavior in a program.
 * `-Zmiri-track-weak-memory-loads` shows a backtrace when weak memory emulation returns an outdated
   value from a load. This can help diagnose problems that disappear under
   `-Zmiri-disable-weak-memory-emulation`.
-* `-Zmiri-tree-borrows` replaces [Stacked Borrows] with the Tree Borrows rules.
+* `-Zmiri-tree-borrows` replaces [Stacked Borrows] with the [Tree Borrows] rules.
   The soundness rules are already experimental without this flag, but even more
   so with this flag.
 * `-Zmiri-force-page-size=<num>` overrides the default page size for an architecture, in multiples of 1k.
@@ -442,7 +443,7 @@ Some native rustc `-Z` flags are also very relevant for Miri:
   functions.  This is needed so that Miri can execute such functions, so Miri
   sets this flag per default.
 * `-Zmir-emit-retag` controls whether `Retag` statements are emitted. Miri
-  enables this per default because it is needed for [Stacked Borrows] and Tree Borrows.
+  enables this per default because it is needed for [Stacked Borrows] and [Tree Borrows].
 
 Moreover, Miri recognizes some environment variables:
 
diff --git a/src/tools/miri/ci.sh b/src/tools/miri/ci.sh
index ef52a37fe31..b5b3b211b05 100755
--- a/src/tools/miri/ci.sh
+++ b/src/tools/miri/ci.sh
@@ -43,7 +43,9 @@ function run_tests {
     # optimizations up all the way, too).
     # Optimizations change diagnostics (mostly backtraces), so we don't check
     # them. Also error locations change so we don't run the failing tests.
-    MIRIFLAGS="${MIRIFLAGS:-} -O -Zmir-opt-level=4" MIRI_SKIP_UI_CHECKS=1 ./miri test -- tests/{pass,panic}
+    # We explicitly enable debug-assertions here, they are disabled by -O but we have tests
+    # which exist to check that we panic on debug assertion failures.
+    MIRIFLAGS="${MIRIFLAGS:-} -O -Zmir-opt-level=4 -Cdebug-assertions=yes" MIRI_SKIP_UI_CHECKS=1 ./miri test -- tests/{pass,panic}
 
     # Also run some many-seeds tests. 64 seeds means this takes around a minute per test.
     for FILE in tests/many-seeds/*.rs; do
diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version
index 18c2561242a..f1ed3be2edd 100644
--- a/src/tools/miri/rust-version
+++ b/src/tools/miri/rust-version
@@ -1 +1 @@
-511364e7874dba9649a264100407e4bffe7b5425
+d4be8efc6296bace5b1e165f1b34d3c6da76aa8e
diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs
index 0aea105ccc4..26a7ead2407 100644
--- a/src/tools/miri/src/bin/miri.rs
+++ b/src/tools/miri/src/bin/miri.rs
@@ -28,9 +28,11 @@ use rustc_middle::{
     middle::exported_symbols::{
         ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel,
     },
-    ty::{query::ExternProviders, TyCtxt},
     query::LocalCrate,
+    ty::{query::ExternProviders, TyCtxt},
 };
+use rustc_session::config::OptLevel;
+
 use rustc_session::{config::CrateType, search_paths::PathKind, CtfeBacktrace};
 
 use miri::{BacktraceStyle, BorrowTrackerMethod, ProvenanceMode, RetagFields};
@@ -83,6 +85,21 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
                 env::set_current_dir(cwd).unwrap();
             }
 
+            if tcx.sess.opts.optimize != OptLevel::No {
+                tcx.sess.warn("Miri does not support optimizations. If you have enabled optimizations \
+                    by selecting a Cargo profile (such as --release) which changes other profile settings \
+                    such as whether debug assertions and overflow checks are enabled, those settings are \
+                    still applied.");
+            }
+            if tcx.sess.mir_opt_level() > 0 {
+                tcx.sess.warn("You have explicitly enabled MIR optimizations, overriding Miri's default \
+                    which is to completely disable them. Any optimizations may hide UB that Miri would \
+                    otherwise detect, and it is not necessarily possible to predict what kind of UB will \
+                    be missed. If you are enabling optimizations to make Miri run faster, we advise using \
+                    cfg(miri) to shrink your workload instead. The performance benefit of enabling MIR \
+                    optimizations is usually marginal at best.");
+            }
+
             if let Some(return_code) = miri::eval_entry(tcx, entry_def_id, entry_type, config) {
                 std::process::exit(
                     i32::try_from(return_code).expect("Return value was too large!"),
diff --git a/src/tools/miri/src/clock.rs b/src/tools/miri/src/clock.rs
index 3f33273e1e5..24bf90f104f 100644
--- a/src/tools/miri/src/clock.rs
+++ b/src/tools/miri/src/clock.rs
@@ -3,7 +3,10 @@ use std::time::{Duration, Instant as StdInstant};
 
 /// When using a virtual clock, this defines how many nanoseconds we pretend are passing for each
 /// basic block.
-const NANOSECONDS_PER_BASIC_BLOCK: u64 = 10;
+/// This number is pretty random, but it has been shown to approximately cause
+/// some sample programs to run within an order of magnitude of real time on desktop CPUs.
+/// (See `tests/pass/shims/time-with-isolation*.rs`.)
+const NANOSECONDS_PER_BASIC_BLOCK: u64 = 5000;
 
 #[derive(Debug)]
 pub struct Instant {
diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs
index fb4e59acd00..5c8aba6d441 100644
--- a/src/tools/miri/src/lib.rs
+++ b/src/tools/miri/src/lib.rs
@@ -124,11 +124,13 @@ pub use crate::tag_gc::{EvalContextExt as _, VisitTags};
 
 /// Insert rustc arguments at the beginning of the argument list that Miri wants to be
 /// set per default, for maximal validation power.
+/// Also disable the MIR pass that inserts an alignment check on every pointer dereference. Miri
+/// does that too, and with a better error message.
 pub const MIRI_DEFAULT_ARGS: &[&str] = &[
+    "--cfg=miri",
     "-Zalways-encode-mir",
+    "-Zextra-const-ub-checks",
     "-Zmir-emit-retag",
     "-Zmir-opt-level=0",
-    "--cfg=miri",
-    "-Cdebug-assertions=on",
-    "-Zextra-const-ub-checks",
+    "-Zmir-enable-passes=-CheckAlignment",
 ];
diff --git a/src/tools/miri/src/shims/panic.rs b/src/tools/miri/src/shims/panic.rs
index 2cca2f3f391..18ae01a19f9 100644
--- a/src/tools/miri/src/shims/panic.rs
+++ b/src/tools/miri/src/shims/panic.rs
@@ -157,7 +157,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 &[catch_unwind.data.into(), payload.into()],
                 None,
                 // Directly return to caller of `try`.
-                StackPopCleanup::Goto { ret: Some(catch_unwind.ret), unwind: mir::UnwindAction::Continue },
+                StackPopCleanup::Goto {
+                    ret: Some(catch_unwind.ret),
+                    unwind: mir::UnwindAction::Continue,
+                },
             )?;
 
             // We pushed a new stack frame, the engine should not do any jumping now!
@@ -211,10 +214,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                     Abi::Rust,
                     &[index.into(), len.into()],
                     None,
-                    StackPopCleanup::Goto {
-                        ret: None,
-                        unwind,
-                    },
+                    StackPopCleanup::Goto { ret: None, unwind },
                 )?;
             }
             MisalignedPointerDereference { required, found } => {
@@ -235,19 +235,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                     Abi::Rust,
                     &[required.into(), found.into()],
                     None,
-                    StackPopCleanup::Goto {
-                        ret: None,
-                        unwind,
-                    },
+                    StackPopCleanup::Goto { ret: None, unwind },
                 )?;
             }
 
             _ => {
                 // Forward everything else to `panic` lang item.
-                this.start_panic(
-                    msg.description(),
-                    unwind,
-                )?;
+                this.start_panic(msg.description(), unwind)?;
             }
         }
         Ok(())
diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs
index 1eca389e984..de271548217 100644
--- a/src/tools/miri/src/shims/unix/fs.rs
+++ b/src/tools/miri/src/shims/unix/fs.rs
@@ -628,13 +628,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         let fd = this.read_scalar(&args[0])?.to_i32()?;
         let cmd = this.read_scalar(&args[1])?.to_i32()?;
 
-        // Reject if isolation is enabled.
-        if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
-            this.reject_in_isolation("`fcntl`", reject_with)?;
-            this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?;
-            return Ok(-1);
-        }
-
         // We only support getting the flags for a descriptor.
         if cmd == this.eval_libc_i32("F_GETFD") {
             // Currently this is the only flag that `F_GETFD` returns. It is OK to just return the
@@ -677,6 +670,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 None => this.handle_not_found(),
             }
         } else if this.tcx.sess.target.os == "macos" && cmd == this.eval_libc_i32("F_FULLFSYNC") {
+            // Reject if isolation is enabled.
+            if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
+                this.reject_in_isolation("`fcntl`", reject_with)?;
+                this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?;
+                return Ok(-1);
+            }
+
             if let Some(file_descriptor) = this.machine.file_handler.handles.get(&fd) {
                 // FIXME: Support fullfsync for all FDs
                 let FileHandle { file, writable } =
diff --git a/src/tools/miri/tests/fail/terminate-terminator.stderr b/src/tools/miri/tests/fail/terminate-terminator.stderr
index 3befd83007b..c046678f73f 100644
--- a/src/tools/miri/tests/fail/terminate-terminator.stderr
+++ b/src/tools/miri/tests/fail/terminate-terminator.stderr
@@ -1,3 +1,5 @@
+warning: You have explicitly enabled MIR optimizations, overriding Miri's default which is to completely disable them. Any optimizations may hide UB that Miri would otherwise detect, and it is not necessarily possible to predict what kind of UB will be missed. If you are enabling optimizations to make Miri run faster, we advise using cfg(miri) to shrink your workload instead. The performance benefit of enabling MIR optimizations is usually marginal at best.
+
 thread 'main' panicked at 'explicit panic', $DIR/terminate-terminator.rs:LL:CC
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
 error: abnormal termination: panic in a function that cannot unwind
@@ -23,5 +25,5 @@ LL |     panic_abort();
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
-error: aborting due to previous error
+error: aborting due to previous error; 1 warning emitted
 
diff --git a/src/tools/miri/tests/panic/alignment-assertion.rs b/src/tools/miri/tests/panic/alignment-assertion.rs
deleted file mode 100644
index 68aa19a88db..00000000000
--- a/src/tools/miri/tests/panic/alignment-assertion.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-//@compile-flags: -Zmiri-disable-alignment-check -Cdebug-assertions=yes
-
-fn main() {
-    let mut x = [0u32; 2];
-    let ptr: *mut u8 = x.as_mut_ptr().cast::<u8>();
-    unsafe {
-        *(ptr.add(1).cast::<u32>()) = 42;
-    }
-}
diff --git a/src/tools/miri/tests/panic/alignment-assertion.stderr b/src/tools/miri/tests/panic/alignment-assertion.stderr
deleted file mode 100644
index 26cf51b0cd2..00000000000
--- a/src/tools/miri/tests/panic/alignment-assertion.stderr
+++ /dev/null
@@ -1,2 +0,0 @@
-thread 'main' panicked at 'misaligned pointer dereference: address must be a multiple of 0x4 but is $HEX', $DIR/alignment-assertion.rs:LL:CC
-note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
diff --git a/src/tools/miri/tests/pass-dep/shims/fcntl_f-fullfsync_apple.rs b/src/tools/miri/tests/pass-dep/shims/fcntl_f-fullfsync_apple.rs
new file mode 100644
index 00000000000..307906f2583
--- /dev/null
+++ b/src/tools/miri/tests/pass-dep/shims/fcntl_f-fullfsync_apple.rs
@@ -0,0 +1,12 @@
+//@only-target-apple: F_FULLFSYNC only on apple systems
+//@compile-flags: -Zmiri-isolation-error=warn-nobacktrace
+
+use std::io::Error;
+
+fn main() {
+    // test `fcntl(F_FULLFSYNC)`
+    unsafe {
+        assert_eq!(libc::fcntl(1, libc::F_FULLFSYNC, 0), -1);
+        assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EPERM));
+    }
+}
diff --git a/src/tools/miri/tests/pass-dep/shims/fcntl_f-fullfsync_apple.stderr b/src/tools/miri/tests/pass-dep/shims/fcntl_f-fullfsync_apple.stderr
new file mode 100644
index 00000000000..09a24e1e5d7
--- /dev/null
+++ b/src/tools/miri/tests/pass-dep/shims/fcntl_f-fullfsync_apple.stderr
@@ -0,0 +1,2 @@
+warning: `fcntl` was made to return an error due to isolation
+
diff --git a/src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.rs b/src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.rs
index d6d19a3fe81..5185db0b0e2 100644
--- a/src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.rs
+++ b/src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.rs
@@ -7,10 +7,9 @@ use std::fs;
 use std::io::{Error, ErrorKind};
 
 fn main() {
-    // test `fcntl`
+    // test `fcntl(F_DUPFD): should work even with isolation.`
     unsafe {
-        assert_eq!(libc::fcntl(1, libc::F_DUPFD, 0), -1);
-        assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EPERM));
+        assert!(libc::fcntl(1, libc::F_DUPFD, 0) >= 0);
     }
 
     // test `readlink`
diff --git a/src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.stderr b/src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.stderr
index 21fcb65243e..b0cadfb970b 100644
--- a/src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.stderr
+++ b/src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.stderr
@@ -1,5 +1,3 @@
-warning: `fcntl` was made to return an error due to isolation
-
 warning: `readlink` was made to return an error due to isolation
 
 warning: `$STAT` was made to return an error due to isolation
diff --git a/src/tools/miri/tests/pass-dep/tokio/sleep.rs b/src/tools/miri/tests/pass-dep/tokio/sleep.rs
index 1341484dda4..00cc68eba3e 100644
--- a/src/tools/miri/tests/pass-dep/tokio/sleep.rs
+++ b/src/tools/miri/tests/pass-dep/tokio/sleep.rs
@@ -1,4 +1,4 @@
-//@compile-flags: -Zmiri-disable-isolation -Zmiri-permissive-provenance -Zmiri-backtrace=full
+//@compile-flags: -Zmiri-permissive-provenance -Zmiri-backtrace=full
 //@only-target-x86_64-unknown-linux: support for tokio only on linux and x86
 
 use tokio::time::{sleep, Duration, Instant};
@@ -7,8 +7,6 @@ use tokio::time::{sleep, Duration, Instant};
 async fn main() {
     let start = Instant::now();
     sleep(Duration::from_secs(1)).await;
-    // It takes 96 millisecond to sleep for 1 millisecond
-    // It takes 1025 millisecond to sleep for 1 second
     let time_elapsed = &start.elapsed().as_millis();
-    assert!(time_elapsed > &1000, "{}", time_elapsed);
+    assert!((1000..1100).contains(time_elapsed), "{}", time_elapsed);
 }
diff --git a/src/tools/miri/tests/pass-dep/tokio/tokio_mvp.rs b/src/tools/miri/tests/pass-dep/tokio/tokio_mvp.rs
index 0bca7cc069a..0ed2a941bc4 100644
--- a/src/tools/miri/tests/pass-dep/tokio/tokio_mvp.rs
+++ b/src/tools/miri/tests/pass-dep/tokio/tokio_mvp.rs
@@ -1,5 +1,5 @@
 // Need to disable preemption to stay on the supported MVP codepath in mio.
-//@compile-flags: -Zmiri-disable-isolation -Zmiri-permissive-provenance
+//@compile-flags: -Zmiri-permissive-provenance
 //@only-target-x86_64-unknown-linux: support for tokio exists only on linux and x86
 
 #[tokio::main]
diff --git a/src/tools/miri/tests/pass/concurrency/thread_park_isolated.rs b/src/tools/miri/tests/pass/concurrency/thread_park_isolated.rs
index bf004012e84..7852d495e28 100644
--- a/src/tools/miri/tests/pass/concurrency/thread_park_isolated.rs
+++ b/src/tools/miri/tests/pass/concurrency/thread_park_isolated.rs
@@ -7,6 +7,6 @@ fn main() {
 
     thread::park_timeout(Duration::from_millis(200));
 
-    // Thanks to deterministic execution, this will wiat *exactly* 200ms (rounded to 1ms).
-    assert!((200..201).contains(&start.elapsed().as_millis()));
+    // Thanks to deterministic execution, this will wait *exactly* 200ms, plus the time for the surrounding code.
+    assert!((200..210).contains(&start.elapsed().as_millis()), "{}", start.elapsed().as_millis());
 }
diff --git a/src/tools/miri/tests/pass/shims/time-with-isolation.rs b/src/tools/miri/tests/pass/shims/time-with-isolation.rs
index b6444319b59..a0c3c6bbaa9 100644
--- a/src/tools/miri/tests/pass/shims/time-with-isolation.rs
+++ b/src/tools/miri/tests/pass/shims/time-with-isolation.rs
@@ -22,14 +22,23 @@ fn test_time_passes() {
     let diff = now2.duration_since(now1);
     assert_eq!(now1 + diff, now2);
     assert_eq!(now2 - diff, now1);
-    // The virtual clock is deterministic and I got 29us on a 64-bit Linux machine. However, this
+    // The virtual clock is deterministic and I got 15ms on a 64-bit Linux machine. However, this
     // changes according to the platform so we use an interval to be safe. This should be updated
     // if `NANOSECONDS_PER_BASIC_BLOCK` changes.
-    assert!(diff.as_micros() > 10);
-    assert!(diff.as_micros() < 40);
+    assert!(diff.as_millis() > 5);
+    assert!(diff.as_millis() < 20);
+}
+
+fn test_block_for_one_second() {
+    let end = Instant::now() + Duration::from_secs(1);
+    // This takes a long time, as we only increment when statements are executed.
+    // When we sleep on all threads, we fast forward to the sleep duration, which we can't
+    // do with busy waiting.
+    while Instant::now() < end {}
 }
 
 fn main() {
     test_time_passes();
+    test_block_for_one_second();
     test_sleep();
 }
diff --git a/src/tools/miri/tests/pass/shims/time-with-isolation2.rs b/src/tools/miri/tests/pass/shims/time-with-isolation2.rs
new file mode 100644
index 00000000000..24e72e22fd8
--- /dev/null
+++ b/src/tools/miri/tests/pass/shims/time-with-isolation2.rs
@@ -0,0 +1,8 @@
+use std::time::Instant;
+
+fn main() {
+    let begin = Instant::now();
+    for _ in 0..100_000 {}
+    let time = begin.elapsed();
+    println!("The loop took around {}s", time.as_secs());
+}
diff --git a/src/tools/miri/tests/pass/shims/time-with-isolation2.stdout b/src/tools/miri/tests/pass/shims/time-with-isolation2.stdout
new file mode 100644
index 00000000000..641e469f50c
--- /dev/null
+++ b/src/tools/miri/tests/pass/shims/time-with-isolation2.stdout
@@ -0,0 +1 @@
+The loop took around 13s