diff --git a/.cargo/config.toml b/.cargo/config.toml
index 688473f2f9b..f3dd9275a42 100644
--- a/.cargo/config.toml
+++ b/.cargo/config.toml
@@ -2,9 +2,12 @@
 uitest = "test --test compile-test"
 dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --"
 lintcheck = "run --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml  -- "
-collect-metadata = "test --test dogfood --features metadata-collector-lint -- run_metadata_collection_lint --ignored"
+collect-metadata = "test --test dogfood --features internal -- run_metadata_collection_lint --ignored"
 
 [build]
 # -Zbinary-dep-depinfo allows us to track which rlib files to use for compiling UI tests
 rustflags = ["-Zunstable-options", "-Zbinary-dep-depinfo"]
 target-dir = "target"
+
+[unstable]
+binary-dep-depinfo = true
diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml
index 3d8c39408a9..116ae031bb7 100644
--- a/.github/workflows/clippy.yml
+++ b/.github/workflows/clippy.yml
@@ -49,17 +49,17 @@ jobs:
         echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV
 
     - name: Build
-      run: cargo build --features deny-warnings,internal-lints,metadata-collector-lint
+      run: cargo build --features deny-warnings,internal
 
     - name: Test
-      run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint
+      run: cargo test --features deny-warnings,internal
 
     - name: Test clippy_lints
-      run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint
+      run: cargo test --features deny-warnings,internal
       working-directory: clippy_lints
 
     - name: Test clippy_utils
-      run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint
+      run: cargo test --features deny-warnings,internal
       working-directory: clippy_utils
 
     - name: Test rustc_tools_util
@@ -70,14 +70,6 @@ jobs:
       run: cargo test --features deny-warnings
       working-directory: clippy_dev
 
-    - name: Test cargo-clippy
-      run: ../target/debug/cargo-clippy
-      working-directory: clippy_workspace_tests
-
-    - name: Test cargo-clippy --fix
-      run: ../target/debug/cargo-clippy clippy --fix
-      working-directory: clippy_workspace_tests
-
     - name: Test clippy-driver
       run: bash .github/driver.sh
       env:
diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml
index 8b644aa2817..989667037c1 100644
--- a/.github/workflows/clippy_bors.yml
+++ b/.github/workflows/clippy_bors.yml
@@ -112,17 +112,22 @@ jobs:
         echo "$SYSROOT/bin" >> $GITHUB_PATH
 
     - name: Build
-      run: cargo build --features deny-warnings,internal-lints,metadata-collector-lint
+      run: cargo build --features deny-warnings,internal
 
     - name: Test
-      run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint
+      if: runner.os == 'Linux'
+      run: cargo test --features deny-warnings,internal
+
+    - name: Test
+      if: runner.os != 'Linux'
+      run: cargo test --features deny-warnings,internal -- --skip dogfood
 
     - name: Test clippy_lints
-      run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint
+      run: cargo test --features deny-warnings,internal
       working-directory: clippy_lints
 
     - name: Test clippy_utils
-      run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint
+      run: cargo test --features deny-warnings,internal
       working-directory: clippy_utils
 
     - name: Test rustc_tools_util
@@ -133,14 +138,6 @@ jobs:
       run: cargo test --features deny-warnings
       working-directory: clippy_dev
 
-    - name: Test cargo-clippy
-      run: ../target/debug/cargo-clippy
-      working-directory: clippy_workspace_tests
-
-    - name: Test cargo-clippy --fix
-      run: ../target/debug/cargo-clippy clippy --fix
-      working-directory: clippy_workspace_tests
-
     - name: Test clippy-driver
       run: bash .github/driver.sh
       env:
diff --git a/.gitignore b/.gitignore
index e82a0ec4765..3e50c45a9b6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,7 +19,6 @@ out
 /target
 /clippy_lints/target
 /clippy_utils/target
-/clippy_workspace_tests/target
 /clippy_dev/target
 /lintcheck/target
 /rustc_tools_util/target
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 27bac4718b6..8f4da9a3827 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2887,6 +2887,7 @@ Released 2018-09-13
 [`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions
 [`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
 [`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison
+[`borrow_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr
 [`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const
 [`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box
 [`box_collection`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_collection
@@ -3070,6 +3071,7 @@ Released 2018-09-13
 [`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
 [`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert
 [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn
+[`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits
 [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map
 [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map
 [`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten
@@ -3253,6 +3255,7 @@ Released 2018-09-13
 [`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait
 [`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names
 [`single_char_add_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_add_str
+[`single_char_lifetime_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_lifetime_names
 [`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern
 [`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports
 [`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop
diff --git a/Cargo.toml b/Cargo.toml
index 8661a867758..e445889a58f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy"
-version = "0.1.59"
+version = "0.1.60"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
@@ -47,7 +47,9 @@ itertools = "0.10"
 quote = "1.0"
 serde = { version = "1.0", features = ["derive"] }
 syn = { version = "1.0", features = ["full"] }
+futures = "0.3"
 parking_lot = "0.11.2"
+tokio = { version = "1", features = ["io-util"] }
 
 [build-dependencies]
 rustc_tools_util = { version = "0.2", path = "rustc_tools_util" }
@@ -55,8 +57,7 @@ rustc_tools_util = { version = "0.2", path = "rustc_tools_util" }
 [features]
 deny-warnings = ["clippy_lints/deny-warnings"]
 integration = ["tempfile"]
-internal-lints = ["clippy_lints/internal-lints"]
-metadata-collector-lint = ["internal-lints", "clippy_lints/metadata-collector-lint"]
+internal = ["clippy_lints/internal"]
 
 [package.metadata.rust-analyzer]
 # This package uses #[feature(rustc_private)]
diff --git a/README.md b/README.md
index 1bbd89e7822..f001a42d917 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
 
 A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
 
-[There are over 450 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
+[There are over 500 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
 
 Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html).
 You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.
@@ -37,8 +37,8 @@ Table of contents:
 
 ## Usage
 
-Below are instructions on how to use Clippy as a subcommand, compiled from source
-or in Travis CI.
+Below are instructions on how to use Clippy as a cargo subcommand,
+in projects that do not use cargo, or in Travis CI.
 
 ### As a cargo subcommand (`cargo clippy`)
 
@@ -98,22 +98,18 @@ If you want to run Clippy **only** on the given crate, use the `--no-deps` optio
 cargo clippy -p example -- --no-deps
 ```
 
-### As a rustc replacement (`clippy-driver`)
+### Using `clippy-driver`
 
-Clippy can also be used in projects that do not use cargo. To do so, you will need to replace
-your `rustc` compilation commands with `clippy-driver`. For example, if your project runs:
-
-```terminal
-rustc --edition 2018 -Cpanic=abort foo.rs
-```
-
-Then, to enable Clippy, you will need to call:
+Clippy can also be used in projects that do not use cargo. To do so, run `clippy-driver`
+with the same arguments you use for `rustc`. For example:
 
 ```terminal
 clippy-driver --edition 2018 -Cpanic=abort foo.rs
 ```
 
-Note that `rustc` will still run, i.e. it will still emit the output files it normally does.
+Note that `clippy-driver` is designed for running Clippy only and should not be used as a general
+replacement for `rustc`. `clippy-driver` may produce artifacts that are not optimized as expected,
+for example.
 
 ### Travis CI
 
diff --git a/clippy_dev/src/fmt.rs b/clippy_dev/src/fmt.rs
index 9ceadee58ea..d513a229b7e 100644
--- a/clippy_dev/src/fmt.rs
+++ b/clippy_dev/src/fmt.rs
@@ -3,7 +3,7 @@ use itertools::Itertools;
 use shell_escape::escape;
 use std::ffi::{OsStr, OsString};
 use std::path::Path;
-use std::process::{self, Command};
+use std::process::{self, Command, Stdio};
 use std::{fs, io};
 use walkdir::WalkDir;
 
@@ -31,6 +31,7 @@ impl From<walkdir::Error> for CliError {
 struct FmtContext {
     check: bool,
     verbose: bool,
+    rustfmt_path: String,
 }
 
 // the "main" function of cargo dev fmt
@@ -102,7 +103,23 @@ Please revert the changes to Cargo.tomls first."
         }
     }
 
-    let context = FmtContext { check, verbose };
+    let output = Command::new("rustup")
+        .args(["which", "rustfmt"])
+        .stderr(Stdio::inherit())
+        .output()
+        .expect("error running `rustup which rustfmt`");
+    if !output.status.success() {
+        eprintln!("`rustup which rustfmt` did not execute successfully");
+        process::exit(1);
+    }
+    let mut rustfmt_path = String::from_utf8(output.stdout).expect("invalid rustfmt path");
+    rustfmt_path.truncate(rustfmt_path.trim_end().len());
+
+    let context = FmtContext {
+        check,
+        verbose,
+        rustfmt_path,
+    };
     let result = try_run(&context);
     let code = match result {
         Ok(true) => 0,
@@ -141,8 +158,12 @@ fn exec(
         println!("{}", format_command(&program, &dir, args));
     }
 
-    let child = Command::new(&program).current_dir(&dir).args(args.iter()).spawn()?;
-    let output = child.wait_with_output()?;
+    let output = Command::new(&program)
+        .env("RUSTFMT", &context.rustfmt_path)
+        .current_dir(&dir)
+        .args(args.iter())
+        .output()
+        .unwrap();
     let success = output.status.success();
 
     if !context.check && !success {
@@ -159,7 +180,6 @@ fn exec(
 fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> {
     let mut args = vec!["fmt", "--all"];
     if context.check {
-        args.push("--");
         args.push("--check");
     }
     let success = exec(context, "cargo", path, &args)?;
@@ -200,7 +220,7 @@ fn rustfmt(context: &FmtContext, paths: impl Iterator<Item = OsString>) -> Resul
     }
     args.extend(paths);
 
-    let success = exec(context, "rustfmt", std::env::current_dir()?, &args)?;
+    let success = exec(context, &context.rustfmt_path, std::env::current_dir()?, &args)?;
 
     Ok(success)
 }
diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs
index 8dd073ef405..d368ef1f46a 100644
--- a/clippy_dev/src/update_lints.rs
+++ b/clippy_dev/src/update_lints.rs
@@ -321,7 +321,7 @@ fn gen_register_lint_list<'a>(
 
     for (is_public, module_name, lint_name) in details {
         if !is_public {
-            output.push_str("    #[cfg(feature = \"internal-lints\")]\n");
+            output.push_str("    #[cfg(feature = \"internal\")]\n");
         }
         output.push_str(&format!("    {}::{},\n", module_name, lint_name));
     }
diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml
index 7d2a3e4f639..2053ca64ba2 100644
--- a/clippy_lints/Cargo.toml
+++ b/clippy_lints/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_lints"
-version = "0.1.59"
+version = "0.1.60"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
@@ -30,8 +30,7 @@ url = { version = "2.2", features = ["serde"] }
 [features]
 deny-warnings = ["clippy_utils/deny-warnings"]
 # build clippy with internal lints enabled, off by default
-internal-lints = ["clippy_utils/internal-lints"]
-metadata-collector-lint = ["serde_json", "clippy_utils/metadata-collector-lint"]
+internal = ["clippy_utils/internal", "serde_json"]
 
 [package.metadata.rust-analyzer]
 # This crate uses #[feature(rustc_private)]
diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs
index b7f414742f1..c82837746bd 100644
--- a/clippy_lints/src/assertions_on_constants.rs
+++ b/clippy_lints/src/assertions_on_constants.rs
@@ -1,12 +1,10 @@
 use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::higher;
-use clippy_utils::source::snippet_opt;
-use clippy_utils::{is_direct_expn_of, is_expn_of, match_panic_call, peel_blocks};
-use if_chain::if_chain;
-use rustc_hir::{Expr, ExprKind, UnOp};
+use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn};
+use rustc_hir::Expr;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -36,107 +34,39 @@ declare_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]);
 
 impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
-        let lint_true = |is_debug: bool| {
+        let Some(macro_call) = root_macro_call_first_node(cx, e) else { return };
+        let is_debug = match cx.tcx.get_diagnostic_name(macro_call.def_id) {
+            Some(sym::debug_assert_macro) => true,
+            Some(sym::assert_macro) => false,
+            _ => return,
+        };
+        let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) else { return };
+        let Some((Constant::Bool(val), _)) = constant(cx, cx.typeck_results(), condition) else { return };
+        if val {
             span_lint_and_help(
                 cx,
                 ASSERTIONS_ON_CONSTANTS,
-                e.span,
-                if is_debug {
-                    "`debug_assert!(true)` will be optimized out by the compiler"
-                } else {
-                    "`assert!(true)` will be optimized out by the compiler"
-                },
+                macro_call.span,
+                &format!(
+                    "`{}!(true)` will be optimized out by the compiler",
+                    cx.tcx.item_name(macro_call.def_id)
+                ),
                 None,
                 "remove it",
             );
-        };
-        let lint_false_without_message = || {
-            span_lint_and_help(
-                cx,
-                ASSERTIONS_ON_CONSTANTS,
-                e.span,
-                "`assert!(false)` should probably be replaced",
-                None,
-                "use `panic!()` or `unreachable!()`",
-            );
-        };
-        let lint_false_with_message = |panic_message: String| {
-            span_lint_and_help(
-                cx,
-                ASSERTIONS_ON_CONSTANTS,
-                e.span,
-                &format!("`assert!(false, {})` should probably be replaced", panic_message),
-                None,
-                &format!("use `panic!({})` or `unreachable!({})`", panic_message, panic_message),
-            );
-        };
-
-        if let Some(debug_assert_span) = is_expn_of(e.span, "debug_assert") {
-            if debug_assert_span.from_expansion() {
-                return;
-            }
-            if_chain! {
-                if let ExprKind::Unary(_, lit) = e.kind;
-                if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), lit);
-                if is_true;
-                then {
-                    lint_true(true);
-                }
+        } else if !is_debug {
+            let (assert_arg, panic_arg) = match panic_expn {
+                PanicExpn::Empty => ("", ""),
+                _ => (", ..", ".."),
             };
-        } else if let Some(assert_span) = is_direct_expn_of(e.span, "assert") {
-            if assert_span.from_expansion() {
-                return;
-            }
-            if let Some(assert_match) = match_assert_with_message(cx, e) {
-                match assert_match {
-                    // matched assert but not message
-                    AssertKind::WithoutMessage(false) => lint_false_without_message(),
-                    AssertKind::WithoutMessage(true) | AssertKind::WithMessage(_, true) => lint_true(false),
-                    AssertKind::WithMessage(panic_message, false) => lint_false_with_message(panic_message),
-                };
-            }
+            span_lint_and_help(
+                cx,
+                ASSERTIONS_ON_CONSTANTS,
+                macro_call.span,
+                &format!("`assert!(false{})` should probably be replaced", assert_arg),
+                None,
+                &format!("use `panic!({})` or `unreachable!({0})`", panic_arg),
+            );
         }
     }
 }
-
-/// Result of calling `match_assert_with_message`.
-enum AssertKind {
-    WithMessage(String, bool),
-    WithoutMessage(bool),
-}
-
-/// Check if the expression matches
-///
-/// ```rust,ignore
-/// if !c {
-///   {
-///     ::std::rt::begin_panic(message, _)
-///   }
-/// }
-/// ```
-///
-/// where `message` is any expression and `c` is a constant bool.
-fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<AssertKind> {
-    if_chain! {
-        if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr);
-        if let ExprKind::Unary(UnOp::Not, expr) = cond.kind;
-        // bind the first argument of the `assert!` macro
-        if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), expr);
-        let begin_panic_call = peel_blocks(then);
-        // function call
-        if let Some(arg) = match_panic_call(cx, begin_panic_call);
-        // bind the second argument of the `assert!` macro if it exists
-        if let panic_message = snippet_opt(cx, arg.span);
-        // second argument of begin_panic is irrelevant
-        // as is the second match arm
-        then {
-            // an empty message occurs when it was generated by the macro
-            // (and not passed by the user)
-            return panic_message
-                .filter(|msg| !msg.is_empty())
-                .map(|msg| AssertKind::WithMessage(msg, is_true))
-                .or(Some(AssertKind::WithoutMessage(is_true)));
-        }
-    }
-    None
-}
diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs
index 0629674307b..a58d12ddd6b 100644
--- a/clippy_lints/src/attrs.rs
+++ b/clippy_lints/src/attrs.rs
@@ -1,9 +1,10 @@
 //! checks for attributes
 
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::macros::{is_panic, macro_backtrace};
 use clippy_utils::msrvs;
 use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments};
-use clippy_utils::{extract_msrv_attr, match_panic_def_id, meets_msrv};
+use clippy_utils::{extract_msrv_attr, meets_msrv};
 use if_chain::if_chain;
 use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
 use rustc_errors::Applicability;
@@ -443,20 +444,15 @@ fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_
 }
 
 fn is_relevant_expr(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, expr: &Expr<'_>) -> bool {
+    if macro_backtrace(expr.span).last().map_or(false, |macro_call| {
+        is_panic(cx, macro_call.def_id) || cx.tcx.item_name(macro_call.def_id) == sym::unreachable
+    }) {
+        return false;
+    }
     match &expr.kind {
         ExprKind::Block(block, _) => is_relevant_block(cx, typeck_results, block),
         ExprKind::Ret(Some(e)) => is_relevant_expr(cx, typeck_results, e),
         ExprKind::Ret(None) | ExprKind::Break(_, None) => false,
-        ExprKind::Call(path_expr, _) => {
-            if let ExprKind::Path(qpath) = &path_expr.kind {
-                typeck_results
-                    .qpath_res(qpath, path_expr.hir_id)
-                    .opt_def_id()
-                    .map_or(true, |fun_id| !match_panic_def_id(cx, fun_id))
-            } else {
-                true
-            }
-        },
         _ => true,
     }
 }
diff --git a/clippy_lints/src/bool_assert_comparison.rs b/clippy_lints/src/bool_assert_comparison.rs
index d0b8c52a36a..c50e214be28 100644
--- a/clippy_lints/src/bool_assert_comparison.rs
+++ b/clippy_lints/src/bool_assert_comparison.rs
@@ -1,4 +1,5 @@
-use clippy_utils::{diagnostics::span_lint_and_sugg, higher, is_direct_expn_of, ty::implements_trait};
+use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node};
+use clippy_utils::{diagnostics::span_lint_and_sugg, ty::implements_trait};
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, Lit};
@@ -41,7 +42,7 @@ fn is_bool_lit(e: &Expr<'_>) -> bool {
     ) && !e.span.from_expansion()
 }
 
-fn is_impl_not_trait_with_bool_out(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
+fn is_impl_not_trait_with_bool_out(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
     let ty = cx.typeck_results().expr_ty(e);
 
     cx.tcx
@@ -66,44 +67,40 @@ fn is_impl_not_trait_with_bool_out(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) ->
 
 impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        let macros = ["assert_eq", "debug_assert_eq"];
-        let inverted_macros = ["assert_ne", "debug_assert_ne"];
-
-        for mac in macros.iter().chain(inverted_macros.iter()) {
-            if let Some(span) = is_direct_expn_of(expr.span, mac) {
-                if let Some(args) = higher::extract_assert_macro_args(expr) {
-                    if let [a, b, ..] = args[..] {
-                        let nb_bool_args = usize::from(is_bool_lit(a)) + usize::from(is_bool_lit(b));
-
-                        if nb_bool_args != 1 {
-                            // If there are two boolean arguments, we definitely don't understand
-                            // what's going on, so better leave things as is...
-                            //
-                            // Or there is simply no boolean and then we can leave things as is!
-                            return;
-                        }
-
-                        if !is_impl_not_trait_with_bool_out(cx, a) || !is_impl_not_trait_with_bool_out(cx, b) {
-                            // At this point the expression which is not a boolean
-                            // literal does not implement Not trait with a bool output,
-                            // so we cannot suggest to rewrite our code
-                            return;
-                        }
-
-                        let non_eq_mac = &mac[..mac.len() - 3];
-                        span_lint_and_sugg(
-                            cx,
-                            BOOL_ASSERT_COMPARISON,
-                            span,
-                            &format!("used `{}!` with a literal bool", mac),
-                            "replace it with",
-                            format!("{}!(..)", non_eq_mac),
-                            Applicability::MaybeIncorrect,
-                        );
-                        return;
-                    }
-                }
-            }
+        let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
+        let macro_name = cx.tcx.item_name(macro_call.def_id);
+        if !matches!(
+            macro_name.as_str(),
+            "assert_eq" | "debug_assert_eq" | "assert_ne" | "debug_assert_ne"
+        ) {
+            return;
         }
+        let Some ((a, b, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return };
+        if !(is_bool_lit(a) ^ is_bool_lit(b)) {
+            // If there are two boolean arguments, we definitely don't understand
+            // what's going on, so better leave things as is...
+            //
+            // Or there is simply no boolean and then we can leave things as is!
+            return;
+        }
+
+        if !is_impl_not_trait_with_bool_out(cx, a) || !is_impl_not_trait_with_bool_out(cx, b) {
+            // At this point the expression which is not a boolean
+            // literal does not implement Not trait with a bool output,
+            // so we cannot suggest to rewrite our code
+            return;
+        }
+
+        let macro_name = macro_name.as_str();
+        let non_eq_mac = &macro_name[..macro_name.len() - 3];
+        span_lint_and_sugg(
+            cx,
+            BOOL_ASSERT_COMPARISON,
+            macro_call.span,
+            &format!("used `{}!` with a literal bool", macro_name),
+            "replace it with",
+            format!("{}!(..)", non_eq_mac),
+            Applicability::MaybeIncorrect,
+        );
     }
 }
diff --git a/clippy_lints/src/borrow_as_ptr.rs b/clippy_lints/src/borrow_as_ptr.rs
new file mode 100644
index 00000000000..b8f5217af2b
--- /dev/null
+++ b/clippy_lints/src/borrow_as_ptr.rs
@@ -0,0 +1,97 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_no_std_crate;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::{meets_msrv, msrvs};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, TyKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for the usage of `&expr as *const T` or
+    /// `&mut expr as *mut T`, and suggest using `ptr::addr_of` or
+    /// `ptr::addr_of_mut` instead.
+    ///
+    /// ### Why is this bad?
+    /// This would improve readability and avoid creating a reference
+    /// that points to an uninitialized value or unaligned place.
+    /// Read the `ptr::addr_of` docs for more information.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let val = 1;
+    /// let p = &val as *const i32;
+    ///
+    /// let mut val_mut = 1;
+    /// let p_mut = &mut val_mut as *mut i32;
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let val = 1;
+    /// let p = std::ptr::addr_of!(val);
+    ///
+    /// let mut val_mut = 1;
+    /// let p_mut = std::ptr::addr_of_mut!(val_mut);
+    /// ```
+    #[clippy::version = "1.60.0"]
+    pub BORROW_AS_PTR,
+    pedantic,
+    "borrowing just to cast to a raw pointer"
+}
+
+impl_lint_pass!(BorrowAsPtr => [BORROW_AS_PTR]);
+
+pub struct BorrowAsPtr {
+    msrv: Option<RustcVersion>,
+}
+
+impl BorrowAsPtr {
+    #[must_use]
+    pub fn new(msrv: Option<RustcVersion>) -> Self {
+        Self { msrv }
+    }
+}
+
+impl<'tcx> LateLintPass<'tcx> for BorrowAsPtr {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if !meets_msrv(self.msrv.as_ref(), &msrvs::BORROW_AS_PTR) {
+            return;
+        }
+
+        if expr.span.from_expansion() {
+            return;
+        }
+
+        if_chain! {
+            if let ExprKind::Cast(left_expr, ty) = &expr.kind;
+            if let TyKind::Ptr(_) = ty.kind;
+            if let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = &left_expr.kind;
+
+            then {
+                let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" };
+                let macro_name = match mutability {
+                    Mutability::Not => "addr_of",
+                    Mutability::Mut => "addr_of_mut",
+                };
+
+                span_lint_and_sugg(
+                    cx,
+                    BORROW_AS_PTR,
+                    expr.span,
+                    "borrow as raw pointer",
+                    "try",
+                    format!(
+                        "{}::ptr::{}!({})",
+                        core_or_std,
+                        macro_name,
+                        snippet_opt(cx, e.span).unwrap()
+                    ),
+                    Applicability::MachineApplicable,
+                );
+            }
+        }
+    }
+}
diff --git a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs
index 3f286dd9e2f..e8f39cd3709 100644
--- a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs
+++ b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs
@@ -67,7 +67,7 @@ fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: &
     None
 }
 
-impl LateLintPass<'tcx> for CaseSensitiveFileExtensionComparisons {
+impl<'tcx> LateLintPass<'tcx> for CaseSensitiveFileExtensionComparisons {
     fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
         if let Some(span) = check_case_sensitive_file_extension_comparison(ctx, expr) {
             span_lint_and_help(
diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs
index 248b35b024e..b9de5510455 100644
--- a/clippy_lints/src/casts/cast_ptr_alignment.rs
+++ b/clippy_lints/src/casts/cast_ptr_alignment.rs
@@ -9,7 +9,7 @@ use rustc_span::symbol::sym;
 
 use super::CAST_PTR_ALIGNMENT;
 
-pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
     if let ExprKind::Cast(cast_expr, cast_to) = expr.kind {
         if is_hir_ty_cfg_dependant(cx, cast_to) {
             return;
diff --git a/clippy_lints/src/casts/cast_ref_to_mut.rs b/clippy_lints/src/casts/cast_ref_to_mut.rs
index d9bf1ea58b9..15f2f81f407 100644
--- a/clippy_lints/src/casts/cast_ref_to_mut.rs
+++ b/clippy_lints/src/casts/cast_ref_to_mut.rs
@@ -6,7 +6,7 @@ use rustc_middle::ty;
 
 use super::CAST_REF_TO_MUT;
 
-pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
     if_chain! {
         if let ExprKind::Unary(UnOp::Deref, e) = &expr.kind;
         if let ExprKind::Cast(e, t) = &e.kind;
diff --git a/clippy_lints/src/casts/char_lit_as_u8.rs b/clippy_lints/src/casts/char_lit_as_u8.rs
index 099a0de881f..7cc406018db 100644
--- a/clippy_lints/src/casts/char_lit_as_u8.rs
+++ b/clippy_lints/src/casts/char_lit_as_u8.rs
@@ -9,7 +9,7 @@ use rustc_middle::ty::{self, UintTy};
 
 use super::CHAR_LIT_AS_U8;
 
-pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
     if_chain! {
         if let ExprKind::Cast(e, _) = &expr.kind;
         if let ExprKind::Lit(l) = &e.kind;
diff --git a/clippy_lints/src/casts/ptr_as_ptr.rs b/clippy_lints/src/casts/ptr_as_ptr.rs
index 3132d3a5cf0..fb04f93fbcf 100644
--- a/clippy_lints/src/casts/ptr_as_ptr.rs
+++ b/clippy_lints/src/casts/ptr_as_ptr.rs
@@ -12,7 +12,7 @@ use rustc_semver::RustcVersion;
 
 use super::PTR_AS_PTR;
 
-pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: &Option<RustcVersion>) {
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option<RustcVersion>) {
     if !meets_msrv(msrv.as_ref(), &msrvs::POINTER_CAST) {
         return;
     }
diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs
index d07bc23235b..73ce656ad15 100644
--- a/clippy_lints/src/copies.rs
+++ b/clippy_lints/src/copies.rs
@@ -316,7 +316,7 @@ struct BlockEqual {
 
 /// This function can also trigger the `IF_SAME_THEN_ELSE` in which case it'll return `None` to
 /// abort any further processing and avoid duplicate lint triggers.
-fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option<BlockEqual> {
+fn scan_block_for_eq(cx: &LateContext<'_>, blocks: &[&Block<'_>]) -> Option<BlockEqual> {
     let mut start_eq = usize::MAX;
     let mut end_eq = usize::MAX;
     let mut expr_eq = true;
@@ -385,11 +385,7 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option<
     })
 }
 
-fn check_for_warn_of_moved_symbol(
-    cx: &LateContext<'tcx>,
-    symbols: &FxHashSet<Symbol>,
-    if_expr: &'tcx Expr<'_>,
-) -> bool {
+fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &FxHashSet<Symbol>, if_expr: &Expr<'_>) -> bool {
     get_enclosing_block(cx, if_expr.hir_id).map_or(false, |block| {
         let ignore_span = block.span.shrink_to_lo().to(if_expr.span);
 
@@ -419,13 +415,13 @@ fn check_for_warn_of_moved_symbol(
 }
 
 fn emit_branches_sharing_code_lint(
-    cx: &LateContext<'tcx>,
+    cx: &LateContext<'_>,
     start_stmts: usize,
     end_stmts: usize,
     lint_end: bool,
     warn_about_moved_symbol: bool,
-    blocks: &[&Block<'tcx>],
-    if_expr: &'tcx Expr<'_>,
+    blocks: &[&Block<'_>],
+    if_expr: &Expr<'_>,
 ) {
     if start_stmts == 0 && !lint_end {
         return;
diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs
index 6422f5aabe5..3070588483c 100644
--- a/clippy_lints/src/default.rs
+++ b/clippy_lints/src/default.rs
@@ -77,7 +77,7 @@ pub struct Default {
 
 impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]);
 
-impl LateLintPass<'_> for Default {
+impl<'tcx> LateLintPass<'tcx> for Default {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if_chain! {
             if !expr.span.from_expansion();
@@ -110,7 +110,7 @@ impl LateLintPass<'_> for Default {
     }
 
     #[allow(clippy::too_many_lines)]
-    fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
+    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
         // start from the `let mut _ = _::default();` and look at all the following
         // statements, see if they re-assign the fields of the binding
         let stmts_head = match block.stmts {
diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs
index 15215ac15cd..66b5f49817d 100644
--- a/clippy_lints/src/default_numeric_fallback.rs
+++ b/clippy_lints/src/default_numeric_fallback.rs
@@ -54,7 +54,7 @@ declare_clippy_lint! {
 
 declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]);
 
-impl LateLintPass<'_> for DefaultNumericFallback {
+impl<'tcx> LateLintPass<'tcx> for DefaultNumericFallback {
     fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
         let mut visitor = NumericFallbackVisitor::new(cx);
         visitor.visit_body(body);
diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs
index fa2b348591b..bf077a212fd 100644
--- a/clippy_lints/src/dereference.rs
+++ b/clippy_lints/src/dereference.rs
@@ -355,7 +355,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
     }
 }
 
-fn try_parse_ref_op(
+fn try_parse_ref_op<'tcx>(
     tcx: TyCtxt<'tcx>,
     typeck: &'tcx TypeckResults<'_>,
     expr: &'tcx Expr<'_>,
@@ -387,7 +387,7 @@ fn try_parse_ref_op(
 
 // Checks whether the type for a deref call actually changed the type, not just the mutability of
 // the reference.
-fn deref_method_same_type(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
+fn deref_method_same_type(result_ty: Ty<'_>, arg_ty: Ty<'_>) -> bool {
     match (result_ty.kind(), arg_ty.kind()) {
         (ty::Ref(_, result_ty, _), ty::Ref(_, arg_ty, _)) => TyS::same_type(result_ty, arg_ty),
 
@@ -457,7 +457,7 @@ fn is_linted_explicit_deref_position(parent: Option<Node<'_>>, child_id: HirId,
 }
 
 /// Adjustments are sometimes made in the parent block rather than the expression itself.
-fn find_adjustments(
+fn find_adjustments<'tcx>(
     tcx: TyCtxt<'tcx>,
     typeck: &'tcx TypeckResults<'_>,
     expr: &'tcx Expr<'_>,
@@ -499,7 +499,7 @@ fn find_adjustments(
 }
 
 #[allow(clippy::needless_pass_by_value)]
-fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) {
+fn report(cx: &LateContext<'_>, expr: &Expr<'_>, state: State, data: StateData) {
     match state {
         State::DerefMethod {
             ty_changed_count,
@@ -568,7 +568,7 @@ fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: Stat
 }
 
 impl Dereferencing {
-    fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, local: HirId) {
+    fn check_local_usage(&mut self, cx: &LateContext<'_>, e: &Expr<'_>, local: HirId) {
         if let Some(outer_pat) = self.ref_locals.get_mut(&local) {
             if let Some(pat) = outer_pat {
                 // Check for auto-deref
diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs
index 6d4065907fb..73c00d97020 100644
--- a/clippy_lints/src/disallowed_methods.rs
+++ b/clippy_lints/src/disallowed_methods.rs
@@ -11,6 +11,9 @@ declare_clippy_lint! {
     /// ### What it does
     /// Denies the configured methods and functions in clippy.toml
     ///
+    /// Note: Even though this lint is warn-by-default, it will only trigger if
+    /// methods are defined in the clippy.toml file.
+    ///
     /// ### Why is this bad?
     /// Some methods are undesirable in certain contexts, and it's beneficial to
     /// lint for them as needed.
@@ -49,14 +52,14 @@ declare_clippy_lint! {
     /// ```
     #[clippy::version = "1.49.0"]
     pub DISALLOWED_METHODS,
-    nursery,
+    style,
     "use of a disallowed method call"
 }
 
 #[derive(Clone, Debug)]
 pub struct DisallowedMethods {
     conf_disallowed: Vec<conf::DisallowedMethod>,
-    disallowed: DefIdMap<Option<String>>,
+    disallowed: DefIdMap<usize>,
 }
 
 impl DisallowedMethods {
@@ -72,17 +75,10 @@ impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]);
 
 impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
     fn check_crate(&mut self, cx: &LateContext<'_>) {
-        for conf in &self.conf_disallowed {
-            let (path, reason) = match conf {
-                conf::DisallowedMethod::Simple(path) => (path, None),
-                conf::DisallowedMethod::WithReason { path, reason } => (
-                    path,
-                    reason.as_ref().map(|reason| format!("{} (from clippy.toml)", reason)),
-                ),
-            };
-            let segs: Vec<_> = path.split("::").collect();
+        for (index, conf) in self.conf_disallowed.iter().enumerate() {
+            let segs: Vec<_> = conf.path().split("::").collect();
             if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs) {
-                self.disallowed.insert(id, reason);
+                self.disallowed.insert(id, index);
             }
         }
     }
@@ -92,15 +88,17 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
             Some(def_id) => def_id,
             None => return,
         };
-        let reason = match self.disallowed.get(&def_id) {
-            Some(reason) => reason,
+        let conf = match self.disallowed.get(&def_id) {
+            Some(&index) => &self.conf_disallowed[index],
             None => return,
         };
-        let func_path = cx.tcx.def_path_str(def_id);
-        let msg = format!("use of a disallowed method `{}`", func_path);
+        let msg = format!("use of a disallowed method `{}`", conf.path());
         span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, &msg, |diag| {
-            if let Some(reason) = reason {
-                diag.note(reason);
+            if let conf::DisallowedMethod::WithReason {
+                reason: Some(reason), ..
+            } = conf
+            {
+                diag.note(&format!("{} (from clippy.toml)", reason));
             }
         });
     }
diff --git a/clippy_lints/src/disallowed_types.rs b/clippy_lints/src/disallowed_types.rs
index eaed4032713..ea4b49b46fe 100644
--- a/clippy_lints/src/disallowed_types.rs
+++ b/clippy_lints/src/disallowed_types.rs
@@ -14,6 +14,9 @@ declare_clippy_lint! {
     /// ### What it does
     /// Denies the configured types in clippy.toml.
     ///
+    /// Note: Even though this lint is warn-by-default, it will only trigger if
+    /// types are defined in the clippy.toml file.
+    ///
     /// ### Why is this bad?
     /// Some types are undesirable in certain contexts.
     ///
@@ -44,7 +47,7 @@ declare_clippy_lint! {
     /// ```
     #[clippy::version = "1.55.0"]
     pub DISALLOWED_TYPES,
-    nursery,
+    style,
     "use of disallowed types"
 }
 #[derive(Clone, Debug)]
diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs
index 7c271773357..cb7d5ac7394 100644
--- a/clippy_lints/src/doc.rs
+++ b/clippy_lints/src/doc.rs
@@ -1,8 +1,9 @@
 use clippy_utils::attrs::is_doc_hidden;
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_then};
+use clippy_utils::macros::{is_panic, root_macro_call_first_node};
 use clippy_utils::source::{first_line_of_span, snippet_with_applicability};
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
-use clippy_utils::{is_entrypoint_fn, is_expn_of, match_panic_def_id, method_chain_args, return_ty};
+use clippy_utils::{is_entrypoint_fn, method_chain_args, return_ty};
 use if_chain::if_chain;
 use itertools::Itertools;
 use rustc_ast::ast::{Async, AttrKind, Attribute, Fn, FnRetTy, ItemKind};
@@ -13,7 +14,7 @@ use rustc_errors::emitter::EmitterWriter;
 use rustc_errors::{Applicability, Handler, SuggestionStyle};
 use rustc_hir as hir;
 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
-use rustc_hir::{AnonConst, Expr, ExprKind, QPath};
+use rustc_hir::{AnonConst, Expr};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::map::Map;
 use rustc_middle::lint::in_external_macro;
@@ -805,24 +806,17 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
             return;
         }
 
-        // check for `begin_panic`
-        if_chain! {
-            if let ExprKind::Call(func_expr, _) = expr.kind;
-            if let ExprKind::Path(QPath::Resolved(_, path)) = func_expr.kind;
-            if let Some(path_def_id) = path.res.opt_def_id();
-            if match_panic_def_id(self.cx, path_def_id);
-            if is_expn_of(expr.span, "unreachable").is_none();
-            if !is_expn_of_debug_assertions(expr.span);
-            then {
-                self.panic_span = Some(expr.span);
+        if let Some(macro_call) = root_macro_call_first_node(self.cx, expr) {
+            if is_panic(self.cx, macro_call.def_id)
+                || matches!(
+                    self.cx.tcx.item_name(macro_call.def_id).as_str(),
+                    "assert" | "assert_eq" | "assert_ne" | "todo"
+                )
+            {
+                self.panic_span = Some(macro_call.span);
             }
         }
 
-        // check for `assert_eq` or `assert_ne`
-        if is_expn_of(expr.span, "assert_eq").is_some() || is_expn_of(expr.span, "assert_ne").is_some() {
-            self.panic_span = Some(expr.span);
-        }
-
         // check for `unwrap`
         if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
             let receiver_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs();
@@ -844,8 +838,3 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
         NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
     }
 }
-
-fn is_expn_of_debug_assertions(span: Span) -> bool {
-    const MACRO_NAMES: &[&str] = &["debug_assert", "debug_assert_eq", "debug_assert_ne"];
-    MACRO_NAMES.iter().any(|name| is_expn_of(span, name).is_some())
-}
diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs
index 3d92eb16870..3ce239273e2 100644
--- a/clippy_lints/src/entry.rs
+++ b/clippy_lints/src/entry.rs
@@ -233,7 +233,7 @@ struct ContainsExpr<'tcx> {
     key: &'tcx Expr<'tcx>,
     call_ctxt: SyntaxContext,
 }
-fn try_parse_contains(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(MapType, ContainsExpr<'tcx>)> {
+fn try_parse_contains<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(MapType, ContainsExpr<'tcx>)> {
     let mut negated = false;
     let expr = peel_hir_expr_while(expr, |e| match e.kind {
         ExprKind::Unary(UnOp::Not, e) => {
@@ -280,7 +280,7 @@ struct InsertExpr<'tcx> {
     key: &'tcx Expr<'tcx>,
     value: &'tcx Expr<'tcx>,
 }
-fn try_parse_insert(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<InsertExpr<'tcx>> {
+fn try_parse_insert<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<InsertExpr<'tcx>> {
     if let ExprKind::MethodCall(_, _, [map, key, value], _) = expr.kind {
         let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
         if match_def_path(cx, id, &paths::BTREEMAP_INSERT) || match_def_path(cx, id, &paths::HASHMAP_INSERT) {
@@ -301,7 +301,7 @@ enum Edit<'tcx> {
     /// An insertion into the map.
     Insertion(Insertion<'tcx>),
 }
-impl Edit<'tcx> {
+impl<'tcx> Edit<'tcx> {
     fn as_insertion(self) -> Option<Insertion<'tcx>> {
         if let Self::Insertion(i) = self { Some(i) } else { None }
     }
@@ -532,7 +532,7 @@ struct InsertSearchResults<'tcx> {
     allow_insert_closure: bool,
     is_single_insert: bool,
 }
-impl InsertSearchResults<'tcx> {
+impl<'tcx> InsertSearchResults<'tcx> {
     fn as_single_insertion(&self) -> Option<Insertion<'tcx>> {
         self.is_single_insert.then(|| self.edits[0].as_insertion().unwrap())
     }
@@ -633,7 +633,7 @@ impl InsertSearchResults<'tcx> {
     }
 }
 
-fn find_insert_calls(
+fn find_insert_calls<'tcx>(
     cx: &LateContext<'tcx>,
     contains_expr: &ContainsExpr<'tcx>,
     expr: &'tcx Expr<'_>,
diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs
index 10123460527..df75b815436 100644
--- a/clippy_lints/src/eq_op.rs
+++ b/clippy_lints/src/eq_op.rs
@@ -1,10 +1,11 @@
 use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_then};
+use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace};
 use clippy_utils::source::snippet;
 use clippy_utils::ty::{implements_trait, is_copy};
-use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, is_expn_of, is_in_test_function};
+use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, is_in_test_function};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, StmtKind};
+use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
@@ -68,32 +69,26 @@ declare_clippy_lint! {
 
 declare_lint_pass!(EqOp => [EQ_OP, OP_REF]);
 
-const ASSERT_MACRO_NAMES: [&str; 4] = ["assert_eq", "assert_ne", "debug_assert_eq", "debug_assert_ne"];
-
 impl<'tcx> LateLintPass<'tcx> for EqOp {
     #[allow(clippy::similar_names, clippy::too_many_lines)]
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
-        if let ExprKind::Block(block, _) = e.kind {
-            for stmt in block.stmts {
-                for amn in &ASSERT_MACRO_NAMES {
-                    if_chain! {
-                        if is_expn_of(stmt.span, amn).is_some();
-                        if let StmtKind::Semi(matchexpr) = stmt.kind;
-                        if let Some(macro_args) = higher::extract_assert_macro_args(matchexpr);
-                        if macro_args.len() == 2;
-                        let (lhs, rhs) = (macro_args[0], macro_args[1]);
-                        if eq_expr_value(cx, lhs, rhs);
-                        if !is_in_test_function(cx.tcx, e.hir_id);
-                        then {
-                            span_lint(
-                                cx,
-                                EQ_OP,
-                                lhs.span.to(rhs.span),
-                                &format!("identical args used in this `{}!` macro call", amn),
-                            );
-                        }
-                    }
-                }
+        if_chain! {
+            if let Some((macro_call, macro_name)) = first_node_macro_backtrace(cx, e).find_map(|macro_call| {
+                let name = cx.tcx.item_name(macro_call.def_id);
+                matches!(name.as_str(), "assert_eq" | "assert_ne" | "debug_assert_eq" | "debug_assert_ne")
+                    .then(|| (macro_call, name))
+            });
+            if let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn);
+            if eq_expr_value(cx, lhs, rhs);
+            if macro_call.is_local();
+            if !is_in_test_function(cx.tcx, e.hir_id);
+            then {
+                span_lint(
+                    cx,
+                    EQ_OP,
+                    lhs.span.to(rhs.span),
+                    &format!("identical args used in this `{}!` macro call", macro_name),
+                );
             }
         }
         if let ExprKind::Binary(op, left, right) = e.kind {
diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs
index 06d128f5527..cf47e581ccb 100644
--- a/clippy_lints/src/equatable_if_let.rs
+++ b/clippy_lints/src/equatable_if_let.rs
@@ -56,7 +56,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool {
     }
 }
 
-fn is_structural_partial_eq(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> bool {
+fn is_structural_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> bool {
     if let Some(def_id) = cx.tcx.lang_items().eq_trait() {
         implements_trait(cx, ty, def_id, &[other.into()])
     } else {
diff --git a/clippy_lints/src/erasing_op.rs b/clippy_lints/src/erasing_op.rs
index d49cec26be5..c1a84973c42 100644
--- a/clippy_lints/src/erasing_op.rs
+++ b/clippy_lints/src/erasing_op.rs
@@ -1,9 +1,11 @@
 use clippy_utils::consts::{constant_simple, Constant};
 use clippy_utils::diagnostics::span_lint;
+use clippy_utils::ty::same_type_and_consts;
+
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::TypeckResults;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -35,24 +37,40 @@ impl<'tcx> LateLintPass<'tcx> for ErasingOp {
             return;
         }
         if let ExprKind::Binary(ref cmp, left, right) = e.kind {
+            let tck = cx.typeck_results();
             match cmp.node {
                 BinOpKind::Mul | BinOpKind::BitAnd => {
-                    check(cx, left, e.span);
-                    check(cx, right, e.span);
+                    check(cx, tck, left, right, e);
+                    check(cx, tck, right, left, e);
                 },
-                BinOpKind::Div => check(cx, left, e.span),
+                BinOpKind::Div => check(cx, tck, left, right, e),
                 _ => (),
             }
         }
     }
 }
 
-fn check(cx: &LateContext<'_>, e: &Expr<'_>, span: Span) {
-    if constant_simple(cx, cx.typeck_results(), e) == Some(Constant::Int(0)) {
+fn different_types(tck: &TypeckResults<'_>, input: &Expr<'_>, output: &Expr<'_>) -> bool {
+    let input_ty = tck.expr_ty(input).peel_refs();
+    let output_ty = tck.expr_ty(output).peel_refs();
+    !same_type_and_consts(input_ty, output_ty)
+}
+
+fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    tck: &TypeckResults<'tcx>,
+    op: &Expr<'tcx>,
+    other: &Expr<'tcx>,
+    parent: &Expr<'tcx>,
+) {
+    if constant_simple(cx, tck, op) == Some(Constant::Int(0)) {
+        if different_types(tck, other, parent) {
+            return;
+        }
         span_lint(
             cx,
             ERASING_OP,
-            span,
+            parent.span,
             "this operation will always return zero. This is likely not the intended outcome",
         );
     }
diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs
index 5a4b4247104..b22515a3907 100644
--- a/clippy_lints/src/eta_reduction.rs
+++ b/clippy_lints/src/eta_reduction.rs
@@ -1,6 +1,7 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::higher::VecArgs;
 use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::usage::local_used_after_expr;
 use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local, path_to_local_id};
 use if_chain::if_chain;
@@ -12,6 +13,7 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
 use rustc_middle::ty::subst::Subst;
 use rustc_middle::ty::{self, ClosureKind, Ty, TypeFoldable};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::sym;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -113,6 +115,9 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
             // A type param function ref like `T::f` is not 'static, however
             // it is if cast like `T::f as fn()`. This seems like a rustc bug.
             if !substs.types().any(|t| matches!(t.kind(), ty::Param(_)));
+            let callee_ty_unadjusted = cx.typeck_results().expr_ty(callee).peel_refs();
+            if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Arc);
+            if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Rc);
             then {
                 span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
                     if let Some(mut snippet) = snippet_opt(cx, callee.span) {
diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs
index 6b327b9ce17..98e5234e0aa 100644
--- a/clippy_lints/src/explicit_write.rs
+++ b/clippy_lints/src/explicit_write.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
-use clippy_utils::higher::FormatArgsExpn;
+use clippy_utils::macros::FormatArgsExpn;
 use clippy_utils::{is_expn_of, match_function_call, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
@@ -48,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
             } else {
                 None
             };
-            if let Some(format_args) = FormatArgsExpn::parse(write_arg);
+            if let Some(format_args) = FormatArgsExpn::parse(cx, write_arg);
             then {
                 let calling_macro =
                     // ordering is important here, since `writeln!` uses `write!` internally
@@ -80,7 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
                     )
                 };
                 let msg = format!("use of `{}.unwrap()`", used);
-                if let [write_output] = *format_args.format_string_symbols {
+                if let [write_output] = *format_args.format_string_parts {
                     let mut write_output = write_output.to_string();
                     if write_output.ends_with('\n') {
                         write_output.pop();
diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs
index 05d300058cf..02f1baf27fa 100644
--- a/clippy_lints/src/fallible_impl_from.rs
+++ b/clippy_lints/src/fallible_impl_from.rs
@@ -1,6 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::macros::{is_panic, root_macro_call_first_node};
+use clippy_utils::method_chain_args;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{is_expn_of, match_panic_def_id, method_chain_args};
 use if_chain::if_chain;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
@@ -68,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for FallibleImplFrom {
 
 fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[hir::ImplItemRef]) {
     use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
-    use rustc_hir::{Expr, ExprKind, ImplItemKind, QPath};
+    use rustc_hir::{Expr, ImplItemKind};
 
     struct FindPanicUnwrap<'a, 'tcx> {
         lcx: &'a LateContext<'tcx>,
@@ -80,14 +81,8 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[h
         type Map = Map<'tcx>;
 
         fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
-            // check for `begin_panic`
-            if_chain! {
-                if let ExprKind::Call(func_expr, _) = expr.kind;
-                if let ExprKind::Path(QPath::Resolved(_, path)) = func_expr.kind;
-                if let Some(path_def_id) = path.res.opt_def_id();
-                if match_panic_def_id(self.lcx, path_def_id);
-                if is_expn_of(expr.span, "unreachable").is_none();
-                then {
+            if let Some(macro_call) = root_macro_call_first_node(self.lcx, expr) {
+                if is_panic(self.lcx, macro_call.def_id) {
                     self.result.push(expr.span);
                 }
             }
diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs
index 3f043e5f2f1..688d8f8630f 100644
--- a/clippy_lints/src/format.rs
+++ b/clippy_lints/src/format.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::higher::FormatExpn;
+use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn};
 use clippy_utils::source::{snippet_opt, snippet_with_applicability};
 use clippy_utils::sugg::Sugg;
 use if_chain::if_chain;
@@ -43,38 +43,41 @@ declare_lint_pass!(UselessFormat => [USELESS_FORMAT]);
 
 impl<'tcx> LateLintPass<'tcx> for UselessFormat {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        let FormatExpn { call_site, format_args } = match FormatExpn::parse(expr) {
-            Some(e) if !e.call_site.from_expansion() => e,
-            _ => return,
+        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 mut applicability = Applicability::MachineApplicable;
         if format_args.value_args.is_empty() {
-            if format_args.format_string_parts.is_empty() {
-                span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability);
-            } else {
-                if_chain! {
-                    if let [e] = &*format_args.format_string_parts;
-                    if let ExprKind::Lit(lit) = &e.kind;
-                    if let Some(s_src) = snippet_opt(cx, lit.span);
-                    then {
+            match *format_args.format_string_parts {
+                [] => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability),
+                [_] => {
+                    if let Some(s_src) = snippet_opt(cx, format_args.format_string_span) {
                         // Simulate macro expansion, converting {{ and }} to { and }.
                         let s_expand = s_src.replace("{{", "{").replace("}}", "}");
                         let sugg = format!("{}.to_string()", s_expand);
                         span_useless_format(cx, call_site, sugg, applicability);
                     }
-                }
+                },
+                [..] => {},
             }
         } else if let [value] = *format_args.value_args {
             if_chain! {
-                if format_args.format_string_symbols == [kw::Empty];
+                if format_args.format_string_parts == [kw::Empty];
                 if match cx.typeck_results().expr_ty(value).peel_refs().kind() {
                     ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did),
                     ty::Str => true,
                     _ => false,
                 };
                 if let Some(args) = format_args.args();
-                if args.iter().all(|arg| arg.is_display() && !arg.has_string_formatting());
+                if args.iter().all(|arg| arg.format_trait == sym::Display && !arg.has_string_formatting());
                 then {
                     let is_new_string = match value.kind {
                         ExprKind::Binary(..) => true,
diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs
index f0e1a67dcdd..ae423d799d7 100644
--- a/clippy_lints/src/format_args.rs
+++ b/clippy_lints/src/format_args.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::higher::{FormatArgsArg, FormatArgsExpn, FormatExpn};
+use clippy_utils::macros::{FormatArgsArg, FormatArgsExpn};
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::implements_trait;
 use clippy_utils::{is_diag_trait_item, match_def_path, paths};
@@ -83,7 +83,7 @@ const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[sym::format_macro, sym::std_panic_m
 impl<'tcx> LateLintPass<'tcx> for FormatArgs {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
         if_chain! {
-            if let Some(format_args) = FormatArgsExpn::parse(expr);
+            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);
             if let Some(macro_def_id) = outermost_expn_data.macro_def_id;
@@ -97,7 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
             if let Some(args) = format_args.args();
             then {
                 for (i, arg) in args.iter().enumerate() {
-                    if !arg.is_display() {
+                    if arg.format_trait != sym::Display {
                         continue;
                     }
                     if arg.has_string_formatting() {
@@ -106,8 +106,8 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
                     if is_aliased(&args, i) {
                         continue;
                     }
-                    check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg);
-                    check_to_string_in_format_args(cx, name, arg);
+                    check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.value);
+                    check_to_string_in_format_args(cx, name, arg.value);
                 }
             }
         }
@@ -122,30 +122,31 @@ fn outermost_expn_data(expn_data: ExpnData) -> ExpnData {
     }
 }
 
-fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symbol, arg: &FormatArgsArg<'_>) {
-    if_chain! {
-        if FormatExpn::parse(arg.value).is_some();
-        if !arg.value.span.ctxt().outer_expn_data().call_site.from_expansion();
-        then {
-            span_lint_and_then(
-                cx,
-                FORMAT_IN_FORMAT_ARGS,
-                call_site,
-                &format!("`format!` in `{}!` args", name),
-                |diag| {
-                    diag.help(&format!(
-                        "combine the `format!(..)` arguments with the outer `{}!(..)` call",
-                        name
-                    ));
-                    diag.help("or consider changing `format!` to `format_args!`");
-                },
-            );
-        }
+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;
     }
+    let Some(mac_id) = expn_data.macro_def_id else { return };
+    if !cx.tcx.is_diagnostic_item(sym::format_macro, mac_id) {
+        return;
+    }
+    span_lint_and_then(
+        cx,
+        FORMAT_IN_FORMAT_ARGS,
+        call_site,
+        &format!("`format!` in `{}!` args", name),
+        |diag| {
+            diag.help(&format!(
+                "combine the `format!(..)` arguments with the outer `{}!(..)` call",
+                name
+            ));
+            diag.help("or consider changing `format!` to `format_args!`");
+        },
+    );
 }
 
-fn check_to_string_in_format_args<'tcx>(cx: &LateContext<'tcx>, name: Symbol, arg: &FormatArgsArg<'tcx>) {
-    let value = arg.value;
+fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Expr<'_>) {
     if_chain! {
         if !value.span.from_expansion();
         if let ExprKind::MethodCall(_, _, [receiver], _) = value.kind;
diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs
index 866ff216f84..5ece2cc5ac4 100644
--- a/clippy_lints/src/from_over_into.rs
+++ b/clippy_lints/src/from_over_into.rs
@@ -53,7 +53,7 @@ impl FromOverInto {
 
 impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]);
 
-impl LateLintPass<'_> for FromOverInto {
+impl<'tcx> LateLintPass<'tcx> for FromOverInto {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
         if !meets_msrv(self.msrv.as_ref(), &msrvs::RE_REBALANCING_COHERENCE) {
             return;
diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs
index 73e800073b0..57b07513205 100644
--- a/clippy_lints/src/from_str_radix_10.rs
+++ b/clippy_lints/src/from_str_radix_10.rs
@@ -43,7 +43,7 @@ declare_clippy_lint! {
 
 declare_lint_pass!(FromStrRadix10 => [FROM_STR_RADIX_10]);
 
-impl LateLintPass<'tcx> for FromStrRadix10 {
+impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, exp: &Expr<'tcx>) {
         if_chain! {
             if let ExprKind::Call(maybe_path, arguments) = &exp.kind;
diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs
index 77d08081c07..f2b4aefaead 100644
--- a/clippy_lints/src/functions/must_use.rs
+++ b/clippy_lints/src/functions/must_use.rs
@@ -18,7 +18,7 @@ use clippy_utils::{match_def_path, must_use_attr, return_ty, trait_ref_of_method
 
 use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT};
 
-pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
+pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
     let attrs = cx.tcx.hir().attrs(item.hir_id());
     let attr = must_use_attr(attrs);
     if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind {
@@ -40,7 +40,7 @@ pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
     }
 }
 
-pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
+pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
     if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind {
         let is_public = cx.access_levels.is_exported(item.def_id);
         let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
@@ -62,7 +62,7 @@ pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<
     }
 }
 
-pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
+pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
     if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind {
         let is_public = cx.access_levels.is_exported(item.def_id);
         let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
diff --git a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs
index f83789bb219..6d829a18b2e 100644
--- a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs
+++ b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs
@@ -9,7 +9,7 @@ use clippy_utils::{iter_input_pats, path_to_local};
 
 use super::NOT_UNSAFE_PTR_ARG_DEREF;
 
-pub(super) fn check_fn(
+pub(super) fn check_fn<'tcx>(
     cx: &LateContext<'tcx>,
     kind: intravisit::FnKind<'tcx>,
     decl: &'tcx hir::FnDecl<'tcx>,
@@ -25,14 +25,14 @@ pub(super) fn check_fn(
     check_raw_ptr(cx, unsafety, decl, body, cx.tcx.hir().local_def_id(hir_id));
 }
 
-pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
+pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
     if let hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(eid)) = item.kind {
         let body = cx.tcx.hir().body(eid);
         check_raw_ptr(cx, sig.header.unsafety, sig.decl, body, item.def_id);
     }
 }
 
-fn check_raw_ptr(
+fn check_raw_ptr<'tcx>(
     cx: &LateContext<'tcx>,
     unsafety: hir::Unsafety,
     decl: &'tcx hir::FnDecl<'tcx>,
diff --git a/clippy_lints/src/functions/result_unit_err.rs b/clippy_lints/src/functions/result_unit_err.rs
index 71f6f87ae60..73f08a04989 100644
--- a/clippy_lints/src/functions/result_unit_err.rs
+++ b/clippy_lints/src/functions/result_unit_err.rs
@@ -13,7 +13,7 @@ use clippy_utils::ty::is_type_diagnostic_item;
 
 use super::RESULT_UNIT_ERR;
 
-pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
+pub(super) fn check_item(cx: &LateContext<'_>, item: &hir::Item<'_>) {
     if let hir::ItemKind::Fn(ref sig, ref _generics, _) = item.kind {
         let is_public = cx.access_levels.is_exported(item.def_id);
         let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
@@ -23,7 +23,7 @@ pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
     }
 }
 
-pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
+pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &hir::ImplItem<'_>) {
     if let hir::ImplItemKind::Fn(ref sig, _) = item.kind {
         let is_public = cx.access_levels.is_exported(item.def_id);
         let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
@@ -33,7 +33,7 @@ pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<
     }
 }
 
-pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
+pub(super) fn check_trait_item(cx: &LateContext<'_>, item: &hir::TraitItem<'_>) {
     if let hir::TraitItemKind::Fn(ref sig, _) = item.kind {
         let is_public = cx.access_levels.is_exported(item.def_id);
         let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
diff --git a/clippy_lints/src/functions/too_many_arguments.rs b/clippy_lints/src/functions/too_many_arguments.rs
index 63a14d8d4cd..3af960491ed 100644
--- a/clippy_lints/src/functions/too_many_arguments.rs
+++ b/clippy_lints/src/functions/too_many_arguments.rs
@@ -9,9 +9,9 @@ use clippy_utils::is_trait_impl_item;
 use super::TOO_MANY_ARGUMENTS;
 
 pub(super) fn check_fn(
-    cx: &LateContext<'tcx>,
-    kind: intravisit::FnKind<'tcx>,
-    decl: &'tcx hir::FnDecl<'_>,
+    cx: &LateContext<'_>,
+    kind: intravisit::FnKind<'_>,
+    decl: &hir::FnDecl<'_>,
     span: Span,
     hir_id: hir::HirId,
     too_many_arguments_threshold: u64,
@@ -39,11 +39,7 @@ pub(super) fn check_fn(
     }
 }
 
-pub(super) fn check_trait_item(
-    cx: &LateContext<'tcx>,
-    item: &'tcx hir::TraitItem<'_>,
-    too_many_arguments_threshold: u64,
-) {
+pub(super) fn check_trait_item(cx: &LateContext<'_>, item: &hir::TraitItem<'_>, too_many_arguments_threshold: u64) {
     if let hir::TraitItemKind::Fn(ref sig, _) = item.kind {
         // don't lint extern functions decls, it's not their fault
         if sig.header.abi == Abi::Rust {
diff --git a/clippy_lints/src/functions/too_many_lines.rs b/clippy_lints/src/functions/too_many_lines.rs
index 65efbbab41a..54bdea7ea25 100644
--- a/clippy_lints/src/functions/too_many_lines.rs
+++ b/clippy_lints/src/functions/too_many_lines.rs
@@ -11,9 +11,9 @@ use super::TOO_MANY_LINES;
 
 pub(super) fn check_fn(
     cx: &LateContext<'_>,
-    kind: FnKind<'tcx>,
+    kind: FnKind<'_>,
     span: Span,
-    body: &'tcx hir::Body<'_>,
+    body: &hir::Body<'_>,
     too_many_lines_threshold: u64,
 ) {
     // Closures must be contained in a parent body, which will be checked for `too_many_lines`.
diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs
index 16e5c5ca603..9525c163ece 100644
--- a/clippy_lints/src/if_then_some_else_none.rs
+++ b/clippy_lints/src/if_then_some_else_none.rs
@@ -55,7 +55,7 @@ impl IfThenSomeElseNone {
 
 impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]);
 
-impl LateLintPass<'_> for IfThenSomeElseNone {
+impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
         if !meets_msrv(self.msrv.as_ref(), &msrvs::BOOL_THEN) {
             return;
diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs
index 07caeada80d..d650d6e9a85 100644
--- a/clippy_lints/src/implicit_return.rs
+++ b/clippy_lints/src/implicit_return.rs
@@ -94,8 +94,8 @@ fn get_call_site(span: Span, ctxt: SyntaxContext) -> Option<Span> {
 }
 
 fn lint_implicit_returns(
-    cx: &LateContext<'tcx>,
-    expr: &'tcx Expr<'_>,
+    cx: &LateContext<'_>,
+    expr: &Expr<'_>,
     // The context of the function body.
     ctxt: SyntaxContext,
     // Whether the expression is from a macro expansion.
diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs
index 388bb3727f9..3d44a669d8f 100644
--- a/clippy_lints/src/inconsistent_struct_constructor.rs
+++ b/clippy_lints/src/inconsistent_struct_constructor.rs
@@ -63,7 +63,7 @@ declare_clippy_lint! {
 
 declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRUCTOR]);
 
-impl LateLintPass<'_> for InconsistentStructConstructor {
+impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
         if_chain! {
             if !expr.span.from_expansion();
diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs
index 69f1c90beec..073313e2bad 100644
--- a/clippy_lints/src/index_refutable_slice.rs
+++ b/clippy_lints/src/index_refutable_slice.rs
@@ -69,7 +69,7 @@ impl IndexRefutableSlice {
 
 impl_lint_pass!(IndexRefutableSlice => [INDEX_REFUTABLE_SLICE]);
 
-impl LateLintPass<'_> for IndexRefutableSlice {
+impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
         if_chain! {
             if !expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some();
diff --git a/clippy_lints/src/init_numbered_fields.rs b/clippy_lints/src/init_numbered_fields.rs
index 5fe6725b581..9284e002409 100644
--- a/clippy_lints/src/init_numbered_fields.rs
+++ b/clippy_lints/src/init_numbered_fields.rs
@@ -1,5 +1,4 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::in_macro;
 use clippy_utils::source::snippet_with_applicability;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
@@ -46,7 +45,7 @@ impl<'tcx> LateLintPass<'tcx> for NumberedFields {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
         if let ExprKind::Struct(path, fields, None) = e.kind {
             if !fields.is_empty()
-                && !in_macro(e.span)
+                && !e.span.from_expansion()
                 && fields
                     .iter()
                     .all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit))
diff --git a/clippy_lints/src/iter_not_returning_iterator.rs b/clippy_lints/src/iter_not_returning_iterator.rs
index 0af6b3b7d46..d3bdc819a9f 100644
--- a/clippy_lints/src/iter_not_returning_iterator.rs
+++ b/clippy_lints/src/iter_not_returning_iterator.rs
@@ -1,8 +1,7 @@
-use clippy_utils::{diagnostics::span_lint, return_ty, ty::implements_trait};
-use rustc_hir::{ImplItem, ImplItemKind};
+use clippy_utils::{diagnostics::span_lint, get_parent_node, ty::implements_trait};
+use rustc_hir::{def_id::LocalDefId, FnSig, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::kw;
 use rustc_span::symbol::sym;
 
 declare_clippy_lint! {
@@ -40,26 +39,52 @@ declare_clippy_lint! {
 
 declare_lint_pass!(IterNotReturningIterator => [ITER_NOT_RETURNING_ITERATOR]);
 
-impl LateLintPass<'_> for IterNotReturningIterator {
-    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'tcx>) {
-        let name = impl_item.ident.name.as_str();
-        if_chain! {
-            if let ImplItemKind::Fn(fn_sig, _) = &impl_item.kind;
-            let ret_ty = return_ty(cx, impl_item.hir_id());
-            if matches!(name, "iter" | "iter_mut");
-            if let [param] = cx.tcx.fn_arg_names(impl_item.def_id);
-            if param.name == kw::SelfLower;
-            if let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
-            if !implements_trait(cx, ret_ty, iter_trait_id, &[]);
+impl<'tcx> LateLintPass<'tcx> for IterNotReturningIterator {
+    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
+        let name = item.ident.name.as_str();
+        if matches!(name, "iter" | "iter_mut") {
+            if let TraitItemKind::Fn(fn_sig, _) = &item.kind {
+                check_sig(cx, name, fn_sig, item.def_id);
+            }
+        }
+    }
 
-            then {
-                span_lint(
-                    cx,
-                    ITER_NOT_RETURNING_ITERATOR,
-                    fn_sig.span,
-                    &format!("this method is named `{}` but its return type does not implement `Iterator`", name),
-                );
+    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'tcx>) {
+        let name = item.ident.name.as_str();
+        if matches!(name, "iter" | "iter_mut")
+            && !matches!(
+                get_parent_node(cx.tcx, item.hir_id()),
+                Some(Node::Item(Item { kind: ItemKind::Impl(i), .. })) if i.of_trait.is_some()
+            )
+        {
+            if let ImplItemKind::Fn(fn_sig, _) = &item.kind {
+                check_sig(cx, name, fn_sig, item.def_id);
             }
         }
     }
 }
+
+fn check_sig(cx: &LateContext<'_>, name: &str, sig: &FnSig<'_>, fn_id: LocalDefId) {
+    if sig.decl.implicit_self.has_implicit_self() {
+        let ret_ty = cx.tcx.fn_sig(fn_id).skip_binder().output();
+        let ret_ty = cx
+            .tcx
+            .try_normalize_erasing_regions(cx.param_env, ret_ty)
+            .unwrap_or(ret_ty);
+        if cx
+            .tcx
+            .get_diagnostic_item(sym::Iterator)
+            .map_or(false, |iter_id| !implements_trait(cx, ret_ty, iter_id, &[]))
+        {
+            span_lint(
+                cx,
+                ITER_NOT_RETURNING_ITERATOR,
+                sig.span,
+                &format!(
+                    "this method is named `{}` but its return type does not implement `Iterator`",
+                    name
+                ),
+            );
+        }
+    }
+}
diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs
index 64f6d62fbdc..e1168c3f602 100644
--- a/clippy_lints/src/len_zero.rs
+++ b/clippy_lints/src/len_zero.rs
@@ -245,7 +245,7 @@ enum LenOutput<'tcx> {
     Option(DefId),
     Result(DefId, Ty<'tcx>),
 }
-fn parse_len_output(cx: &LateContext<'_>, sig: FnSig<'tcx>) -> Option<LenOutput<'tcx>> {
+fn parse_len_output<'tcx>(cx: &LateContext<'_>, sig: FnSig<'tcx>) -> Option<LenOutput<'tcx>> {
     match *sig.output().kind() {
         ty::Int(_) | ty::Uint(_) => Some(LenOutput::Integral),
         ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Option, adt.did) => {
diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs
index 944411087e9..26fb4259952 100644
--- a/clippy_lints/src/lib.register_all.rs
+++ b/clippy_lints/src/lib.register_all.rs
@@ -37,6 +37,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(derivable_impls::DERIVABLE_IMPLS),
     LintId::of(derive::DERIVE_HASH_XOR_EQ),
     LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD),
+    LintId::of(disallowed_methods::DISALLOWED_METHODS),
+    LintId::of(disallowed_types::DISALLOWED_TYPES),
     LintId::of(doc::MISSING_SAFETY_DOC),
     LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
     LintId::of(double_comparison::DOUBLE_COMPARISONS),
@@ -113,6 +115,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(loops::WHILE_LET_ON_ITERATOR),
     LintId::of(main_recursion::MAIN_RECURSION),
     LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
+    LintId::of(manual_bits::MANUAL_BITS),
     LintId::of(manual_map::MANUAL_MAP),
     LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
     LintId::of(manual_strip::MANUAL_STRIP),
@@ -204,7 +207,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(mut_key::MUTABLE_KEY_TYPE),
     LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
     LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
-    LintId::of(mutex_atomic::MUTEX_ATOMIC),
     LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
     LintId::of(needless_bool::BOOL_COMPARISON),
     LintId::of(needless_bool::NEEDLESS_BOOL),
diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs
index 002122793f3..746bdb19c3d 100644
--- a/clippy_lints/src/lib.register_lints.rs
+++ b/clippy_lints/src/lib.register_lints.rs
@@ -3,33 +3,33 @@
 // Manual edits will be overwritten.
 
 store.register_lints(&[
-    #[cfg(feature = "internal-lints")]
+    #[cfg(feature = "internal")]
     utils::internal_lints::CLIPPY_LINTS_INTERNAL,
-    #[cfg(feature = "internal-lints")]
+    #[cfg(feature = "internal")]
     utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS,
-    #[cfg(feature = "internal-lints")]
+    #[cfg(feature = "internal")]
     utils::internal_lints::COMPILER_LINT_FUNCTIONS,
-    #[cfg(feature = "internal-lints")]
+    #[cfg(feature = "internal")]
     utils::internal_lints::DEFAULT_LINT,
-    #[cfg(feature = "internal-lints")]
+    #[cfg(feature = "internal")]
     utils::internal_lints::IF_CHAIN_STYLE,
-    #[cfg(feature = "internal-lints")]
+    #[cfg(feature = "internal")]
     utils::internal_lints::INTERNING_DEFINED_SYMBOL,
-    #[cfg(feature = "internal-lints")]
+    #[cfg(feature = "internal")]
     utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE,
-    #[cfg(feature = "internal-lints")]
+    #[cfg(feature = "internal")]
     utils::internal_lints::INVALID_PATHS,
-    #[cfg(feature = "internal-lints")]
+    #[cfg(feature = "internal")]
     utils::internal_lints::LINT_WITHOUT_LINT_PASS,
-    #[cfg(feature = "internal-lints")]
+    #[cfg(feature = "internal")]
     utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
-    #[cfg(feature = "internal-lints")]
+    #[cfg(feature = "internal")]
     utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE,
-    #[cfg(feature = "internal-lints")]
+    #[cfg(feature = "internal")]
     utils::internal_lints::OUTER_EXPN_EXPN_DATA,
-    #[cfg(feature = "internal-lints")]
+    #[cfg(feature = "internal")]
     utils::internal_lints::PRODUCE_ICE,
-    #[cfg(feature = "internal-lints")]
+    #[cfg(feature = "internal")]
     utils::internal_lints::UNNECESSARY_SYMBOL_STR,
     absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS,
     approx_const::APPROX_CONSTANT,
@@ -59,6 +59,7 @@ store.register_lints(&[
     bool_assert_comparison::BOOL_ASSERT_COMPARISON,
     booleans::LOGIC_BUG,
     booleans::NONMINIMAL_BOOL,
+    borrow_as_ptr::BORROW_AS_PTR,
     bytecount::NAIVE_BYTECOUNT,
     cargo_common_metadata::CARGO_COMMON_METADATA,
     case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
@@ -225,6 +226,7 @@ store.register_lints(&[
     main_recursion::MAIN_RECURSION,
     manual_assert::MANUAL_ASSERT,
     manual_async_fn::MANUAL_ASYNC_FN,
+    manual_bits::MANUAL_BITS,
     manual_map::MANUAL_MAP,
     manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
     manual_ok_or::MANUAL_OK_OR,
@@ -435,6 +437,7 @@ store.register_lints(&[
     shadow::SHADOW_REUSE,
     shadow::SHADOW_SAME,
     shadow::SHADOW_UNRELATED,
+    single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES,
     single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
     size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT,
     slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs
index e3cf0670018..a7353790100 100644
--- a/clippy_lints/src/lib.register_nursery.rs
+++ b/clippy_lints/src/lib.register_nursery.rs
@@ -6,8 +6,6 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
     LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR),
     LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY),
     LintId::of(copies::BRANCHES_SHARING_CODE),
-    LintId::of(disallowed_methods::DISALLOWED_METHODS),
-    LintId::of(disallowed_types::DISALLOWED_TYPES),
     LintId::of(equatable_if_let::EQUATABLE_IF_LET),
     LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM),
     LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS),
@@ -17,6 +15,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
     LintId::of(let_if_seq::USELESS_LET_IF_SEQ),
     LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN),
     LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
+    LintId::of(mutex_atomic::MUTEX_ATOMIC),
     LintId::of(mutex_atomic::MUTEX_INTEGER),
     LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
     LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs
index 70a4a624378..1744b7c8250 100644
--- a/clippy_lints/src/lib.register_pedantic.rs
+++ b/clippy_lints/src/lib.register_pedantic.rs
@@ -7,6 +7,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
     LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
     LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
     LintId::of(bit_mask::VERBOSE_BIT_MASK),
+    LintId::of(borrow_as_ptr::BORROW_AS_PTR),
     LintId::of(bytecount::NAIVE_BYTECOUNT),
     LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
     LintId::of(casts::CAST_LOSSLESS),
diff --git a/clippy_lints/src/lib.register_perf.rs b/clippy_lints/src/lib.register_perf.rs
index 2ea0b696f1f..f9ffd4cf829 100644
--- a/clippy_lints/src/lib.register_perf.rs
+++ b/clippy_lints/src/lib.register_perf.rs
@@ -19,7 +19,6 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
     LintId::of(methods::SINGLE_CHAR_PATTERN),
     LintId::of(methods::UNNECESSARY_TO_OWNED),
     LintId::of(misc::CMP_OWNED),
-    LintId::of(mutex_atomic::MUTEX_ATOMIC),
     LintId::of(redundant_clone::REDUNDANT_CLONE),
     LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
     LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs
index eab389a9bd8..e7e2798da7d 100644
--- a/clippy_lints/src/lib.register_restriction.rs
+++ b/clippy_lints/src/lib.register_restriction.rs
@@ -54,6 +54,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
     LintId::of(shadow::SHADOW_REUSE),
     LintId::of(shadow::SHADOW_SAME),
     LintId::of(shadow::SHADOW_UNRELATED),
+    LintId::of(single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES),
     LintId::of(strings::STRING_ADD),
     LintId::of(strings::STRING_SLICE),
     LintId::of(strings::STRING_TO_STRING),
diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs
index 1a0b869d40a..05211476ff2 100644
--- a/clippy_lints/src/lib.register_style.rs
+++ b/clippy_lints/src/lib.register_style.rs
@@ -16,6 +16,8 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
     LintId::of(comparison_chain::COMPARISON_CHAIN),
     LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
     LintId::of(dereference::NEEDLESS_BORROW),
+    LintId::of(disallowed_methods::DISALLOWED_METHODS),
+    LintId::of(disallowed_types::DISALLOWED_TYPES),
     LintId::of(doc::MISSING_SAFETY_DOC),
     LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
     LintId::of(enum_variants::ENUM_VARIANT_NAMES),
@@ -41,6 +43,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
     LintId::of(loops::WHILE_LET_ON_ITERATOR),
     LintId::of(main_recursion::MAIN_RECURSION),
     LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
+    LintId::of(manual_bits::MANUAL_BITS),
     LintId::of(manual_map::MANUAL_MAP),
     LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
     LintId::of(map_clone::MAP_CLONE),
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index d4687a1e287..79e9882fef4 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -4,7 +4,6 @@
 #![feature(box_patterns)]
 #![feature(control_flow_enum)]
 #![feature(drain_filter)]
-#![feature(in_band_lifetimes)]
 #![feature(iter_intersperse)]
 #![feature(let_else)]
 #![feature(once_cell)]
@@ -153,12 +152,9 @@ macro_rules! declare_clippy_lint {
     };
 }
 
-#[cfg(feature = "metadata-collector-lint")]
+#[cfg(feature = "internal")]
 mod deprecated_lints;
-#[cfg_attr(
-    any(feature = "internal-lints", feature = "metadata-collector-lint"),
-    allow(clippy::missing_clippy_version_attribute)
-)]
+#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
 mod utils;
 
 // begin lints modules, do not remove this comment, it’s used in `update_lints`
@@ -177,6 +173,7 @@ mod blacklisted_name;
 mod blocks_in_if_conditions;
 mod bool_assert_comparison;
 mod booleans;
+mod borrow_as_ptr;
 mod bytecount;
 mod cargo_common_metadata;
 mod case_sensitive_file_extension_comparisons;
@@ -264,6 +261,7 @@ mod macro_use;
 mod main_recursion;
 mod manual_assert;
 mod manual_async_fn;
+mod manual_bits;
 mod manual_map;
 mod manual_non_exhaustive;
 mod manual_ok_or;
@@ -351,6 +349,7 @@ mod self_named_constructors;
 mod semicolon_if_nothing_returned;
 mod serde_api;
 mod shadow;
+mod single_char_lifetime_names;
 mod single_component_path_imports;
 mod size_of_in_element_count;
 mod slow_vector_initialization;
@@ -471,7 +470,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     include!("lib.register_restriction.rs");
     include!("lib.register_pedantic.rs");
 
-    #[cfg(feature = "internal-lints")]
+    #[cfg(feature = "internal")]
     include!("lib.register_internal.rs");
 
     include!("lib.register_all.rs");
@@ -483,7 +482,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     include!("lib.register_cargo.rs");
     include!("lib.register_nursery.rs");
 
-    #[cfg(feature = "metadata-collector-lint")]
+    #[cfg(feature = "internal")]
     {
         if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) {
             store.register_late_pass(|| Box::new(utils::internal_lints::metadata_collector::MetadataCollector::new()));
@@ -492,7 +491,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     }
 
     // all the internal lints
-    #[cfg(feature = "internal-lints")]
+    #[cfg(feature = "internal")]
     {
         store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal));
         store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce));
@@ -858,6 +857,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(needless_late_init::NeedlessLateInit));
     store.register_late_pass(|| Box::new(return_self_not_must_use::ReturnSelfNotMustUse));
     store.register_late_pass(|| Box::new(init_numbered_fields::NumberedFields));
+    store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames));
+    store.register_late_pass(move || Box::new(borrow_as_ptr::BorrowAsPtr::new(msrv)));
+    store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv)));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/clippy_lints/src/loops/empty_loop.rs b/clippy_lints/src/loops/empty_loop.rs
index dda09fecdf9..823cf0f4322 100644
--- a/clippy_lints/src/loops/empty_loop.rs
+++ b/clippy_lints/src/loops/empty_loop.rs
@@ -5,7 +5,7 @@ use clippy_utils::{is_in_panic_handler, is_no_std_crate};
 use rustc_hir::{Block, Expr};
 use rustc_lint::LateContext;
 
-pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) {
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, loop_block: &Block<'_>) {
     if loop_block.stmts.is_empty() && loop_block.expr.is_none() && !is_in_panic_handler(cx, expr) {
         let msg = "empty `loop {}` wastes CPU cycles";
         let help = if is_no_std_crate(cx) {
diff --git a/clippy_lints/src/loops/explicit_into_iter_loop.rs b/clippy_lints/src/loops/explicit_into_iter_loop.rs
index 1bab0d99b69..17246cc5426 100644
--- a/clippy_lints/src/loops/explicit_into_iter_loop.rs
+++ b/clippy_lints/src/loops/explicit_into_iter_loop.rs
@@ -8,7 +8,7 @@ use rustc_lint::LateContext;
 use rustc_middle::ty::TyS;
 use rustc_span::symbol::sym;
 
-pub(super) fn check(cx: &LateContext<'_>, self_arg: &'hir Expr<'hir>, call_expr: &Expr<'_>) {
+pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, call_expr: &Expr<'_>) {
     let self_ty = cx.typeck_results().expr_ty(self_arg);
     let self_ty_adjusted = cx.typeck_results().expr_ty_adjusted(self_arg);
     if !(TyS::same_type(self_ty, self_ty_adjusted) && is_trait_method(cx, call_expr, sym::IntoIterator)) {
diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs
index c62fa5e998b..48c4015e07b 100644
--- a/clippy_lints/src/loops/manual_memcpy.rs
+++ b/clippy_lints/src/loops/manual_memcpy.rs
@@ -2,7 +2,7 @@ use super::{IncrementVisitor, InitializeVisitor, MANUAL_MEMCPY};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet;
 use clippy_utils::sugg::Sugg;
-use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::ty::is_copy;
 use clippy_utils::{get_enclosing_block, higher, path_to_local, sugg};
 use if_chain::if_chain;
 use rustc_ast::ast;
@@ -62,15 +62,15 @@ pub(super) fn check<'tcx>(
                         if_chain! {
                             if let ExprKind::Index(base_left, idx_left) = lhs.kind;
                             if let ExprKind::Index(base_right, idx_right) = rhs.kind;
-                            if is_slice_like(cx, cx.typeck_results().expr_ty(base_left));
-                            if is_slice_like(cx, cx.typeck_results().expr_ty(base_right));
+                            if let Some(ty) = get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_left));
+                            if get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_right)).is_some();
                             if let Some((start_left, offset_left)) = get_details_from_idx(cx, idx_left, &starts);
                             if let Some((start_right, offset_right)) = get_details_from_idx(cx, idx_right, &starts);
 
                             // Source and destination must be different
                             if path_to_local(base_left) != path_to_local(base_right);
                             then {
-                                Some((IndexExpr { base: base_left, idx: start_left, idx_offset: offset_left },
+                                Some((ty, IndexExpr { base: base_left, idx: start_left, idx_offset: offset_left },
                                     IndexExpr { base: base_right, idx: start_right, idx_offset: offset_right }))
                             } else {
                                 None
@@ -78,7 +78,7 @@ pub(super) fn check<'tcx>(
                         }
                     })
                 })
-                .map(|o| o.map(|(dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, &dst, &src)))
+                .map(|o| o.map(|(ty, dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, ty, &dst, &src)))
                 .collect::<Option<Vec<_>>>()
                 .filter(|v| !v.is_empty())
                 .map(|v| v.join("\n    "));
@@ -105,6 +105,7 @@ fn build_manual_memcpy_suggestion<'tcx>(
     start: &Expr<'_>,
     end: &Expr<'_>,
     limits: ast::RangeLimits,
+    elem_ty: Ty<'tcx>,
     dst: &IndexExpr<'_>,
     src: &IndexExpr<'_>,
 ) -> String {
@@ -187,9 +188,16 @@ fn build_manual_memcpy_suggestion<'tcx>(
         .into()
     };
 
+    let method_str = if is_copy(cx, elem_ty) {
+        "copy_from_slice"
+    } else {
+        "clone_from_slice"
+    };
+
     format!(
-        "{}.clone_from_slice(&{}[{}..{}]);",
+        "{}.{}(&{}[{}..{}]);",
         dst,
+        method_str,
         src_base_str,
         src_offset.maybe_par(),
         src_limit.maybe_par()
@@ -203,7 +211,7 @@ fn build_manual_memcpy_suggestion<'tcx>(
 #[derive(Clone)]
 struct MinifyingSugg<'a>(Sugg<'a>);
 
-impl Display for MinifyingSugg<'a> {
+impl<'a> Display for MinifyingSugg<'a> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         self.0.fmt(f)
     }
@@ -324,14 +332,13 @@ struct Start<'hir> {
     kind: StartKind<'hir>,
 }
 
-fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'_>) -> bool {
-    let is_slice = match ty.kind() {
-        ty::Ref(_, subty, _) => is_slice_like(cx, subty),
-        ty::Slice(..) | ty::Array(..) => true,
-        _ => false,
-    };
-
-    is_slice || is_type_diagnostic_item(cx, ty, sym::Vec) || is_type_diagnostic_item(cx, ty, sym::VecDeque)
+fn get_slice_like_element_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
+    match ty.kind() {
+        ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Vec, adt.did) => Some(subs.type_at(0)),
+        ty::Ref(_, subty, _) => get_slice_like_element_ty(cx, subty),
+        ty::Slice(ty) | ty::Array(ty, _) => Some(ty),
+        _ => None,
+    }
 }
 
 fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs
index aedf0844937..37a57d8feb1 100644
--- a/clippy_lints/src/loops/mut_range_bound.rs
+++ b/clippy_lints/src/loops/mut_range_bound.rs
@@ -147,7 +147,7 @@ impl BreakAfterExprVisitor {
     }
 }
 
-impl intravisit::Visitor<'tcx> for BreakAfterExprVisitor {
+impl<'tcx> intravisit::Visitor<'tcx> for BreakAfterExprVisitor {
     type Map = Map<'tcx>;
 
     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs
index ba895f35faa..6248680aa62 100644
--- a/clippy_lints/src/loops/needless_collect.rs
+++ b/clippy_lints/src/loops/needless_collect.rs
@@ -339,8 +339,8 @@ fn detect_iter_and_into_iters<'tcx: 'a, 'a>(
     }
 }
 
-fn get_captured_ids(cx: &LateContext<'tcx>, ty: &'_ TyS<'_>) -> HirIdSet {
-    fn get_captured_ids_recursive(cx: &LateContext<'tcx>, ty: &'_ TyS<'_>, set: &mut HirIdSet) {
+fn get_captured_ids(cx: &LateContext<'_>, ty: &'_ TyS<'_>) -> HirIdSet {
+    fn get_captured_ids_recursive(cx: &LateContext<'_>, ty: &'_ TyS<'_>, set: &mut HirIdSet) {
         match ty.kind() {
             ty::Adt(_, generics) => {
                 for generic in *generics {
diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs
index a3aa6be6afd..bb1b3e2a1ec 100644
--- a/clippy_lints/src/loops/never_loop.rs
+++ b/clippy_lints/src/loops/never_loop.rs
@@ -10,8 +10,8 @@ use rustc_span::Span;
 use std::iter::{once, Iterator};
 
 pub(super) fn check(
-    cx: &LateContext<'tcx>,
-    block: &'tcx Block<'_>,
+    cx: &LateContext<'_>,
+    block: &Block<'_>,
     loop_id: HirId,
     span: Span,
     for_loop: Option<&ForLoop<'_>>,
diff --git a/clippy_lints/src/loops/while_let_loop.rs b/clippy_lints/src/loops/while_let_loop.rs
index 4dcd5c87722..8f57df0be6b 100644
--- a/clippy_lints/src/loops/while_let_loop.rs
+++ b/clippy_lints/src/loops/while_let_loop.rs
@@ -7,7 +7,7 @@ use rustc_hir::{Block, Expr, ExprKind, MatchSource, Pat, StmtKind};
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::lint::in_external_macro;
 
-pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) {
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) {
     // extract the expression from the first statement (if any) in a block
     let inner_stmt_expr = extract_expr_from_first_stmt(loop_block);
     // or extract the first expression (if any) from the block
diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs
index b390476a664..750328d1d01 100644
--- a/clippy_lints/src/loops/while_let_on_iterator.rs
+++ b/clippy_lints/src/loops/while_let_on_iterator.rs
@@ -8,12 +8,13 @@ use clippy_utils::{
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor};
-use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, PatKind, QPath, UnOp};
+use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp};
 use rustc_lint::LateContext;
-use rustc_span::{symbol::sym, Span, Symbol};
+use rustc_middle::ty::adjustment::Adjust;
+use rustc_span::{symbol::sym, Symbol};
 
-pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-    let (scrutinee_expr, iter_expr, some_pat, loop_expr) = if_chain! {
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+    let (scrutinee_expr, iter_expr_struct, iter_expr, some_pat, loop_expr) = if_chain! {
         if let Some(higher::WhileLet { if_then, let_pat, let_expr }) = higher::WhileLet::hir(expr);
         // check for `Some(..)` pattern
         if let PatKind::TupleStruct(QPath::Resolved(None, pat_path), some_pat, _) = let_pat.kind;
@@ -27,7 +28,7 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         // get the loop containing the match expression
         if !uses_iter(cx, &iter_expr_struct, if_then);
         then {
-            (let_expr, iter_expr_struct, some_pat, expr)
+            (let_expr, iter_expr_struct, iter_expr, some_pat, expr)
         } else {
             return;
         }
@@ -47,7 +48,11 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
     // If the iterator is a field or the iterator is accessed after the loop is complete it needs to be
     // borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used
     // afterwards a mutable borrow of a field isn't necessary.
-    let by_ref = if !iter_expr.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr, loop_expr) {
+    let by_ref = if cx.typeck_results().expr_ty(iter_expr).ref_mutability() == Some(Mutability::Mut)
+        || !iter_expr_struct.can_move
+        || !iter_expr_struct.fields.is_empty()
+        || needs_mutable_borrow(cx, &iter_expr_struct, loop_expr)
+    {
         ".by_ref()"
     } else {
         ""
@@ -67,26 +72,36 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 
 #[derive(Debug)]
 struct IterExpr {
-    /// The span of the whole expression, not just the path and fields stored here.
-    span: Span,
     /// The fields used, in order of child to parent.
     fields: Vec<Symbol>,
     /// The path being used.
     path: Res,
+    /// Whether or not the iterator can be moved.
+    can_move: bool,
 }
 
 /// Parses any expression to find out which field of which variable is used. Will return `None` if
 /// the expression might have side effects.
 fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExpr> {
-    let span = e.span;
     let mut fields = Vec::new();
+    let mut can_move = true;
     loop {
+        if cx
+            .typeck_results()
+            .expr_adjustments(e)
+            .iter()
+            .any(|a| matches!(a.kind, Adjust::Deref(Some(..))))
+        {
+            // Custom deref impls need to borrow the whole value as it's captured by reference
+            can_move = false;
+            fields.clear();
+        }
         match e.kind {
             ExprKind::Path(ref path) => {
                 break Some(IterExpr {
-                    span,
                     fields,
                     path: cx.qpath_res(path, e.hir_id),
+                    can_move,
                 });
             },
             ExprKind::Field(base, name) => {
@@ -99,10 +114,12 @@ fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExp
             // Shouldn't have side effects, but there's no way to trace which field is used. So forget which fields have
             // already been seen.
             ExprKind::Index(base, idx) if !idx.can_have_side_effects() => {
+                can_move = false;
                 fields.clear();
                 e = base;
             },
             ExprKind::Unary(UnOp::Deref, base) => {
+                can_move = false;
                 fields.clear();
                 e = base;
             },
@@ -174,7 +191,7 @@ fn is_expr_same_child_or_parent_field(cx: &LateContext<'_>, expr: &Expr<'_>, fie
 
 /// Strips off all field and path expressions. This will return true if a field or path has been
 /// skipped. Used to skip them after failing to check for equality.
-fn skip_fields_and_path(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool) {
+fn skip_fields_and_path<'tcx>(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool) {
     let mut e = expr;
     let e = loop {
         match e.kind {
@@ -187,13 +204,13 @@ fn skip_fields_and_path(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool
 }
 
 /// Checks if the given expression uses the iterator.
-fn uses_iter(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr<'_>) -> bool {
+fn uses_iter<'tcx>(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr<'_>) -> bool {
     struct V<'a, 'b, 'tcx> {
         cx: &'a LateContext<'tcx>,
         iter_expr: &'b IterExpr,
         uses_iter: bool,
     }
-    impl Visitor<'tcx> for V<'_, '_, 'tcx> {
+    impl<'tcx> Visitor<'tcx> for V<'_, '_, 'tcx> {
         type Map = ErasedMap<'tcx>;
         fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
             NestedVisitorMap::None
@@ -228,7 +245,7 @@ fn uses_iter(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr
 }
 
 #[allow(clippy::too_many_lines)]
-fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr: &'tcx Expr<'_>) -> bool {
+fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: &Expr<'_>) -> bool {
     struct AfterLoopVisitor<'a, 'b, 'tcx> {
         cx: &'a LateContext<'tcx>,
         iter_expr: &'b IterExpr,
@@ -236,7 +253,7 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr:
         after_loop: bool,
         used_iter: bool,
     }
-    impl Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> {
+    impl<'tcx> Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> {
         type Map = ErasedMap<'tcx>;
         fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
             NestedVisitorMap::None
@@ -275,7 +292,7 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr:
         found_local: bool,
         used_after: bool,
     }
-    impl Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> {
+    impl<'a, 'b, 'tcx> Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> {
         type Map = ErasedMap<'tcx>;
 
         fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs
index 41f5a913b31..ca1ccb93bf3 100644
--- a/clippy_lints/src/macro_use.rs
+++ b/clippy_lints/src/macro_use.rs
@@ -1,5 +1,4 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::in_macro;
 use clippy_utils::source::snippet;
 use hir::def::{DefKind, Res};
 use if_chain::if_chain;
@@ -104,34 +103,34 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
                     }
                 }
             } else {
-                if in_macro(item.span) {
+                if item.span.from_expansion() {
                     self.push_unique_macro_pat_ty(cx, item.span);
                 }
             }
         }
     }
     fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) {
-        if in_macro(attr.span) {
+        if attr.span.from_expansion() {
             self.push_unique_macro(cx, attr.span);
         }
     }
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
-        if in_macro(expr.span) {
+        if expr.span.from_expansion() {
             self.push_unique_macro(cx, expr.span);
         }
     }
     fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &hir::Stmt<'_>) {
-        if in_macro(stmt.span) {
+        if stmt.span.from_expansion() {
             self.push_unique_macro(cx, stmt.span);
         }
     }
     fn check_pat(&mut self, cx: &LateContext<'_>, pat: &hir::Pat<'_>) {
-        if in_macro(pat.span) {
+        if pat.span.from_expansion() {
             self.push_unique_macro_pat_ty(cx, pat.span);
         }
     }
     fn check_ty(&mut self, cx: &LateContext<'_>, ty: &hir::Ty<'_>) {
-        if in_macro(ty.span) {
+        if ty.span.from_expansion() {
             self.push_unique_macro_pat_ty(cx, ty.span);
         }
     }
diff --git a/clippy_lints/src/manual_assert.rs b/clippy_lints/src/manual_assert.rs
index 5a2a965716c..26b53ab5d68 100644
--- a/clippy_lints/src/manual_assert.rs
+++ b/clippy_lints/src/manual_assert.rs
@@ -1,11 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::higher::PanicExpn;
+use clippy_utils::macros::{root_macro_call, FormatArgsExpn};
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{is_expn_of, sugg};
+use clippy_utils::{peel_blocks_with_stmt, sugg};
 use rustc_errors::Applicability;
-use rustc_hir::{Block, Expr, ExprKind, StmtKind, UnOp};
+use rustc_hir::{Expr, ExprKind, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -34,65 +35,34 @@ declare_clippy_lint! {
 
 declare_lint_pass!(ManualAssert => [MANUAL_ASSERT]);
 
-impl LateLintPass<'_> for ManualAssert {
-    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+impl<'tcx> LateLintPass<'tcx> for ManualAssert {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
         if_chain! {
-            if let Expr {
-                kind: ExprKind:: If(cond, Expr {
-                    kind: ExprKind::Block(
-                        Block {
-                            stmts: [stmt],
-                            ..
-                        },
-                        _),
-                    ..
-                }, None),
-                ..
-            } = &expr;
-            if is_expn_of(stmt.span, "panic").is_some();
+            if let ExprKind::If(cond, then, None) = expr.kind;
             if !matches!(cond.kind, ExprKind::Let(_));
-            if let StmtKind::Semi(semi) = stmt.kind;
+            if !expr.span.from_expansion();
+            let then = peel_blocks_with_stmt(then);
+            if let Some(macro_call) = root_macro_call(then.span);
+            if cx.tcx.item_name(macro_call.def_id) == sym::panic;
             if !cx.tcx.sess.source_map().is_multiline(cond.span);
-
+            if let Some(format_args) = FormatArgsExpn::find_nested(cx, then, macro_call.expn);
             then {
-                let call = if_chain! {
-                    if let ExprKind::Block(block, _) = semi.kind;
-                    if let Some(init) = block.expr;
-                    then {
-                        init
-                    } else {
-                        semi
-                    }
-                };
-                let span = if let Some(panic_expn) = PanicExpn::parse(call) {
-                    match *panic_expn.format_args.value_args {
-                        [] => panic_expn.format_args.format_string_span,
-                        [.., last] => panic_expn.format_args.format_string_span.to(last.span),
-                    }
-                } else if let ExprKind::Call(_, [format_args]) = call.kind {
-                    format_args.span
-                } else {
-                    return
-                };
                 let mut applicability = Applicability::MachineApplicable;
-                let sugg = snippet_with_applicability(cx, span, "..", &mut applicability);
-                let cond_sugg = if let ExprKind::DropTemps(e, ..) = cond.kind {
-                    if let Expr{kind: ExprKind::Unary(UnOp::Not, not_expr), ..} = e {
-                         sugg::Sugg::hir_with_applicability(cx, not_expr, "..", &mut applicability).maybe_par().to_string()
-                    } else {
-                       format!("!{}", sugg::Sugg::hir_with_applicability(cx, e, "..", &mut applicability).maybe_par())
-                    }
-                } else {
-                   format!("!{}", sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par())
+                let format_args_snip = snippet_with_applicability(cx, format_args.inputs_span(), "..", &mut applicability);
+                let cond = cond.peel_drop_temps();
+                let (cond, not) = match cond.kind {
+                    ExprKind::Unary(UnOp::Not, e) => (e, ""),
+                    _ => (cond, "!"),
                 };
-
+                let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par();
+                let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip});");
                 span_lint_and_sugg(
                     cx,
                     MANUAL_ASSERT,
                     expr.span,
                     "only a `panic!` in `if`-then statement",
                     "try",
-                    format!("assert!({}, {});", cond_sugg, sugg),
+                    sugg,
                     Applicability::MachineApplicable,
                 );
             }
diff --git a/clippy_lints/src/manual_bits.rs b/clippy_lints/src/manual_bits.rs
new file mode 100644
index 00000000000..50bf2527e39
--- /dev/null
+++ b/clippy_lints/src/manual_bits.rs
@@ -0,0 +1,107 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::{match_def_path, meets_msrv, msrvs, paths};
+use rustc_ast::ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::{self, Ty};
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for uses of `std::mem::size_of::<T>() * 8` when
+    /// `T::BITS` is available.
+    ///
+    /// ### Why is this bad?
+    /// Can be written as the shorter `T::BITS`.
+    ///
+    /// ### Example
+    /// ```rust
+    /// std::mem::size_of::<usize>() * 8;
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// usize::BITS;
+    /// ```
+    #[clippy::version = "1.60.0"]
+    pub MANUAL_BITS,
+    style,
+    "manual implementation of `size_of::<T>() * 8` can be simplified with `T::BITS`"
+}
+
+#[derive(Clone)]
+pub struct ManualBits {
+    msrv: Option<RustcVersion>,
+}
+
+impl ManualBits {
+    #[must_use]
+    pub fn new(msrv: Option<RustcVersion>) -> Self {
+        Self { msrv }
+    }
+}
+
+impl_lint_pass!(ManualBits => [MANUAL_BITS]);
+
+impl<'tcx> LateLintPass<'tcx> for ManualBits {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if !meets_msrv(self.msrv.as_ref(), &msrvs::MANUAL_BITS) {
+            return;
+        }
+
+        if_chain! {
+            if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind;
+            if let BinOpKind::Mul = &bin_op.node;
+            if let Some((real_ty, resolved_ty, other_expr)) = get_one_size_of_ty(cx, left_expr, right_expr);
+            if matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_));
+            if let ExprKind::Lit(lit) = &other_expr.kind;
+            if let LitKind::Int(8, _) = lit.node;
+
+            then {
+                span_lint_and_sugg(
+                    cx,
+                    MANUAL_BITS,
+                    expr.span,
+                    "usage of `mem::size_of::<T>()` to obtain the size of `T` in bits",
+                    "consider using",
+                    format!("{}::BITS", snippet_opt(cx, real_ty.span).unwrap()),
+                    Applicability::MachineApplicable,
+                );
+            }
+        }
+    }
+}
+
+fn get_one_size_of_ty<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr1: &'tcx Expr<'_>,
+    expr2: &'tcx Expr<'_>,
+) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>, &'tcx Expr<'tcx>)> {
+    match (get_size_of_ty(cx, expr1), get_size_of_ty(cx, expr2)) {
+        (Some((real_ty, resolved_ty)), None) => Some((real_ty, resolved_ty, expr2)),
+        (None, Some((real_ty, resolved_ty))) => Some((real_ty, resolved_ty, expr1)),
+        _ => None,
+    }
+}
+
+fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>)> {
+    if_chain! {
+        if let ExprKind::Call(count_func, _func_args) = expr.kind;
+        if let ExprKind::Path(ref count_func_qpath) = count_func.kind;
+
+        if let QPath::Resolved(_, count_func_path) = count_func_qpath;
+        if let Some(segment_zero) = count_func_path.segments.get(0);
+        if let Some(args) = segment_zero.args;
+        if let Some(GenericArg::Type(real_ty)) = args.args.get(0);
+
+        if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id();
+        if match_def_path(cx, def_id, &paths::MEM_SIZE_OF);
+        then {
+            cx.typeck_results().node_substs(count_func.hir_id).types().next().map(|resolved_ty| (real_ty, resolved_ty))
+        } else {
+            None
+        }
+    }
+}
diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs
index 34a70ca76c6..8475e367b09 100644
--- a/clippy_lints/src/manual_map.rs
+++ b/clippy_lints/src/manual_map.rs
@@ -45,7 +45,7 @@ declare_clippy_lint! {
 
 declare_lint_pass!(ManualMap => [MANUAL_MAP]);
 
-impl LateLintPass<'_> for ManualMap {
+impl<'tcx> LateLintPass<'tcx> for ManualMap {
     #[allow(clippy::too_many_lines)]
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         let (scrutinee, then_pat, then_body, else_pat, else_body) = match IfLetOrMatch::parse(cx, expr) {
@@ -219,7 +219,7 @@ impl LateLintPass<'_> for ManualMap {
 
 // Checks whether the expression could be passed as a function, or whether a closure is needed.
 // Returns the function to be passed to `map` if it exists.
-fn can_pass_as_func(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
     match expr.kind {
         ExprKind::Call(func, [arg])
             if path_to_local_id(arg, binding)
@@ -251,8 +251,13 @@ struct SomeExpr<'tcx> {
 
 // Try to parse into a recognized `Option` pattern.
 // i.e. `_`, `None`, `Some(..)`, or a reference to any of those.
-fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> {
-    fn f(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ref_count: usize, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> {
+fn try_parse_pattern<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> {
+    fn f<'tcx>(
+        cx: &LateContext<'tcx>,
+        pat: &'tcx Pat<'_>,
+        ref_count: usize,
+        ctxt: SyntaxContext,
+    ) -> Option<OptionPat<'tcx>> {
         match pat.kind {
             PatKind::Wild => Some(OptionPat::Wild),
             PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt),
@@ -269,7 +274,7 @@ fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxCon
 }
 
 // Checks for an expression wrapped by the `Some` constructor. Returns the contained expression.
-fn get_some_expr(
+fn get_some_expr<'tcx>(
     cx: &LateContext<'tcx>,
     expr: &'tcx Expr<'_>,
     needs_unsafe_block: bool,
@@ -306,6 +311,6 @@ fn get_some_expr(
 }
 
 // Checks for the `None` value.
-fn is_none_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     matches!(peel_blocks(expr).kind, ExprKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone))
 }
diff --git a/clippy_lints/src/manual_ok_or.rs b/clippy_lints/src/manual_ok_or.rs
index b60e2dc366b..bd083e3e9e2 100644
--- a/clippy_lints/src/manual_ok_or.rs
+++ b/clippy_lints/src/manual_ok_or.rs
@@ -40,7 +40,7 @@ declare_clippy_lint! {
 
 declare_lint_pass!(ManualOkOr => [MANUAL_OK_OR]);
 
-impl LateLintPass<'_> for ManualOkOr {
+impl<'tcx> LateLintPass<'tcx> for ManualOkOr {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'tcx>) {
         if in_external_macro(cx.sess(), scrutinee.span) {
             return;
diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs
index aac3c6e0de2..b3a91d9f18f 100644
--- a/clippy_lints/src/manual_unwrap_or.rs
+++ b/clippy_lints/src/manual_unwrap_or.rs
@@ -43,7 +43,7 @@ declare_clippy_lint! {
 
 declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]);
 
-impl LateLintPass<'_> for ManualUnwrapOr {
+impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOr {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
         if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) {
             return;
diff --git a/clippy_lints/src/match_str_case_mismatch.rs b/clippy_lints/src/match_str_case_mismatch.rs
index 2c0fc218ca0..1fc7eb72142 100644
--- a/clippy_lints/src/match_str_case_mismatch.rs
+++ b/clippy_lints/src/match_str_case_mismatch.rs
@@ -55,7 +55,7 @@ enum CaseMethod {
     AsciiUppercase,
 }
 
-impl LateLintPass<'_> for MatchStrCaseMismatch {
+impl<'tcx> LateLintPass<'tcx> for MatchStrCaseMismatch {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if_chain! {
             if !in_external_macro(cx.tcx.sess, expr.span);
diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs
index 5fa8f249e70..60dd957db01 100644
--- a/clippy_lints/src/matches.rs
+++ b/clippy_lints/src/matches.rs
@@ -2,16 +2,17 @@ use clippy_utils::consts::{constant, constant_full_int, miri_to_const, FullInt};
 use clippy_utils::diagnostics::{
     multispan_sugg, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
 };
-use clippy_utils::higher;
+use clippy_utils::macros::{is_panic, root_macro_call};
 use clippy_utils::source::{expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability};
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs};
 use clippy_utils::visitors::is_local_used;
 use clippy_utils::{
-    get_parent_expr, is_expn_of, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild, meets_msrv, msrvs,
+    get_parent_expr, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild, meets_msrv, msrvs,
     path_to_local, path_to_local_id, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns,
     strip_pat_refs,
 };
+use clippy_utils::{higher, peel_blocks_with_stmt};
 use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash};
 use core::iter::{once, ExactSizeIterator};
 use if_chain::if_chain;
@@ -974,7 +975,8 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm
                     }
                     if_chain! {
                         if matching_wild;
-                        if is_panic_call(arm.body);
+                        if let Some(macro_call) = root_macro_call(peel_blocks_with_stmt(arm.body).span);
+                        if is_panic(cx, macro_call.def_id);
                         then {
                             // `Err(_)` or `Err(_e)` arm with `panic!` found
                             span_lint_and_note(cx,
@@ -997,7 +999,7 @@ enum CommonPrefixSearcher<'a> {
     Path(&'a [PathSegment<'a>]),
     Mixed,
 }
-impl CommonPrefixSearcher<'a> {
+impl<'a> CommonPrefixSearcher<'a> {
     fn with_path(&mut self, path: &'a [PathSegment<'a>]) {
         match path {
             [path @ .., _] => self.with_prefix(path),
@@ -1179,22 +1181,6 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>])
     };
 }
 
-// If the block contains only a `panic!` macro (as expression or statement)
-fn is_panic_call(expr: &Expr<'_>) -> bool {
-    // Unwrap any wrapping blocks
-    let span = if let ExprKind::Block(block, _) = expr.kind {
-        match (&block.expr, block.stmts.len(), block.stmts.first()) {
-            (&Some(exp), 0, _) => exp.span,
-            (&None, 1, Some(stmt)) => stmt.span,
-            _ => return false,
-        }
-    } else {
-        expr.span
-    };
-
-    is_expn_of(span, "panic").is_some() && is_expn_of(span, "unreachable").is_none()
-}
-
 fn check_match_ref_pats<'a, 'b, I>(cx: &LateContext<'_>, ex: &Expr<'_>, pats: I, expr: &Expr<'_>)
 where
     'b: 'a,
@@ -1818,11 +1804,15 @@ mod redundant_pattern_match {
     /// Checks if the drop order for a type matters. Some std types implement drop solely to
     /// deallocate memory. For these types, and composites containing them, changing the drop order
     /// won't result in any observable side effects.
-    fn type_needs_ordered_drop(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+    fn type_needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
         type_needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
     }
 
-    fn type_needs_ordered_drop_inner(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool {
+    fn type_needs_ordered_drop_inner<'tcx>(
+        cx: &LateContext<'tcx>,
+        ty: Ty<'tcx>,
+        seen: &mut FxHashSet<Ty<'tcx>>,
+    ) -> bool {
         if !seen.insert(ty) {
             return false;
         }
@@ -1884,7 +1874,7 @@ mod redundant_pattern_match {
 
     // Checks if there are any temporaries created in the given expression for which drop order
     // matters.
-    fn temporaries_need_ordered_drop(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+    fn temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
         struct V<'a, 'tcx> {
             cx: &'a LateContext<'tcx>,
             res: bool,
diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs
index 0ec9387f9c4..0f39470f342 100644
--- a/clippy_lints/src/methods/expect_fun_call.rs
+++ b/clippy_lints/src/methods/expect_fun_call.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::higher::FormatExpn;
+use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_errors::Applicability;
@@ -14,7 +14,13 @@ use super::EXPECT_FUN_CALL;
 
 /// Checks for the `EXPECT_FUN_CALL` lint.
 #[allow(clippy::too_many_lines)]
-pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_span: Span, name: &str, args: &[hir::Expr<'_>]) {
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &hir::Expr<'_>,
+    method_span: Span,
+    name: &str,
+    args: &'tcx [hir::Expr<'tcx>],
+) {
     // Strip `&`, `as_ref()` and `as_str()` off `arg` until we're left with either a `String` or
     // `&str`
     fn get_arg_root<'a>(cx: &LateContext<'_>, arg: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
@@ -128,11 +134,12 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_span: Spa
     let mut applicability = Applicability::MachineApplicable;
 
     //Special handling for `format!` as arg_root
-    if let Some(format_expn) = FormatExpn::parse(arg_root) {
-        let span = match *format_expn.format_args.value_args {
-            [] => format_expn.format_args.format_string_span,
-            [.., last] => format_expn.format_args.format_string_span.to(last.span),
-        };
+    if let Some(macro_call) = root_macro_call_first_node(cx, arg_root) {
+        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,
diff --git a/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/clippy_lints/src/methods/from_iter_instead_of_collect.rs
index 8ea9312c0f7..6436e28a63c 100644
--- a/clippy_lints/src/methods/from_iter_instead_of_collect.rs
+++ b/clippy_lints/src/methods/from_iter_instead_of_collect.rs
@@ -37,7 +37,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Exp
     }
 }
 
-fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'tcx>) -> String {
+fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'_>) -> String {
     fn strip_angle_brackets(s: &str) -> Option<&str> {
         s.strip_prefix('<')?.strip_suffix('>')
     }
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index 4e33b2ff14c..1041f644e32 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -80,7 +80,6 @@ use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, TraitRef, Ty, TyS};
 use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::symbol::Symbol;
 use rustc_span::{sym, Span};
 use rustc_typeck::hir_ty_to_ty;
 
@@ -1895,6 +1894,11 @@ declare_clippy_lint! {
     /// ### Why is this bad?
     /// The unnecessary calls result in useless allocations.
     ///
+    /// ### Known problems
+    /// `unnecessary_to_owned` can falsely trigger if `IntoIterator::into_iter` is applied to an
+    /// owned copy of a resource and the resource is later used mutably. See
+    /// [#8148](https://github.com/rust-lang/rust-clippy/issues/8148).
+    ///
     /// ### Example
     /// ```rust
     /// let path = std::path::Path::new("x");
@@ -1997,24 +2001,16 @@ impl_lint_pass!(Methods => [
 ]);
 
 /// Extracts a method call name, args, and `Span` of the method name.
-fn method_call<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> Option<(Symbol, &'tcx [hir::Expr<'tcx>], Span)> {
+fn method_call<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx str, &'tcx [hir::Expr<'tcx>], Span)> {
     if let ExprKind::MethodCall(path, span, args, _) = recv.kind {
         if !args.iter().any(|e| e.span.from_expansion()) {
-            return Some((path.ident.name, args, span));
+            let name = path.ident.name.as_str();
+            return Some((name, args, span));
         }
     }
     None
 }
 
-/// Same as `method_call` but the `Symbol` is dereferenced into a temporary `&str`
-macro_rules! method_call {
-    ($expr:expr) => {
-        method_call($expr)
-            .as_ref()
-            .map(|&(ref name, args, span)| (name.as_str(), args, span))
-    };
-}
-
 impl<'tcx> LateLintPass<'tcx> for Methods {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
         if expr.span.from_expansion() {
@@ -2217,7 +2213,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
 
 #[allow(clippy::too_many_lines)]
 fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Option<&RustcVersion>) {
-    if let Some((name, [recv, args @ ..], span)) = method_call!(expr) {
+    if let Some((name, [recv, args @ ..], span)) = method_call(expr) {
         match (name, args) {
             ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => {
                 zst_offset::check(cx, expr, recv);
@@ -2233,7 +2229,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
             ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
             ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
             ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, msrv),
-            ("collect", []) => match method_call!(recv) {
+            ("collect", []) => match method_call(recv) {
                 Some((name @ ("cloned" | "copied"), [recv2], _)) => {
                     iter_cloned_collect::check(cx, name, expr, recv2);
                 },
@@ -2247,14 +2243,14 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
                 },
                 _ => {},
             },
-            ("count", []) => match method_call!(recv) {
+            ("count", []) => match method_call(recv) {
                 Some((name @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => {
                     iter_count::check(cx, expr, recv2, name);
                 },
                 Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg),
                 _ => {},
             },
-            ("expect", [_]) => match method_call!(recv) {
+            ("expect", [_]) => match method_call(recv) {
                 Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
                 _ => expect_used::check(cx, expr, recv),
             },
@@ -2271,13 +2267,13 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
                 flat_map_option::check(cx, expr, arg, span);
             },
             ("flatten", []) => {
-                if let Some(("map", [recv, map_arg], _)) = method_call!(recv) {
+                if let Some(("map", [recv, map_arg], _)) = method_call(recv) {
                     map_flatten::check(cx, expr, recv, map_arg);
                 }
             },
             ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span),
             ("for_each", [_]) => {
-                if let Some(("inspect", [_, _], span2)) = method_call!(recv) {
+                if let Some(("inspect", [_, _], span2)) = method_call(recv) {
                     inspect_for_each::check(cx, expr, span2);
                 }
             },
@@ -2286,7 +2282,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
             ("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
             ("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
             ("map", [m_arg]) => {
-                if let Some((name, [recv2, args @ ..], span2)) = method_call!(recv) {
+                if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) {
                     match (name, args) {
                         ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, msrv),
                         ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, msrv),
@@ -2301,7 +2297,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
             },
             ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
             ("next", []) => {
-                if let Some((name, [recv, args @ ..], _)) = method_call!(recv) {
+                if let Some((name, [recv, args @ ..], _)) = method_call(recv) {
                     match (name, args) {
                         ("filter", [arg]) => filter_next::check(cx, expr, recv, arg),
                         ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv, arg, msrv),
@@ -2312,7 +2308,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
                     }
                 }
             },
-            ("nth", [n_arg]) => match method_call!(recv) {
+            ("nth", [n_arg]) => match method_call(recv) {
                 Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg),
                 Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false),
                 Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true),
@@ -2344,12 +2340,12 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
             ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
                 implicit_clone::check(cx, name, expr, recv, span);
             },
-            ("unwrap", []) => match method_call!(recv) {
+            ("unwrap", []) => match method_call(recv) {
                 Some(("get", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, false),
                 Some(("get_mut", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, true),
                 _ => unwrap_used::check(cx, expr, recv),
             },
-            ("unwrap_or", [u_arg]) => match method_call!(recv) {
+            ("unwrap_or", [u_arg]) => match method_call(recv) {
                 Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => {
                     manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]);
                 },
@@ -2358,7 +2354,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
                 },
                 _ => {},
             },
-            ("unwrap_or_else", [u_arg]) => match method_call!(recv) {
+            ("unwrap_or_else", [u_arg]) => match method_call(recv) {
                 Some(("map", [recv, map_arg], _)) if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, msrv) => {},
                 _ => {
                     unwrap_or_else_default::check(cx, expr, recv, u_arg);
@@ -2371,7 +2367,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
 }
 
 fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) {
-    if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call!(recv) {
+    if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call(recv) {
         search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span);
     }
 }
@@ -2535,11 +2531,17 @@ impl SelfKind {
             implements_trait(cx, ty, trait_def_id, &[parent_ty.into()])
         }
 
+        fn matches_none<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
+            !matches_value(cx, parent_ty, ty)
+                && !matches_ref(cx, hir::Mutability::Not, parent_ty, ty)
+                && !matches_ref(cx, hir::Mutability::Mut, parent_ty, ty)
+        }
+
         match self {
             Self::Value => matches_value(cx, parent_ty, ty),
             Self::Ref => matches_ref(cx, hir::Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty),
             Self::RefMut => matches_ref(cx, hir::Mutability::Mut, parent_ty, ty),
-            Self::No => ty != parent_ty,
+            Self::No => matches_none(cx, parent_ty, ty),
         }
     }
 
diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs
index 70f20da1d6d..514bdadc442 100644
--- a/clippy_lints/src/methods/str_splitn.rs
+++ b/clippy_lints/src/methods/str_splitn.rs
@@ -124,7 +124,7 @@ struct IterUsage {
 }
 
 #[allow(clippy::too_many_lines)]
-fn parse_iter_usage(
+fn parse_iter_usage<'tcx>(
     cx: &LateContext<'tcx>,
     ctxt: SyntaxContext,
     mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>,
@@ -281,7 +281,7 @@ pub(super) fn check_needless_splitn(
     }
 }
 
-fn check_iter(
+fn check_iter<'tcx>(
     cx: &LateContext<'tcx>,
     ctxt: SyntaxContext,
     mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>,
diff --git a/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/clippy_lints/src/methods/unnecessary_iter_cloned.rs
index 8300df03e99..5999245ea7d 100644
--- a/clippy_lints/src/methods/unnecessary_iter_cloned.rs
+++ b/clippy_lints/src/methods/unnecessary_iter_cloned.rs
@@ -12,13 +12,13 @@ use rustc_span::{sym, Symbol};
 
 use super::UNNECESSARY_TO_OWNED;
 
-pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, receiver: &'tcx Expr<'tcx>) -> bool {
+pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool {
     if_chain! {
         if let Some(parent) = get_parent_expr(cx, expr);
         if let Some(callee_def_id) = fn_def_id(cx, parent);
         if is_into_iter(cx, callee_def_id);
         then {
-            check_for_loop_iter(cx, parent, method_name, receiver)
+            check_for_loop_iter(cx, parent, method_name, receiver, false)
         } else {
             false
         }
@@ -30,10 +30,11 @@ pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol
 /// include this code directly is so that it can be called from
 /// `unnecessary_into_owned::check_into_iter_call_arg`.
 pub fn check_for_loop_iter(
-    cx: &LateContext<'tcx>,
-    expr: &'tcx Expr<'tcx>,
+    cx: &LateContext<'_>,
+    expr: &Expr<'_>,
     method_name: Symbol,
-    receiver: &'tcx Expr<'tcx>,
+    receiver: &Expr<'_>,
+    cloned_before_iter: bool,
 ) -> bool {
     if_chain! {
         if let Some(grandparent) = get_parent_expr(cx, expr).and_then(|parent| get_parent_expr(cx, parent));
@@ -70,12 +71,22 @@ pub fn check_for_loop_iter(
                 expr.span,
                 &format!("unnecessary use of `{}`", method_name),
                 |diag| {
-                    diag.span_suggestion(expr.span, "use", snippet, Applicability::MachineApplicable);
+                    // If `check_into_iter_call_arg` called `check_for_loop_iter` because a call to
+                    // a `to_owned`-like function was removed, then the next suggestion may be
+                    // incorrect. This is because the iterator that results from the call's removal
+                    // could hold a reference to a resource that is used mutably. See
+                    // https://github.com/rust-lang/rust-clippy/issues/8148.
+                    let applicability = if cloned_before_iter {
+                        Applicability::MaybeIncorrect
+                    } else {
+                        Applicability::MachineApplicable
+                    };
+                    diag.span_suggestion(expr.span, "use", snippet, applicability);
                     for addr_of_expr in addr_of_exprs {
                         match addr_of_expr.kind {
                             ExprKind::AddrOf(_, _, referent) => {
                                 let span = addr_of_expr.span.with_hi(referent.span.lo());
-                                diag.span_suggestion(span, "remove this `&`", String::new(), Applicability::MachineApplicable);
+                                diag.span_suggestion(span, "remove this `&`", String::new(), applicability);
                             }
                             _ => unreachable!(),
                         }
@@ -90,7 +101,7 @@ pub fn check_for_loop_iter(
 
 /// The core logic of `check_for_loop_iter` above, this function wraps a use of
 /// `CloneOrCopyVisitor`.
-fn clone_or_copy_needed(
+fn clone_or_copy_needed<'tcx>(
     cx: &LateContext<'tcx>,
     pat: &Pat<'tcx>,
     body: &'tcx Expr<'tcx>,
diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs
index c48bacfce0d..e5b6d296b2d 100644
--- a/clippy_lints/src/methods/unnecessary_to_owned.rs
+++ b/clippy_lints/src/methods/unnecessary_to_owned.rs
@@ -16,7 +16,7 @@ use std::cmp::max;
 
 use super::UNNECESSARY_TO_OWNED;
 
-pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, args: &'tcx [Expr<'tcx>]) {
+pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, args: &'tcx [Expr<'tcx>]) {
     if_chain! {
         if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
         if let [receiver] = args;
@@ -44,11 +44,11 @@ pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol
 /// call of a `to_owned`-like function is unnecessary.
 #[allow(clippy::too_many_lines)]
 fn check_addr_of_expr(
-    cx: &LateContext<'tcx>,
-    expr: &'tcx Expr<'tcx>,
+    cx: &LateContext<'_>,
+    expr: &Expr<'_>,
     method_name: Symbol,
     method_def_id: DefId,
-    receiver: &'tcx Expr<'tcx>,
+    receiver: &Expr<'_>,
 ) -> bool {
     if_chain! {
         if let Some(parent) = get_parent_expr(cx, expr);
@@ -171,12 +171,7 @@ fn check_addr_of_expr(
 
 /// Checks whether `expr` is an argument in an `into_iter` call and, if so, determines whether its
 /// call of a `to_owned`-like function is unnecessary.
-fn check_into_iter_call_arg(
-    cx: &LateContext<'tcx>,
-    expr: &'tcx Expr<'tcx>,
-    method_name: Symbol,
-    receiver: &'tcx Expr<'tcx>,
-) -> bool {
+fn check_into_iter_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool {
     if_chain! {
         if let Some(parent) = get_parent_expr(cx, expr);
         if let Some(callee_def_id) = fn_def_id(cx, parent);
@@ -187,7 +182,13 @@ fn check_into_iter_call_arg(
         if let Some(item_ty) = get_iterator_item_ty(cx, parent_ty);
         if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
         then {
-            if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver) {
+            if unnecessary_iter_cloned::check_for_loop_iter(
+                cx,
+                parent,
+                method_name,
+                receiver,
+                true,
+            ) {
                 return true;
             }
             let cloned_or_copied = if is_copy(cx, item_ty) {
@@ -195,6 +196,9 @@ fn check_into_iter_call_arg(
             } else {
                 "cloned"
             };
+            // The next suggestion may be incorrect because the removal of the `to_owned`-like
+            // function could cause the iterator to hold a reference to a resource that is used
+            // mutably. See https://github.com/rust-lang/rust-clippy/issues/8148.
             span_lint_and_sugg(
                 cx,
                 UNNECESSARY_TO_OWNED,
@@ -202,7 +206,7 @@ fn check_into_iter_call_arg(
                 &format!("unnecessary use of `{}`", method_name),
                 "use",
                 format!("{}.iter().{}()", receiver_snippet, cloned_or_copied),
-                Applicability::MachineApplicable,
+                Applicability::MaybeIncorrect,
             );
             return true;
         }
@@ -212,7 +216,7 @@ fn check_into_iter_call_arg(
 
 /// Checks whether `expr` is an argument in a function call and, if so, determines whether its call
 /// of a `to_owned`-like function is unnecessary.
-fn check_other_call_arg(
+fn check_other_call_arg<'tcx>(
     cx: &LateContext<'tcx>,
     expr: &'tcx Expr<'tcx>,
     method_name: Symbol,
@@ -278,7 +282,7 @@ fn check_other_call_arg(
 
 /// Walks an expression's ancestors until it finds a non-`AddrOf` expression. Returns the first such
 /// expression found (if any) along with the immediately prior expression.
-fn skip_addr_of_ancestors(
+fn skip_addr_of_ancestors<'tcx>(
     cx: &LateContext<'tcx>,
     mut expr: &'tcx Expr<'tcx>,
 ) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
@@ -294,7 +298,7 @@ fn skip_addr_of_ancestors(
 
 /// Checks whether an expression is a function or method call and, if so, returns its `DefId`,
 /// `Substs`, and arguments.
-fn get_callee_substs_and_args(
+fn get_callee_substs_and_args<'tcx>(
     cx: &LateContext<'tcx>,
     expr: &'tcx Expr<'tcx>,
 ) -> Option<(DefId, SubstsRef<'tcx>, &'tcx [Expr<'tcx>])> {
@@ -319,7 +323,7 @@ fn get_callee_substs_and_args(
 }
 
 /// Returns the `TraitPredicate`s and `ProjectionPredicate`s for a function's input type.
-fn get_input_traits_and_projections(
+fn get_input_traits_and_projections<'tcx>(
     cx: &LateContext<'tcx>,
     callee_def_id: DefId,
     input: Ty<'tcx>,
@@ -359,7 +363,11 @@ fn get_input_traits_and_projections(
 }
 
 /// Composes two substitutions by applying the latter to the types of the former.
-fn compose_substs(cx: &LateContext<'tcx>, left: &[GenericArg<'tcx>], right: SubstsRef<'tcx>) -> Vec<GenericArg<'tcx>> {
+fn compose_substs<'tcx>(
+    cx: &LateContext<'tcx>,
+    left: &[GenericArg<'tcx>],
+    right: SubstsRef<'tcx>,
+) -> Vec<GenericArg<'tcx>> {
     left.iter()
         .map(|arg| {
             if let GenericArgKind::Type(arg_ty) = arg.unpack() {
diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs
index 401dc27811d..21b3f81d5d9 100644
--- a/clippy_lints/src/misc.rs
+++ b/clippy_lints/src/misc.rs
@@ -717,7 +717,7 @@ fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>)
     }
 }
 
-fn check_binary(
+fn check_binary<'a>(
     cx: &LateContext<'a>,
     expr: &Expr<'_>,
     cmp: &rustc_span::source_map::Spanned<rustc_hir::BinOpKind>,
diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs
index 12e219cd5c8..842959ce36b 100644
--- a/clippy_lints/src/mutable_debug_assertion.rs
+++ b/clippy_lints/src/mutable_debug_assertion.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::{higher, is_direct_expn_of};
+use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node};
 use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
 use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability};
 use rustc_lint::{LateContext, LateLintPass};
@@ -34,26 +34,30 @@ declare_clippy_lint! {
 
 declare_lint_pass!(DebugAssertWithMutCall => [DEBUG_ASSERT_WITH_MUT_CALL]);
 
-const DEBUG_MACRO_NAMES: [&str; 3] = ["debug_assert", "debug_assert_eq", "debug_assert_ne"];
-
 impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
-        for dmn in &DEBUG_MACRO_NAMES {
-            if is_direct_expn_of(e.span, dmn).is_some() {
-                if let Some(macro_args) = higher::extract_assert_macro_args(e) {
-                    for arg in macro_args {
-                        let mut visitor = MutArgVisitor::new(cx);
-                        visitor.visit_expr(arg);
-                        if let Some(span) = visitor.expr_span() {
-                            span_lint(
-                                cx,
-                                DEBUG_ASSERT_WITH_MUT_CALL,
-                                span,
-                                &format!("do not call a function with mutable arguments inside of `{}!`", dmn),
-                            );
-                        }
-                    }
-                }
+        let Some(macro_call) = root_macro_call_first_node(cx, e) else { return };
+        let macro_name = cx.tcx.item_name(macro_call.def_id);
+        if !matches!(
+            macro_name.as_str(),
+            "debug_assert" | "debug_assert_eq" | "debug_assert_ne"
+        ) {
+            return;
+        }
+        let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn) else { return };
+        for arg in [lhs, rhs] {
+            let mut visitor = MutArgVisitor::new(cx);
+            visitor.visit_expr(arg);
+            if let Some(span) = visitor.expr_span() {
+                span_lint(
+                    cx,
+                    DEBUG_ASSERT_WITH_MUT_CALL,
+                    span,
+                    &format!(
+                        "do not call a function with mutable arguments inside of `{}!`",
+                        macro_name
+                    ),
+                );
             }
         }
     }
diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs
index 816377fe65e..73823779e49 100644
--- a/clippy_lints/src/mutex_atomic.rs
+++ b/clippy_lints/src/mutex_atomic.rs
@@ -38,7 +38,7 @@ declare_clippy_lint! {
     /// ```
     #[clippy::version = "pre 1.29.0"]
     pub MUTEX_ATOMIC,
-    perf,
+    nursery,
     "using a mutex where an atomic value could be used instead"
 }
 
diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs
index 0c1da035173..19d58f7474b 100644
--- a/clippy_lints/src/needless_for_each.rs
+++ b/clippy_lints/src/needless_for_each.rs
@@ -48,7 +48,7 @@ declare_clippy_lint! {
 
 declare_lint_pass!(NeedlessForEach => [NEEDLESS_FOR_EACH]);
 
-impl LateLintPass<'_> for NeedlessForEach {
+impl<'tcx> LateLintPass<'tcx> for NeedlessForEach {
     fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
         let expr = match stmt.kind {
             StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr,
diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs
index 094a3f111ba..9957afcbf04 100644
--- a/clippy_lints/src/needless_late_init.rs
+++ b/clippy_lints/src/needless_late_init.rs
@@ -330,7 +330,7 @@ fn check<'tcx>(
     Some(())
 }
 
-impl LateLintPass<'tcx> for NeedlessLateInit {
+impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit {
     fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
         let mut parents = cx.tcx.hir().parent_iter(local.hir_id);
 
diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs
index 9d5babc5de8..5bf8a1ba1ca 100644
--- a/clippy_lints/src/no_effect.rs
+++ b/clippy_lints/src/no_effect.rs
@@ -82,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect {
     }
 }
 
-fn check_no_effect(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> bool {
+fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
     if let StmtKind::Semi(expr) = stmt.kind {
         if has_no_effect(cx, expr) {
             span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect");
@@ -155,7 +155,7 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     }
 }
 
-fn check_unnecessary_operation(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
+fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
     if_chain! {
         if let StmtKind::Semi(expr) = stmt.kind;
         if let Some(reduced) = reduce_expression(cx, expr);
diff --git a/clippy_lints/src/non_octal_unix_permissions.rs b/clippy_lints/src/non_octal_unix_permissions.rs
index 4b57dbc4c41..e46fee4cac5 100644
--- a/clippy_lints/src/non_octal_unix_permissions.rs
+++ b/clippy_lints/src/non_octal_unix_permissions.rs
@@ -40,7 +40,7 @@ declare_clippy_lint! {
 
 declare_lint_pass!(NonOctalUnixPermissions => [NON_OCTAL_UNIX_PERMISSIONS]);
 
-impl LateLintPass<'_> for NonOctalUnixPermissions {
+impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
         match &expr.kind {
             ExprKind::MethodCall(path, _, [func, param], _) => {
diff --git a/clippy_lints/src/octal_escapes.rs b/clippy_lints/src/octal_escapes.rs
index 9c971437645..e0da12f77fc 100644
--- a/clippy_lints/src/octal_escapes.rs
+++ b/clippy_lints/src/octal_escapes.rs
@@ -50,7 +50,7 @@ declare_clippy_lint! {
 declare_lint_pass!(OctalEscapes => [OCTAL_ESCAPES]);
 
 impl EarlyLintPass for OctalEscapes {
-    fn check_expr(&mut self, cx: &EarlyContext<'tcx>, expr: &Expr) {
+    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
         if in_external_macro(cx.sess, expr.span) {
             return;
         }
@@ -65,7 +65,7 @@ impl EarlyLintPass for OctalEscapes {
     }
 }
 
-fn check_lit(cx: &EarlyContext<'tcx>, lit: &Lit, span: Span, is_string: bool) {
+fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) {
     let contents = lit.symbol.as_str();
     let mut iter = contents.char_indices().peekable();
     let mut found = vec![];
diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs
index 8769c045214..b7a56970b33 100644
--- a/clippy_lints/src/panic_in_result_fn.rs
+++ b/clippy_lints/src/panic_in_result_fn.rs
@@ -1,8 +1,10 @@
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::macros::root_macro_call_first_node;
+use clippy_utils::return_ty;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{find_macro_calls, is_expn_of, return_ty};
+use clippy_utils::visitors::expr_visitor_no_bodies;
 use rustc_hir as hir;
-use rustc_hir::intravisit::FnKind;
+use rustc_hir::intravisit::{FnKind, Visitor};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::{sym, Span};
@@ -55,19 +57,19 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn {
 }
 
 fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) {
-    let mut panics = find_macro_calls(
-        &[
-            "unimplemented",
-            "unreachable",
-            "panic",
-            "todo",
-            "assert",
-            "assert_eq",
-            "assert_ne",
-        ],
-        body,
-    );
-    panics.retain(|span| is_expn_of(*span, "debug_assert").is_none());
+    let mut panics = Vec::new();
+    expr_visitor_no_bodies(|expr| {
+        let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return true };
+        if matches!(
+            &*cx.tcx.item_name(macro_call.def_id).as_str(),
+            "unimplemented" | "unreachable" | "panic" | "todo" | "assert" | "assert_eq" | "assert_ne"
+        ) {
+            panics.push(macro_call.span);
+            return false;
+        }
+        true
+    })
+    .visit_expr(&body.value);
     if !panics.is_empty() {
         span_lint_and_then(
             cx,
diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs
index edfac824ded..6ef6b9a20aa 100644
--- a/clippy_lints/src/panic_unimplemented.rs
+++ b/clippy_lints/src/panic_unimplemented.rs
@@ -1,10 +1,8 @@
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::{is_expn_of, match_panic_call};
-use if_chain::if_chain;
+use clippy_utils::macros::{is_panic, root_macro_call_first_node};
 use rustc_hir::Expr;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::Span;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -78,37 +76,37 @@ declare_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANI
 
 impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if match_panic_call(cx, expr).is_some()
-            && (is_expn_of(expr.span, "debug_assert").is_none() && is_expn_of(expr.span, "assert").is_none())
-        {
-            let span = get_outer_span(expr);
-            if is_expn_of(expr.span, "unimplemented").is_some() {
+        let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
+        if is_panic(cx, macro_call.def_id) {
+            span_lint(
+                cx,
+                PANIC,
+                macro_call.span,
+                "`panic` should not be present in production code",
+            );
+            return;
+        }
+        match cx.tcx.item_name(macro_call.def_id).as_str() {
+            "todo" => {
+                span_lint(
+                    cx,
+                    TODO,
+                    macro_call.span,
+                    "`todo` should not be present in production code",
+                );
+            },
+            "unimplemented" => {
                 span_lint(
                     cx,
                     UNIMPLEMENTED,
-                    span,
+                    macro_call.span,
                     "`unimplemented` should not be present in production code",
                 );
-            } else if is_expn_of(expr.span, "todo").is_some() {
-                span_lint(cx, TODO, span, "`todo` should not be present in production code");
-            } else if is_expn_of(expr.span, "unreachable").is_some() {
-                span_lint(cx, UNREACHABLE, span, "usage of the `unreachable!` macro");
-            } else if is_expn_of(expr.span, "panic").is_some() {
-                span_lint(cx, PANIC, span, "`panic` should not be present in production code");
-            }
-        }
-    }
-}
-
-fn get_outer_span(expr: &Expr<'_>) -> Span {
-    if_chain! {
-        if expr.span.from_expansion();
-        let first = expr.span.ctxt().outer_expn_data().call_site;
-        if first.from_expansion();
-        then {
-            first.ctxt().outer_expn_data().call_site
-        } else {
-            expr.span
+            },
+            "unreachable" => {
+                span_lint(cx, UNREACHABLE, macro_call.span, "usage of the `unreachable!` macro");
+            },
+            _ => {},
         }
     }
 }
diff --git a/clippy_lints/src/ptr_eq.rs b/clippy_lints/src/ptr_eq.rs
index 3c126fc1ca6..2bec93ac606 100644
--- a/clippy_lints/src/ptr_eq.rs
+++ b/clippy_lints/src/ptr_eq.rs
@@ -39,7 +39,7 @@ declare_lint_pass!(PtrEq => [PTR_EQ]);
 
 static LINT_MSG: &str = "use `std::ptr::eq` when comparing raw pointers";
 
-impl LateLintPass<'_> for PtrEq {
+impl<'tcx> LateLintPass<'tcx> for PtrEq {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if expr.span.from_expansion() {
             return;
diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs
index 1cf349f8aa7..1991a01fb60 100644
--- a/clippy_lints/src/redundant_clone.rs
+++ b/clippy_lints/src/redundant_clone.rs
@@ -220,7 +220,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
                     continue;
                 } else if let Some(loc) = clone_usage.cloned_consume_or_mutate_loc {
                     // cloned value is mutated, and the clone is alive.
-                    if possible_borrower.is_alive_at(ret_local, loc) {
+                    if possible_borrower.local_is_alive_at(ret_local, loc) {
                         continue;
                     }
                 }
@@ -767,7 +767,7 @@ impl PossibleBorrowerMap<'_, '_> {
         self.bitset.0 == self.bitset.1
     }
 
-    fn is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool {
+    fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool {
         self.maybe_live.seek_after_primary_effect(at);
         self.maybe_live.contains(local)
     }
diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs
index b2bd0103d11..7c88b42ea31 100644
--- a/clippy_lints/src/redundant_slicing.rs
+++ b/clippy_lints/src/redundant_slicing.rs
@@ -42,7 +42,7 @@ declare_clippy_lint! {
 
 declare_lint_pass!(RedundantSlicing => [REDUNDANT_SLICING]);
 
-impl LateLintPass<'_> for RedundantSlicing {
+impl<'tcx> LateLintPass<'tcx> for RedundantSlicing {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if expr.span.from_expansion() {
             return;
diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs
index 22ae7a291d0..b2448372370 100644
--- a/clippy_lints/src/reference.rs
+++ b/clippy_lints/src/reference.rs
@@ -50,6 +50,7 @@ impl EarlyLintPass for DerefAddrOf {
         if_chain! {
             if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind;
             if let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind;
+            if deref_target.span.ctxt() == e.span.ctxt();
             if !addrof_target.span.from_expansion();
             then {
                 let mut applicability = Applicability::MachineApplicable;
diff --git a/clippy_lints/src/return_self_not_must_use.rs b/clippy_lints/src/return_self_not_must_use.rs
index b57ec96bc7e..5dafd08cf3b 100644
--- a/clippy_lints/src/return_self_not_must_use.rs
+++ b/clippy_lints/src/return_self_not_must_use.rs
@@ -1,5 +1,6 @@
+use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::ty::is_must_use_ty;
-use clippy_utils::{diagnostics::span_lint, nth_arg, return_ty};
+use clippy_utils::{nth_arg, return_ty};
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{Body, FnDecl, HirId, TraitItem, TraitItemKind};
@@ -13,25 +14,46 @@ declare_clippy_lint! {
     /// This lint warns when a method returning `Self` doesn't have the `#[must_use]` attribute.
     ///
     /// ### Why is this bad?
-    /// It prevents to "forget" to use the newly created value.
+    /// Methods returning `Self` often create new values, having the `#[must_use]` attribute
+    /// prevents users from "forgetting" to use the newly created value.
+    ///
+    /// The `#[must_use]` attribute can be added to the type itself to ensure that instances
+    /// are never forgotten. Functions returning a type marked with `#[must_use]` will not be
+    /// linted, as the usage is already enforced by the type attribute.
     ///
     /// ### Limitations
     /// This lint is only applied on methods taking a `self` argument. It would be mostly noise
     /// if it was added on constructors for example.
     ///
     /// ### Example
+    /// Missing attribute
     /// ```rust
     /// pub struct Bar;
-    ///
     /// impl Bar {
     ///     // Bad
     ///     pub fn bar(&self) -> Self {
     ///         Self
     ///     }
+    /// }
+    /// ```
     ///
-    ///     // Good
+    /// It's better to have the `#[must_use]` attribute on the method like this:
+    /// ```rust
+    /// pub struct Bar;
+    /// impl Bar {
     ///     #[must_use]
-    ///     pub fn foo(&self) -> Self {
+    ///     pub fn bar(&self) -> Self {
+    ///         Self
+    ///     }
+    /// }
+    /// ```
+    ///
+    /// Or on the type definition like this:
+    /// ```rust
+    /// #[must_use]
+    /// pub struct Bar;
+    /// impl Bar {
+    ///     pub fn bar(&self) -> Self {
     ///         Self
     ///     }
     /// }
@@ -44,7 +66,7 @@ declare_clippy_lint! {
 
 declare_lint_pass!(ReturnSelfNotMustUse => [RETURN_SELF_NOT_MUST_USE]);
 
-fn check_method(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'tcx>, fn_def: LocalDefId, span: Span, hir_id: HirId) {
+fn check_method(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_def: LocalDefId, span: Span, hir_id: HirId) {
     if_chain! {
         // If it comes from an external macro, better ignore it.
         if !in_external_macro(cx.sess(), span);
@@ -65,11 +87,13 @@ fn check_method(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'tcx>, fn_def: LocalD
         if !is_must_use_ty(cx, ret_ty);
 
         then {
-            span_lint(
+            span_lint_and_help(
                 cx,
                 RETURN_SELF_NOT_MUST_USE,
                 span,
                 "missing `#[must_use]` attribute on a method returning `Self`",
+                None,
+                "consider adding the `#[must_use]` attribute to the method or directly to the `Self` type"
             );
         }
     }
diff --git a/clippy_lints/src/semicolon_if_nothing_returned.rs b/clippy_lints/src/semicolon_if_nothing_returned.rs
index 0b3bbbc8155..729694da46d 100644
--- a/clippy_lints/src/semicolon_if_nothing_returned.rs
+++ b/clippy_lints/src/semicolon_if_nothing_returned.rs
@@ -37,7 +37,7 @@ declare_clippy_lint! {
 
 declare_lint_pass!(SemicolonIfNothingReturned => [SEMICOLON_IF_NOTHING_RETURNED]);
 
-impl LateLintPass<'_> for SemicolonIfNothingReturned {
+impl<'tcx> LateLintPass<'tcx> for SemicolonIfNothingReturned {
     fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
         if_chain! {
             if !block.span.from_expansion();
diff --git a/clippy_lints/src/single_char_lifetime_names.rs b/clippy_lints/src/single_char_lifetime_names.rs
new file mode 100644
index 00000000000..ee82666b5af
--- /dev/null
+++ b/clippy_lints/src/single_char_lifetime_names.rs
@@ -0,0 +1,63 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use rustc_ast::ast::{GenericParam, GenericParamKind};
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for lifetimes with names which are one character
+    /// long.
+    ///
+    /// ### Why is this bad?
+    /// A single character is likely not enough to express the
+    /// purpose of a lifetime. Using a longer name can make code
+    /// easier to understand, especially for those who are new to
+    /// Rust.
+    ///
+    /// ### Known problems
+    /// Rust programmers and learning resources tend to use single
+    /// character lifetimes, so this lint is at odds with the
+    /// ecosystem at large. In addition, the lifetime's purpose may
+    /// be obvious or, rarely, expressible in one character.
+    ///
+    /// ### Example
+    /// ```rust
+    /// struct DiagnosticCtx<'a> {
+    ///     source: &'a str,
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// struct DiagnosticCtx<'src> {
+    ///     source: &'src str,
+    /// }
+    /// ```
+    #[clippy::version = "1.59.0"]
+    pub SINGLE_CHAR_LIFETIME_NAMES,
+    restriction,
+    "warns against single-character lifetime names"
+}
+
+declare_lint_pass!(SingleCharLifetimeNames => [SINGLE_CHAR_LIFETIME_NAMES]);
+
+impl EarlyLintPass for SingleCharLifetimeNames {
+    fn check_generic_param(&mut self, ctx: &EarlyContext<'_>, param: &GenericParam) {
+        if in_external_macro(ctx.sess, param.ident.span) {
+            return;
+        }
+
+        if let GenericParamKind::Lifetime = param.kind {
+            if !param.is_placeholder && param.ident.as_str().len() <= 2 {
+                span_lint_and_help(
+                    ctx,
+                    SINGLE_CHAR_LIFETIME_NAMES,
+                    param.ident.span,
+                    "single-character lifetime names are likely uninformative",
+                    None,
+                    "use a more informative name",
+                );
+            }
+        }
+    }
+}
diff --git a/clippy_lints/src/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs
index df1e85afdd7..9b195f3c0a2 100644
--- a/clippy_lints/src/size_of_in_element_count.rs
+++ b/clippy_lints/src/size_of_in_element_count.rs
@@ -37,7 +37,7 @@ declare_clippy_lint! {
 
 declare_lint_pass!(SizeOfInElementCount => [SIZE_OF_IN_ELEMENT_COUNT]);
 
-fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) -> Option<Ty<'tcx>> {
+fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) -> Option<Ty<'tcx>> {
     match expr.kind {
         ExprKind::Call(count_func, _func_args) => {
             if_chain! {
@@ -64,7 +64,10 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool)
     }
 }
 
-fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> {
+fn get_pointee_ty_and_count_expr<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> {
     const FUNCTIONS: [&[&str]; 8] = [
         &paths::PTR_COPY_NONOVERLAPPING,
         &paths::PTR_COPY,
diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs
index ad8e72ad764..b4a71aefd43 100644
--- a/clippy_lints/src/strings.rs
+++ b/clippy_lints/src/strings.rs
@@ -381,7 +381,7 @@ declare_clippy_lint! {
 
 declare_lint_pass!(StrToString => [STR_TO_STRING]);
 
-impl LateLintPass<'_> for StrToString {
+impl<'tcx> LateLintPass<'tcx> for StrToString {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
         if_chain! {
             if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind;
@@ -431,7 +431,7 @@ declare_clippy_lint! {
 
 declare_lint_pass!(StringToString => [STRING_TO_STRING]);
 
-impl LateLintPass<'_> for StringToString {
+impl<'tcx> LateLintPass<'tcx> for StringToString {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
         if_chain! {
             if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind;
diff --git a/clippy_lints/src/strlen_on_c_strings.rs b/clippy_lints/src/strlen_on_c_strings.rs
index fee01fb0bd1..d6e948a7560 100644
--- a/clippy_lints/src/strlen_on_c_strings.rs
+++ b/clippy_lints/src/strlen_on_c_strings.rs
@@ -39,7 +39,7 @@ declare_clippy_lint! {
 
 declare_lint_pass!(StrlenOnCStrings => [STRLEN_ON_C_STRINGS]);
 
-impl LateLintPass<'tcx> for StrlenOnCStrings {
+impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if_chain! {
             if !expr.span.from_expansion();
diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs
index faf43fd9fc1..ca725918e87 100644
--- a/clippy_lints/src/suspicious_operation_groupings.rs
+++ b/clippy_lints/src/suspicious_operation_groupings.rs
@@ -355,7 +355,7 @@ struct BinaryOp<'exprs> {
     right: &'exprs Expr,
 }
 
-impl BinaryOp<'exprs> {
+impl<'exprs> BinaryOp<'exprs> {
     fn new(op: BinOpKind, span: Span, (left, right): (&'exprs Expr, &'exprs Expr)) -> Self {
         Self { op, span, left, right }
     }
@@ -419,7 +419,7 @@ fn chained_binops(kind: &ExprKind) -> Option<Vec<BinaryOp<'_>>> {
     }
 }
 
-fn chained_binops_helper(left_outer: &'expr Expr, right_outer: &'expr Expr) -> Option<Vec<BinaryOp<'expr>>> {
+fn chained_binops_helper<'expr>(left_outer: &'expr Expr, right_outer: &'expr Expr) -> Option<Vec<BinaryOp<'expr>>> {
     match (&left_outer.kind, &right_outer.kind) {
         (
             ExprKind::Paren(ref left_e) | ExprKind::Unary(_, ref left_e),
diff --git a/clippy_lints/src/trailing_empty_array.rs b/clippy_lints/src/trailing_empty_array.rs
index af36f726700..c9b2ce476e8 100644
--- a/clippy_lints/src/trailing_empty_array.rs
+++ b/clippy_lints/src/trailing_empty_array.rs
@@ -53,13 +53,12 @@ impl<'tcx> LateLintPass<'tcx> for TrailingEmptyArray {
     }
 }
 
-fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) -> bool {
+fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
     if_chain! {
         // First check if last field is an array
         if let ItemKind::Struct(data, _) = &item.kind;
         if let Some(last_field) = data.fields().last();
-        if let rustc_hir::TyKind::Array(_, length) = last_field.ty.kind;
-        if let rustc_hir::ArrayLen::Body(length) = length;
+        if let rustc_hir::TyKind::Array(_, rustc_hir::ArrayLen::Body(length)) = last_field.ty.kind;
 
         // Then check if that that array zero-sized
         let length_ldid = cx.tcx.hir().local_def_id(length.hir_id);
diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs
index fb4abceac25..6369aafe3f9 100644
--- a/clippy_lints/src/trait_bounds.rs
+++ b/clippy_lints/src/trait_bounds.rs
@@ -1,11 +1,16 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::source::{snippet, snippet_with_applicability};
-use clippy_utils::SpanlessHash;
+use clippy_utils::{SpanlessEq, SpanlessHash};
+use core::hash::{Hash, Hasher};
 use if_chain::if_chain;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::unhash::UnhashMap;
 use rustc_errors::Applicability;
-use rustc_hir::{def::Res, GenericBound, Generics, ParamName, Path, QPath, TyKind, WherePredicate};
+use rustc_hir::def::Res;
+use rustc_hir::{
+    GenericBound, Generics, Item, ItemKind, Node, ParamName, Path, PathSegment, QPath, TraitItem, Ty, TyKind,
+    WherePredicate,
+};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::Span;
@@ -83,6 +88,53 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
         self.check_type_repetition(cx, gen);
         check_trait_bound_duplication(cx, gen);
     }
+
+    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'tcx>) {
+        let Generics { where_clause, .. } = &item.generics;
+        let mut self_bounds_set = FxHashSet::default();
+
+        for predicate in where_clause.predicates {
+            if_chain! {
+                if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
+                if !bound_predicate.span.from_expansion();
+                if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind;
+                if let Some(PathSegment { res: Some(Res::SelfTy(Some(def_id), _)), .. }) = segments.first();
+
+                if let Some(
+                    Node::Item(
+                        Item {
+                            kind: ItemKind::Trait(_, _, _, self_bounds, _),
+                            .. }
+                        )
+                    ) = cx.tcx.hir().get_if_local(*def_id);
+                then {
+                    if self_bounds_set.is_empty() {
+                        for bound in self_bounds.iter() {
+                            let Some((self_res, _)) = get_trait_res_span_from_bound(bound) else { continue };
+                            self_bounds_set.insert(self_res);
+                        }
+                    }
+
+                    bound_predicate
+                        .bounds
+                        .iter()
+                        .filter_map(get_trait_res_span_from_bound)
+                        .for_each(|(trait_item_res, span)| {
+                            if self_bounds_set.get(&trait_item_res).is_some() {
+                                span_lint_and_help(
+                                    cx,
+                                    TRAIT_DUPLICATION_IN_BOUNDS,
+                                    span,
+                                    "this trait bound is already specified in trait declaration",
+                                    None,
+                                    "consider removing this trait bound",
+                                );
+                            }
+                        });
+                }
+            }
+        }
+    }
 }
 
 fn get_trait_res_span_from_bound(bound: &GenericBound<'_>) -> Option<(Res, Span)> {
@@ -94,24 +146,40 @@ fn get_trait_res_span_from_bound(bound: &GenericBound<'_>) -> Option<(Res, Span)
 }
 
 impl TraitBounds {
-    fn check_type_repetition(self, cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
+    fn check_type_repetition<'tcx>(self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
+        struct SpanlessTy<'cx, 'tcx> {
+            ty: &'tcx Ty<'tcx>,
+            cx: &'cx LateContext<'tcx>,
+        }
+        impl PartialEq for SpanlessTy<'_, '_> {
+            fn eq(&self, other: &Self) -> bool {
+                let mut eq = SpanlessEq::new(self.cx);
+                eq.inter_expr().eq_ty(self.ty, other.ty)
+            }
+        }
+        impl Hash for SpanlessTy<'_, '_> {
+            fn hash<H: Hasher>(&self, h: &mut H) {
+                let mut t = SpanlessHash::new(self.cx);
+                t.hash_ty(self.ty);
+                h.write_u64(t.finish());
+            }
+        }
+        impl Eq for SpanlessTy<'_, '_> {}
+
         if gen.span.from_expansion() {
             return;
         }
-        let hash = |ty| -> u64 {
-            let mut hasher = SpanlessHash::new(cx);
-            hasher.hash_ty(ty);
-            hasher.finish()
-        };
-        let mut map: UnhashMap<u64, Vec<&GenericBound<'_>>> = UnhashMap::default();
+        let mut map: UnhashMap<SpanlessTy<'_, '_>, Vec<&GenericBound<'_>>> = UnhashMap::default();
         let mut applicability = Applicability::MaybeIncorrect;
         for bound in gen.where_clause.predicates {
             if_chain! {
                 if let WherePredicate::BoundPredicate(ref p) = bound;
                 if p.bounds.len() as u64 <= self.max_trait_bounds;
                 if !p.span.from_expansion();
-                let h = hash(p.bounded_ty);
-                if let Some(ref v) = map.insert(h, p.bounds.iter().collect::<Vec<_>>());
+                if let Some(ref v) = map.insert(
+                    SpanlessTy { ty: p.bounded_ty, cx },
+                    p.bounds.iter().collect::<Vec<_>>()
+                );
 
                 then {
                     let mut hint_string = format!(
diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs
index 481e5957435..9d57505e55e 100644
--- a/clippy_lints/src/types/mod.rs
+++ b/clippy_lints/src/types/mod.rs
@@ -167,8 +167,9 @@ declare_clippy_lint! {
     /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.
     ///
     /// ### Why is this bad?
-    /// Any `&Box<T>` can also be a `&T`, which is more
-    /// general.
+    /// A `&Box<T>` parameter requires the function caller to box `T` first before passing it to a function.
+    /// Using `&T` defines a concrete type for the parameter and generalizes the function, this would also
+    /// auto-deref to `&T` at the function call site if passed a `&Box<T>`.
     ///
     /// ### Example
     /// ```rust,ignore
diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs
index 3d3b4a6679d..697ed267e2f 100644
--- a/clippy_lints/src/undocumented_unsafe_blocks.rs
+++ b/clippy_lints/src/undocumented_unsafe_blocks.rs
@@ -113,8 +113,8 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks {
     }
 }
 
-impl<'hir> Visitor<'hir> for UndocumentedUnsafeBlocks {
-    type Map = Map<'hir>;
+impl<'v> Visitor<'v> for UndocumentedUnsafeBlocks {
+    type Map = Map<'v>;
 
     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
         NestedVisitorMap::None
diff --git a/clippy_lints/src/undropped_manually_drops.rs b/clippy_lints/src/undropped_manually_drops.rs
index c58fa67a023..7557e14d11f 100644
--- a/clippy_lints/src/undropped_manually_drops.rs
+++ b/clippy_lints/src/undropped_manually_drops.rs
@@ -36,7 +36,7 @@ declare_clippy_lint! {
 
 declare_lint_pass!(UndroppedManuallyDrops => [UNDROPPED_MANUALLY_DROPS]);
 
-impl LateLintPass<'tcx> for UndroppedManuallyDrops {
+impl<'tcx> LateLintPass<'tcx> for UndroppedManuallyDrops {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if let Some([arg_0, ..]) = match_function_call(cx, expr, &paths::DROP) {
             let ty = cx.typeck_results().expr_ty(arg_0);
diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs
index 46cc76b150e..2ffaf24f942 100644
--- a/clippy_lints/src/uninit_vec.rs
+++ b/clippy_lints/src/uninit_vec.rs
@@ -78,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for UninitVec {
     }
 }
 
-fn handle_uninit_vec_pair(
+fn handle_uninit_vec_pair<'tcx>(
     cx: &LateContext<'tcx>,
     maybe_init_or_reserve: &'tcx Stmt<'tcx>,
     maybe_set_len: &'tcx Expr<'tcx>,
@@ -196,7 +196,7 @@ fn is_reserve(cx: &LateContext<'_>, path: &PathSegment<'_>, self_expr: &Expr<'_>
 }
 
 /// Returns self if the expression is `Vec::set_len()`
-fn extract_set_len_self(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(&'tcx Expr<'tcx>, Span)> {
+fn extract_set_len_self<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(&'tcx Expr<'tcx>, Span)> {
     // peel unsafe blocks in `unsafe { vec.set_len() }`
     let expr = peel_hir_expr_while(expr, |e| {
         if let ExprKind::Block(block, _) = e.kind {
diff --git a/clippy_lints/src/unit_hash.rs b/clippy_lints/src/unit_hash.rs
index 26b4e0f58a8..dcf8a9d7c84 100644
--- a/clippy_lints/src/unit_hash.rs
+++ b/clippy_lints/src/unit_hash.rs
@@ -46,7 +46,7 @@ declare_clippy_lint! {
 }
 declare_lint_pass!(UnitHash => [UNIT_HASH]);
 
-impl LateLintPass<'tcx> for UnitHash {
+impl<'tcx> LateLintPass<'tcx> for UnitHash {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
         if_chain! {
             if let ExprKind::MethodCall(name_ident, _, args, _) = &expr.kind;
diff --git a/clippy_lints/src/unit_types/unit_cmp.rs b/clippy_lints/src/unit_types/unit_cmp.rs
index 6d9aff47421..1dd8895ebd0 100644
--- a/clippy_lints/src/unit_types/unit_cmp.rs
+++ b/clippy_lints/src/unit_types/unit_cmp.rs
@@ -1,35 +1,29 @@
 use clippy_utils::diagnostics::span_lint;
+use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node};
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::LateContext;
-use rustc_span::hygiene::{ExpnKind, MacroKind};
 
 use super::UNIT_CMP;
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
     if expr.span.from_expansion() {
-        if let Some(callee) = expr.span.source_callee() {
-            if let ExpnKind::Macro(MacroKind::Bang, symbol) = callee.kind {
-                if let ExprKind::Binary(ref cmp, left, _) = expr.kind {
-                    let op = cmp.node;
-                    if op.is_comparison() && cx.typeck_results().expr_ty(left).is_unit() {
-                        let result = match symbol.as_str() {
-                            "assert_eq" | "debug_assert_eq" => "succeed",
-                            "assert_ne" | "debug_assert_ne" => "fail",
-                            _ => return,
-                        };
-                        span_lint(
-                            cx,
-                            UNIT_CMP,
-                            expr.span,
-                            &format!(
-                                "`{}` of unit values detected. This will always {}",
-                                symbol.as_str(),
-                                result
-                            ),
-                        );
-                    }
-                }
+        if let Some(macro_call) = root_macro_call_first_node(cx, expr) {
+            let macro_name = cx.tcx.item_name(macro_call.def_id);
+            let result = match macro_name.as_str() {
+                "assert_eq" | "debug_assert_eq" => "succeed",
+                "assert_ne" | "debug_assert_ne" => "fail",
+                _ => return,
+            };
+            let Some ((left, _, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return };
+            if !cx.typeck_results().expr_ty(left).is_unit() {
+                return;
             }
+            span_lint(
+                cx,
+                UNIT_CMP,
+                macro_call.span,
+                &format!("`{}` of unit values detected. This will always {}", macro_name, result),
+            );
         }
         return;
     }
diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs
index 004530db086..287ac5b4a90 100644
--- a/clippy_lints/src/unused_io_amount.rs
+++ b/clippy_lints/src/unused_io_amount.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint;
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
 use clippy_utils::{is_try, match_trait_method, paths};
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
@@ -17,10 +17,17 @@ declare_clippy_lint! {
     /// partial-write/read, use
     /// `write_all`/`read_exact` instead.
     ///
+    /// When working with asynchronous code (either with the `futures`
+    /// crate or with `tokio`), a similar issue exists for
+    /// `AsyncWriteExt::write()` and `AsyncReadExt::read()` : these
+    /// functions are also not guaranteed to process the entire
+    /// buffer.  Your code should either handle partial-writes/reads, or
+    /// call the `write_all`/`read_exact` methods on those traits instead.
+    ///
     /// ### Known problems
     /// Detects only common patterns.
     ///
-    /// ### Example
+    /// ### Examples
     /// ```rust,ignore
     /// use std::io;
     /// fn foo<W: io::Write>(w: &mut W) -> io::Result<()> {
@@ -68,6 +75,23 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount {
     }
 }
 
+/// If `expr` is an (e).await, return the inner expression "e" that's being
+/// waited on.  Otherwise return None.
+fn try_remove_await<'a>(expr: &'a hir::Expr<'a>) -> Option<&hir::Expr<'a>> {
+    if let hir::ExprKind::Match(expr, _, hir::MatchSource::AwaitDesugar) = expr.kind {
+        if let hir::ExprKind::Call(func, [ref arg_0, ..]) = expr.kind {
+            if matches!(
+                func.kind,
+                hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoFutureIntoFuture, ..))
+            ) {
+                return Some(arg_0);
+            }
+        }
+    }
+
+    None
+}
+
 fn check_map_error(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) {
     let mut call = call;
     while let hir::ExprKind::MethodCall(path, _, args, _) = call.kind {
@@ -77,30 +101,69 @@ fn check_map_error(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<
             break;
         }
     }
-    check_method_call(cx, call, expr);
+
+    if let Some(call) = try_remove_await(call) {
+        check_method_call(cx, call, expr, true);
+    } else {
+        check_method_call(cx, call, expr, false);
+    }
 }
 
-fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) {
+fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>, is_await: bool) {
     if let hir::ExprKind::MethodCall(path, _, _, _) = call.kind {
         let symbol = path.ident.as_str();
-        let read_trait = match_trait_method(cx, call, &paths::IO_READ);
-        let write_trait = match_trait_method(cx, call, &paths::IO_WRITE);
+        let read_trait = if is_await {
+            match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCREADEXT)
+                || match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCREADEXT)
+        } else {
+            match_trait_method(cx, call, &paths::IO_READ)
+        };
+        let write_trait = if is_await {
+            match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCWRITEEXT)
+                || match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCWRITEEXT)
+        } else {
+            match_trait_method(cx, call, &paths::IO_WRITE)
+        };
 
-        match (read_trait, write_trait, symbol) {
-            (true, _, "read") => span_lint(
+        match (read_trait, write_trait, symbol, is_await) {
+            (true, _, "read", false) => span_lint_and_help(
                 cx,
                 UNUSED_IO_AMOUNT,
                 expr.span,
-                "read amount is not handled. Use `Read::read_exact` instead",
+                "read amount is not handled",
+                None,
+                "use `Read::read_exact` instead, or handle partial reads",
             ),
-            (true, _, "read_vectored") => span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "read amount is not handled"),
-            (_, true, "write") => span_lint(
+            (true, _, "read", true) => span_lint_and_help(
                 cx,
                 UNUSED_IO_AMOUNT,
                 expr.span,
-                "written amount is not handled. Use `Write::write_all` instead",
+                "read amount is not handled",
+                None,
+                "use `AsyncReadExt::read_exact` instead, or handle partial reads",
             ),
-            (_, true, "write_vectored") => span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "written amount is not handled"),
+            (true, _, "read_vectored", _) => {
+                span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "read amount is not handled");
+            },
+            (_, true, "write", false) => span_lint_and_help(
+                cx,
+                UNUSED_IO_AMOUNT,
+                expr.span,
+                "written amount is not handled",
+                None,
+                "use `Write::write_all` instead, or handle partial writes",
+            ),
+            (_, true, "write", true) => span_lint_and_help(
+                cx,
+                UNUSED_IO_AMOUNT,
+                expr.span,
+                "written amount is not handled",
+                None,
+                "use `AsyncWriteExt::write_all` instead, or handle partial writes",
+            ),
+            (_, true, "write_vectored", _) => {
+                span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "written amount is not handled");
+            },
             _ => (),
         }
     }
diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs
index 9b06ca4e824..7751c593e43 100644
--- a/clippy_lints/src/utils/author.rs
+++ b/clippy_lints/src/utils/author.rs
@@ -573,7 +573,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
                         bind!(self, anon_const);
                         out!("if let ArrayLen::Body({anon_const}) = {length};");
                         self.body(field!(anon_const.body));
-                    }
+                    },
                 }
             },
             ExprKind::Err => kind!("Err"),
diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs
index 9c83d30eb9c..d6deb50cc90 100644
--- a/clippy_lints/src/utils/conf.rs
+++ b/clippy_lints/src/utils/conf.rs
@@ -23,6 +23,14 @@ pub enum DisallowedMethod {
     WithReason { path: String, reason: Option<String> },
 }
 
+impl DisallowedMethod {
+    pub fn path(&self) -> &str {
+        let (Self::Simple(path) | Self::WithReason { path, .. }) = self;
+
+        path
+    }
+}
+
 /// A single disallowed type, used by the `DISALLOWED_TYPES` lint.
 #[derive(Clone, Debug, Deserialize)]
 #[serde(untagged)]
@@ -113,7 +121,7 @@ macro_rules! define_Conf {
             }
         }
 
-        #[cfg(feature = "metadata-collector-lint")]
+        #[cfg(feature = "internal")]
         pub mod metadata {
             use crate::utils::internal_lints::metadata_collector::ClippyConfiguration;
 
diff --git a/clippy_lints/src/utils/inspector.rs b/clippy_lints/src/utils/inspector.rs
index c96766e5678..e90b6b73b34 100644
--- a/clippy_lints/src/utils/inspector.rs
+++ b/clippy_lints/src/utils/inspector.rs
@@ -342,8 +342,8 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) {
             match length {
                 hir::ArrayLen::Infer(_, _) => println!("{}repeat count: _", ind),
                 hir::ArrayLen::Body(anon_const) => {
-                    print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1)
-                }
+                    print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1);
+                },
             }
         },
         hir::ExprKind::Err => {
diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs
index 7d196af7a53..9c3dcc8e96a 100644
--- a/clippy_lints/src/utils/internal_lints.rs
+++ b/clippy_lints/src/utils/internal_lints.rs
@@ -1,5 +1,6 @@
 use clippy_utils::consts::{constant_simple, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::macros::root_macro_call_first_node;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::match_type;
 use clippy_utils::{
@@ -34,7 +35,7 @@ use rustc_typeck::hir_ty_to_ty;
 
 use std::borrow::{Borrow, Cow};
 
-#[cfg(feature = "metadata-collector-lint")]
+#[cfg(feature = "internal")]
 pub mod metadata_collector;
 
 declare_clippy_lint! {
@@ -410,9 +411,13 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
                 }
                 self.declared_lints.insert(item.ident.name, item.span);
             }
-        } else if is_expn_of(item.span, "impl_lint_pass").is_some()
-            || is_expn_of(item.span, "declare_lint_pass").is_some()
-        {
+        } else if let Some(macro_call) = root_macro_call_first_node(cx, item) {
+            if !matches!(
+                &*cx.tcx.item_name(macro_call.def_id).as_str(),
+                "impl_lint_pass" | "declare_lint_pass"
+            ) {
+                return;
+            }
             if let hir::ItemKind::Impl(hir::Impl {
                 of_trait: None,
                 items: impl_item_refs,
@@ -924,9 +929,20 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
         let lang_item_path = cx.get_def_path(*item_def_id);
         if path_syms.starts_with(&lang_item_path) {
             if let [item] = &path_syms[lang_item_path.len()..] {
-                for child in cx.tcx.module_children(*item_def_id) {
-                    if child.ident.name == *item {
-                        return true;
+                if matches!(
+                    cx.tcx.def_kind(*item_def_id),
+                    DefKind::Mod | DefKind::Enum | DefKind::Trait
+                ) {
+                    for child in cx.tcx.module_children(*item_def_id) {
+                        if child.ident.name == *item {
+                            return true;
+                        }
+                    }
+                } else {
+                    for child in cx.tcx.associated_item_def_ids(*item_def_id) {
+                        if cx.tcx.item_name(*child) == *item {
+                            return true;
+                        }
                     }
                 }
             }
diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs
index 7707eebd622..4e46d79dc08 100644
--- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs
+++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs
@@ -1,8 +1,7 @@
 //! This lint is used to collect metadata about clippy lints. This metadata is exported as a json
 //! file and then used to generate the [clippy lint list](https://rust-lang.github.io/rust-clippy/master/index.html)
 //!
-//! This module and therefor the entire lint is guarded by a feature flag called
-//! `metadata-collector-lint`
+//! This module and therefore the entire lint is guarded by a feature flag called `internal`
 //!
 //! The module transforms all lint names to ascii lowercase to ensure that we don't have mismatches
 //! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such
@@ -578,7 +577,7 @@ fn get_lint_version(cx: &LateContext<'_>, item: &Item<'_>) -> String {
 fn get_lint_group_and_level_or_lint(
     cx: &LateContext<'_>,
     lint_name: &str,
-    item: &'hir Item<'_>,
+    item: &Item<'_>,
 ) -> Option<(String, &'static str)> {
     let result = cx
         .lint_store
@@ -697,20 +696,20 @@ fn extract_emission_info<'hir>(
 }
 
 /// Resolves the possible lints that this expression could reference
-fn resolve_lints(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Vec<String> {
+fn resolve_lints<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Vec<String> {
     let mut resolver = LintResolver::new(cx);
     resolver.visit_expr(expr);
     resolver.lints
 }
 
 /// This function tries to resolve the linked applicability to the given expression.
-fn resolve_applicability(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<usize> {
+fn resolve_applicability<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<usize> {
     let mut resolver = ApplicabilityResolver::new(cx);
     resolver.visit_expr(expr);
     resolver.complete()
 }
 
-fn check_is_multi_part(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hir>) -> bool {
+fn check_is_multi_part<'hir>(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hir>) -> bool {
     if let ExprKind::Closure(_, _, body_id, _, _) = closure_expr.kind {
         let mut scanner = IsMultiSpanScanner::new(cx);
         intravisit::walk_body(&mut scanner, cx.tcx.hir().body(body_id));
@@ -825,7 +824,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> {
 }
 
 /// This returns the parent local node if the expression is a reference one
-fn get_parent_local(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::Local<'hir>> {
+fn get_parent_local<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::Local<'hir>> {
     if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind {
         if let hir::def::Res::Local(local_hir) = path.res {
             return get_parent_local_hir_id(cx, local_hir);
@@ -835,7 +834,7 @@ fn get_parent_local(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Opti
     None
 }
 
-fn get_parent_local_hir_id(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> {
+fn get_parent_local_hir_id<'hir>(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> {
     let map = cx.tcx.hir();
 
     match map.find(map.get_parent_node(hir_id)) {
diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs
index b67448e3a57..dc385ebacba 100644
--- a/clippy_lints/src/utils/mod.rs
+++ b/clippy_lints/src/utils/mod.rs
@@ -1,5 +1,5 @@
 pub mod author;
 pub mod conf;
 pub mod inspector;
-#[cfg(any(feature = "internal-lints", feature = "metadata-collector-lint"))]
+#[cfg(feature = "internal")]
 pub mod internal_lints;
diff --git a/clippy_lints/src/vec_init_then_push.rs b/clippy_lints/src/vec_init_then_push.rs
index 1bc0eb6303c..43474da3450 100644
--- a/clippy_lints/src/vec_init_then_push.rs
+++ b/clippy_lints/src/vec_init_then_push.rs
@@ -77,7 +77,7 @@ impl VecPushSearcher {
     }
 }
 
-impl LateLintPass<'_> for VecInitThenPush {
+impl<'tcx> LateLintPass<'tcx> for VecInitThenPush {
     fn check_block(&mut self, _: &LateContext<'tcx>, _: &'tcx Block<'tcx>) {
         self.searcher = None;
     }
diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml
index 0ba0b59ed13..afff6491aba 100644
--- a/clippy_utils/Cargo.toml
+++ b/clippy_utils/Cargo.toml
@@ -1,17 +1,17 @@
 [package]
 name = "clippy_utils"
-version = "0.1.59"
+version = "0.1.60"
 edition = "2021"
 publish = false
 
 [dependencies]
+arrayvec = { version = "0.7", default-features = false }
 if_chain = "1.0"
 rustc-semver = "1.1"
 
 [features]
 deny-warnings = []
-internal-lints = []
-metadata-collector-lint = []
+internal = []
 
 [package.metadata.rust-analyzer]
 # This crate uses #[feature(rustc_private)]
diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs
index 8400bfbbd99..3d3180521ab 100644
--- a/clippy_utils/src/ast_utils.rs
+++ b/clippy_utils/src/ast_utils.rs
@@ -5,7 +5,6 @@
 #![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)]
 
 use crate::{both, over};
-use if_chain::if_chain;
 use rustc_ast::ptr::P;
 use rustc_ast::{self as ast, *};
 use rustc_span::symbol::Ident;
@@ -679,34 +678,3 @@ pub fn eq_mac_args(l: &MacArgs, r: &MacArgs) -> bool {
         _ => false,
     }
 }
-
-/// Extract args from an assert-like macro.
-///
-/// Currently working with:
-/// - `assert_eq!` and `assert_ne!`
-/// - `debug_assert_eq!` and `debug_assert_ne!`
-///
-/// For example:
-///
-/// `debug_assert_eq!(a, b)` will return Some([a, b])
-pub fn extract_assert_macro_args(mut expr: &Expr) -> Option<[&Expr; 2]> {
-    if_chain! {
-        if let ExprKind::If(_, ref block, _) = expr.kind;
-        if let StmtKind::Semi(ref e) = block.stmts.get(0)?.kind;
-        then {
-            expr = e;
-        }
-    }
-    if_chain! {
-        if let ExprKind::Block(ref block, _) = expr.kind;
-        if let StmtKind::Expr(ref expr) = block.stmts.get(0)?.kind;
-        if let ExprKind::Match(ref match_expr, _) = expr.kind;
-        if let ExprKind::Tup(ref tup) = match_expr.kind;
-        if let [a, b, ..] = tup.as_slice();
-        if let (&ExprKind::AddrOf(_, _, ref a), &ExprKind::AddrOf(_, _, ref b)) = (&a.kind, &b.kind);
-        then {
-            return Some([&*a, &*b]);
-        }
-    }
-    None
-}
diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs
index 5c024612f8e..34c5af848a6 100644
--- a/clippy_utils/src/consts.rs
+++ b/clippy_utils/src/consts.rs
@@ -223,7 +223,7 @@ pub fn constant_simple<'tcx>(
     constant(lcx, typeck_results, e).and_then(|(cst, res)| if res { None } else { Some(cst) })
 }
 
-pub fn constant_full_int(
+pub fn constant_full_int<'tcx>(
     lcx: &LateContext<'tcx>,
     typeck_results: &ty::TypeckResults<'tcx>,
     e: &Expr<'_>,
diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs
index d47b002ad7a..ca222c3d669 100644
--- a/clippy_utils/src/diagnostics.rs
+++ b/clippy_utils/src/diagnostics.rs
@@ -198,7 +198,7 @@ pub fn span_lint_hir_and_then(
 ///     |
 ///     = note: `-D fold-any` implied by `-D warnings`
 /// ```
-#[cfg_attr(feature = "internal-lints", allow(clippy::collapsible_span_lint_calls))]
+#[cfg_attr(feature = "internal", allow(clippy::collapsible_span_lint_calls))]
 pub fn span_lint_and_sugg<'a, T: LintContext>(
     cx: &'a T,
     lint: &'static Lint,
diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs
index 61e529a6079..c3936ec95d4 100644
--- a/clippy_utils/src/eager_or_lazy.rs
+++ b/clippy_utils/src/eager_or_lazy.rs
@@ -45,7 +45,12 @@ impl ops::BitOrAssign for EagernessSuggestion {
 }
 
 /// Determine the eagerness of the given function call.
-fn fn_eagerness(cx: &LateContext<'tcx>, fn_id: DefId, name: Symbol, args: &'tcx [Expr<'_>]) -> EagernessSuggestion {
+fn fn_eagerness<'tcx>(
+    cx: &LateContext<'tcx>,
+    fn_id: DefId,
+    name: Symbol,
+    args: &'tcx [Expr<'_>],
+) -> EagernessSuggestion {
     use EagernessSuggestion::{Eager, Lazy, NoChange};
     let name = name.as_str();
 
@@ -92,7 +97,7 @@ fn fn_eagerness(cx: &LateContext<'tcx>, fn_id: DefId, name: Symbol, args: &'tcx
 }
 
 #[allow(clippy::too_many_lines)]
-fn expr_eagerness(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion {
+fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion {
     struct V<'cx, 'tcx> {
         cx: &'cx LateContext<'tcx>,
         eagerness: EagernessSuggestion,
@@ -225,11 +230,11 @@ fn expr_eagerness(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggest
 }
 
 /// Whether the given expression should be changed to evaluate eagerly
-pub fn switch_to_eager_eval(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+pub fn switch_to_eager_eval<'tcx>(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
     expr_eagerness(cx, expr) == EagernessSuggestion::Eager
 }
 
 /// Whether the given expression should be changed to evaluate lazily
-pub fn switch_to_lazy_eval(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+pub fn switch_to_lazy_eval<'tcx>(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
     expr_eagerness(cx, expr) == EagernessSuggestion::Lazy
 }
diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs
index c764c35d444..160a51740cd 100644
--- a/clippy_utils/src/higher.rs
+++ b/clippy_utils/src/higher.rs
@@ -3,15 +3,13 @@
 #![deny(clippy::missing_docs_in_private_items)]
 
 use crate::ty::is_type_diagnostic_item;
-use crate::{is_expn_of, last_path_segment, match_def_path, paths};
+use crate::{is_expn_of, match_def_path, paths};
 use if_chain::if_chain;
 use rustc_ast::ast::{self, LitKind};
 use rustc_hir as hir;
-use rustc_hir::{
-    Arm, Block, BorrowKind, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath, StmtKind, UnOp,
-};
+use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath};
 use rustc_lint::LateContext;
-use rustc_span::{sym, symbol, ExpnKind, Span, Symbol};
+use rustc_span::{sym, symbol, Span};
 
 /// The essential nodes of a desugared for loop as well as the entire span:
 /// `for pat in arg { body }` becomes `(pat, arg, body)`. Return `(pat, arg, body, span)`.
@@ -428,293 +426,6 @@ pub fn binop(op: hir::BinOpKind) -> ast::BinOpKind {
     }
 }
 
-/// Extract args from an assert-like macro.
-/// Currently working with:
-/// - `assert!`, `assert_eq!` and `assert_ne!`
-/// - `debug_assert!`, `debug_assert_eq!` and `debug_assert_ne!`
-/// For example:
-/// `assert!(expr)` will return `Some([expr])`
-/// `debug_assert_eq!(a, b)` will return `Some([a, b])`
-pub fn extract_assert_macro_args<'tcx>(e: &'tcx Expr<'tcx>) -> Option<Vec<&'tcx Expr<'tcx>>> {
-    /// Try to match the AST for a pattern that contains a match, for example when two args are
-    /// compared
-    fn ast_matchblock(matchblock_expr: &'tcx Expr<'tcx>) -> Option<Vec<&Expr<'_>>> {
-        if_chain! {
-            if let ExprKind::Match(headerexpr, _, _) = &matchblock_expr.kind;
-            if let ExprKind::Tup([lhs, rhs]) = &headerexpr.kind;
-            if let ExprKind::AddrOf(BorrowKind::Ref, _, lhs) = lhs.kind;
-            if let ExprKind::AddrOf(BorrowKind::Ref, _, rhs) = rhs.kind;
-            then {
-                return Some(vec![lhs, rhs]);
-            }
-        }
-        None
-    }
-
-    if let ExprKind::Block(block, _) = e.kind {
-        if block.stmts.len() == 1 {
-            if let StmtKind::Semi(matchexpr) = block.stmts.get(0)?.kind {
-                // macros with unique arg: `{debug_}assert!` (e.g., `debug_assert!(some_condition)`)
-                if_chain! {
-                    if let Some(If { cond, .. }) = If::hir(matchexpr);
-                    if let ExprKind::Unary(UnOp::Not, condition) = cond.kind;
-                    then {
-                        return Some(vec![condition]);
-                    }
-                }
-
-                // debug macros with two args: `debug_assert_{ne, eq}` (e.g., `assert_ne!(a, b)`)
-                if_chain! {
-                    if let ExprKind::Block(matchblock,_) = matchexpr.kind;
-                    if let Some(matchblock_expr) = matchblock.expr;
-                    then {
-                        return ast_matchblock(matchblock_expr);
-                    }
-                }
-            }
-        } else if let Some(matchblock_expr) = block.expr {
-            // macros with two args: `assert_{ne, eq}` (e.g., `assert_ne!(a, b)`)
-            return ast_matchblock(matchblock_expr);
-        }
-    }
-    None
-}
-
-/// A parsed `format!` expansion
-pub struct FormatExpn<'tcx> {
-    /// Span of `format!(..)`
-    pub call_site: Span,
-    /// Inner `format_args!` expansion
-    pub format_args: FormatArgsExpn<'tcx>,
-}
-
-impl FormatExpn<'tcx> {
-    /// Parses an expanded `format!` invocation
-    pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> {
-        if_chain! {
-            if let ExprKind::Block(block, _) = expr.kind;
-            if let [stmt] = block.stmts;
-            if let StmtKind::Local(local) = stmt.kind;
-            if let Some(init) = local.init;
-            if let ExprKind::Call(_, [format_args]) = init.kind;
-            let expn_data = expr.span.ctxt().outer_expn_data();
-            if let ExpnKind::Macro(_, sym::format) = expn_data.kind;
-            if let Some(format_args) = FormatArgsExpn::parse(format_args);
-            then {
-                Some(FormatExpn {
-                    call_site: expn_data.call_site,
-                    format_args,
-                })
-            } else {
-                None
-            }
-        }
-    }
-}
-
-/// A parsed `format_args!` expansion
-pub struct FormatArgsExpn<'tcx> {
-    /// Span of the first argument, the format string
-    pub format_string_span: Span,
-    /// Values passed after the format string
-    pub value_args: Vec<&'tcx Expr<'tcx>>,
-
-    /// String literal expressions which represent the format string split by "{}"
-    pub format_string_parts: &'tcx [Expr<'tcx>],
-    /// Symbols corresponding to [`Self::format_string_parts`]
-    pub format_string_symbols: Vec<Symbol>,
-    /// Expressions like `ArgumentV1::new(arg0, Debug::fmt)`
-    pub args: &'tcx [Expr<'tcx>],
-    /// The final argument passed to `Arguments::new_v1_formatted`, if applicable
-    pub fmt_expr: Option<&'tcx Expr<'tcx>>,
-}
-
-impl FormatArgsExpn<'tcx> {
-    /// Parses an expanded `format_args!` or `format_args_nl!` invocation
-    pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> {
-        if_chain! {
-            if let ExpnKind::Macro(_, name) = expr.span.ctxt().outer_expn_data().kind;
-            let name = name.as_str();
-            if name.ends_with("format_args") || name.ends_with("format_args_nl");
-            if let ExprKind::Call(_, args) = expr.kind;
-            if let Some((strs_ref, args, fmt_expr)) = match args {
-                // Arguments::new_v1
-                [strs_ref, args] => Some((strs_ref, args, None)),
-                // Arguments::new_v1_formatted
-                [strs_ref, args, fmt_expr, _unsafe_arg] => Some((strs_ref, args, Some(fmt_expr))),
-                _ => None,
-            };
-            if let ExprKind::AddrOf(BorrowKind::Ref, _, strs_arr) = strs_ref.kind;
-            if let ExprKind::Array(format_string_parts) = strs_arr.kind;
-            if let Some(format_string_symbols) = format_string_parts
-                .iter()
-                .map(|e| {
-                    if let ExprKind::Lit(lit) = &e.kind {
-                        if let LitKind::Str(symbol, _style) = lit.node {
-                            return Some(symbol);
-                        }
-                    }
-                    None
-                })
-                .collect();
-            if let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args.kind;
-            if let ExprKind::Match(args, [arm], _) = args.kind;
-            if let ExprKind::Tup(value_args) = args.kind;
-            if let Some(value_args) = value_args
-                .iter()
-                .map(|e| match e.kind {
-                    ExprKind::AddrOf(_, _, e) => Some(e),
-                    _ => None,
-                })
-                .collect();
-            if let ExprKind::Array(args) = arm.body.kind;
-            then {
-                Some(FormatArgsExpn {
-                    format_string_span: strs_ref.span,
-                    value_args,
-                    format_string_parts,
-                    format_string_symbols,
-                    args,
-                    fmt_expr,
-                })
-            } else {
-                None
-            }
-        }
-    }
-
-    /// Returns a vector of `FormatArgsArg`.
-    pub fn args(&self) -> Option<Vec<FormatArgsArg<'tcx>>> {
-        if let Some(expr) = self.fmt_expr {
-            if_chain! {
-                if let ExprKind::AddrOf(BorrowKind::Ref, _, expr) = expr.kind;
-                if let ExprKind::Array(exprs) = expr.kind;
-                then {
-                    exprs.iter().map(|fmt| {
-                        if_chain! {
-                            // struct `core::fmt::rt::v1::Argument`
-                            if let ExprKind::Struct(_, fields, _) = fmt.kind;
-                            if let Some(position_field) = fields.iter().find(|f| f.ident.name == sym::position);
-                            if let ExprKind::Lit(lit) = &position_field.expr.kind;
-                            if let LitKind::Int(position, _) = lit.node;
-                            if let Ok(i) = usize::try_from(position);
-                            let arg = &self.args[i];
-                            if let ExprKind::Call(_, [arg_name, _]) = arg.kind;
-                            if let ExprKind::Field(_, j) = arg_name.kind;
-                            if let Ok(j) = j.name.as_str().parse::<usize>();
-                            then {
-                                Some(FormatArgsArg { value: self.value_args[j], arg, fmt: Some(fmt) })
-                            } else {
-                                None
-                            }
-                        }
-                    }).collect()
-                } else {
-                    None
-                }
-            }
-        } else {
-            Some(
-                self.value_args
-                    .iter()
-                    .zip(self.args.iter())
-                    .map(|(value, arg)| FormatArgsArg { value, arg, fmt: None })
-                    .collect(),
-            )
-        }
-    }
-}
-
-/// Type representing a `FormatArgsExpn`'s format arguments
-pub struct FormatArgsArg<'tcx> {
-    /// An element of `value_args` according to `position`
-    pub value: &'tcx Expr<'tcx>,
-    /// An element of `args` according to `position`
-    pub arg: &'tcx Expr<'tcx>,
-    /// An element of `fmt_expn`
-    pub fmt: Option<&'tcx Expr<'tcx>>,
-}
-
-impl<'tcx> FormatArgsArg<'tcx> {
-    /// Returns true if any formatting parameters are used that would have an effect on strings,
-    /// like `{:+2}` instead of just `{}`.
-    pub fn has_string_formatting(&self) -> bool {
-        self.fmt.map_or(false, |fmt| {
-            // `!` because these conditions check that `self` is unformatted.
-            !if_chain! {
-                // struct `core::fmt::rt::v1::Argument`
-                if let ExprKind::Struct(_, fields, _) = fmt.kind;
-                if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format);
-                // struct `core::fmt::rt::v1::FormatSpec`
-                if let ExprKind::Struct(_, subfields, _) = format_field.expr.kind;
-                let mut precision_found = false;
-                let mut width_found = false;
-                if subfields.iter().all(|field| {
-                    match field.ident.name {
-                        sym::precision => {
-                            precision_found = true;
-                            if let ExprKind::Path(ref precision_path) = field.expr.kind {
-                                last_path_segment(precision_path).ident.name == sym::Implied
-                            } else {
-                                false
-                            }
-                        }
-                        sym::width => {
-                            width_found = true;
-                            if let ExprKind::Path(ref width_qpath) = field.expr.kind {
-                                last_path_segment(width_qpath).ident.name == sym::Implied
-                            } else {
-                                false
-                            }
-                        }
-                        _ => true,
-                    }
-                });
-                if precision_found && width_found;
-                then { true } else { false }
-            }
-        })
-    }
-
-    /// Returns true if the argument is formatted using `Display::fmt`.
-    pub fn is_display(&self) -> bool {
-        if_chain! {
-            if let ExprKind::Call(_, [_, format_field]) = self.arg.kind;
-            if let ExprKind::Path(QPath::Resolved(_, path)) = format_field.kind;
-            if let [.., t, _] = path.segments;
-            if t.ident.name == sym::Display;
-            then { true } else { false }
-        }
-    }
-}
-
-/// A parsed `panic!` expansion
-pub struct PanicExpn<'tcx> {
-    /// Span of `panic!(..)`
-    pub call_site: Span,
-    /// Inner `format_args!` expansion
-    pub format_args: FormatArgsExpn<'tcx>,
-}
-
-impl PanicExpn<'tcx> {
-    /// Parses an expanded `panic!` invocation
-    pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> {
-        if_chain! {
-            if let ExprKind::Call(_, [format_args]) = expr.kind;
-            let expn_data = expr.span.ctxt().outer_expn_data();
-            if let Some(format_args) = FormatArgsExpn::parse(format_args);
-            then {
-                Some(PanicExpn {
-                    call_site: expn_data.call_site,
-                    format_args,
-                })
-            } else {
-                None
-            }
-        }
-    }
-}
-
 /// A parsed `Vec` initialization expression
 #[derive(Clone, Copy)]
 pub enum VecInitKind {
diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs
index ac2b1a0259e..5a08a411dd1 100644
--- a/clippy_utils/src/hir_utils.rs
+++ b/clippy_utils/src/hir_utils.rs
@@ -6,9 +6,9 @@ use rustc_data_structures::fx::FxHasher;
 use rustc_hir::def::Res;
 use rustc_hir::HirIdMap;
 use rustc_hir::{
-    BinOpKind, Block, BodyId, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, Guard, HirId,
+    ArrayLen, BinOpKind, Block, BodyId, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, Guard, HirId,
     InlineAsmOperand, Let, Lifetime, LifetimeName, ParamName, Pat, PatField, PatKind, Path, PathSegment, QPath, Stmt,
-    StmtKind, Ty, TyKind, TypeBinding, ArrayLen
+    StmtKind, Ty, TyKind, TypeBinding,
 };
 use rustc_lexer::{tokenize, TokenKind};
 use rustc_lint::LateContext;
@@ -171,11 +171,11 @@ impl HirEqInterExpr<'_, '_, '_> {
     }
 
     pub fn eq_array_length(&mut self, left: ArrayLen, right: ArrayLen) -> bool {
-            match (left, right) {
-                (ArrayLen::Infer(..), ArrayLen::Infer(..)) => true,
-                (ArrayLen::Body(l_ct), ArrayLen::Body(r_ct)) => self.eq_body(l_ct.body, r_ct.body),
-                (_, _) => false,
-            }
+        match (left, right) {
+            (ArrayLen::Infer(..), ArrayLen::Infer(..)) => true,
+            (ArrayLen::Body(l_ct), ArrayLen::Body(r_ct)) => self.eq_body(l_ct.body, r_ct.body),
+            (_, _) => false,
+        }
     }
 
     pub fn eq_body(&mut self, left: BodyId, right: BodyId) -> bool {
@@ -396,12 +396,10 @@ impl HirEqInterExpr<'_, '_, '_> {
     }
 
     #[allow(clippy::similar_names)]
-    fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {
+    pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {
         match (&left.kind, &right.kind) {
             (&TyKind::Slice(l_vec), &TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec),
-            (&TyKind::Array(lt, ll), &TyKind::Array(rt, rl)) => {
-                self.eq_ty(lt, rt) && self.eq_array_length(ll, rl)
-            },
+            (&TyKind::Array(lt, ll), &TyKind::Array(rt, rl)) => self.eq_ty(lt, rt) && self.eq_array_length(ll, rl),
             (&TyKind::Ptr(ref l_mut), &TyKind::Ptr(ref r_mut)) => {
                 l_mut.mutbl == r_mut.mutbl && self.eq_ty(&*l_mut.ty, &*r_mut.ty)
             },
@@ -853,6 +851,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
     pub fn hash_path(&mut self, path: &Path<'_>) {
         match path.res {
             // constant hash since equality is dependant on inter-expression context
+            // e.g. The expressions `if let Some(x) = foo() {}` and `if let Some(y) = foo() {}` are considered equal
+            // even though the binding names are different and they have different `HirId`s.
             Res::Local(_) => 1_usize.hash(&mut self.s),
             _ => {
                 for seg in path.segments {
@@ -963,7 +963,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
 
     pub fn hash_array_length(&mut self, length: ArrayLen) {
         match length {
-            ArrayLen::Infer(..) => {}
+            ArrayLen::Infer(..) => {},
             ArrayLen::Body(anon_const) => self.hash_body(anon_const.body),
         }
     }
diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs
index 90f5baffd22..57183b58b2a 100644
--- a/clippy_utils/src/lib.rs
+++ b/clippy_utils/src/lib.rs
@@ -1,6 +1,5 @@
 #![feature(box_patterns)]
 #![feature(control_flow_enum)]
-#![feature(in_band_lifetimes)]
 #![feature(let_else)]
 #![feature(once_cell)]
 #![feature(rustc_private)]
@@ -44,6 +43,7 @@ pub mod diagnostics;
 pub mod eager_or_lazy;
 pub mod higher;
 mod hir_utils;
+pub mod macros;
 pub mod msrvs;
 pub mod numeric_literal;
 pub mod paths;
@@ -70,16 +70,16 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::unhash::UnhashMap;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::{CrateNum, DefId};
 use rustc_hir::hir_id::{HirIdMap, HirIdSet};
 use rustc_hir::intravisit::{walk_expr, ErasedMap, FnKind, NestedVisitorMap, Visitor};
 use rustc_hir::itemlikevisit::ItemLikeVisitor;
 use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
 use rustc_hir::{
-    def, Arm, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr, ExprKind, FnDecl,
-    ForeignItem, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local,
-    MatchSource, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem,
-    TraitItemKind, TraitRef, TyKind, UnOp, ArrayLen
+    def, lang_items, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr,
+    ExprKind, FnDecl, ForeignItem, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem,
+    Local, MatchSource, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind,
+    Target, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp,
 };
 use rustc_lint::{LateContext, Level, Lint, LintContext};
 use rustc_middle::hir::map::Map;
@@ -126,7 +126,7 @@ macro_rules! extract_msrv_attr {
         extract_msrv_attr!(@EarlyContext);
     };
     (@$context:ident$(, $call:tt)?) => {
-        fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'tcx>, attrs: &'tcx [rustc_ast::ast::Attribute]) {
+        fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) {
             use $crate::get_unique_inner_attr;
             match get_unique_inner_attr(cx.sess$($call)?, attrs, "msrv") {
                 Some(msrv_attr) => {
@@ -146,13 +146,6 @@ macro_rules! extract_msrv_attr {
     };
 }
 
-/// Returns `true` if the span comes from a macro expansion, no matter if from a
-/// macro by example or from a procedural macro
-#[must_use]
-pub fn in_macro(span: Span) -> bool {
-    span.from_expansion() && !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..))
-}
-
 /// Returns `true` if the two spans come from differing expansions (i.e., one is
 /// from a macro and one isn't).
 #[must_use]
@@ -282,7 +275,11 @@ pub fn is_wild(pat: &Pat<'_>) -> bool {
 }
 
 /// Checks if the first type parameter is a lang item.
-pub fn is_ty_param_lang_item(cx: &LateContext<'_>, qpath: &QPath<'tcx>, item: LangItem) -> Option<&'tcx hir::Ty<'tcx>> {
+pub fn is_ty_param_lang_item<'tcx>(
+    cx: &LateContext<'_>,
+    qpath: &QPath<'tcx>,
+    item: LangItem,
+) -> Option<&'tcx hir::Ty<'tcx>> {
     let ty = get_qpath_generic_tys(qpath).next()?;
 
     if let TyKind::Path(qpath) = &ty.kind {
@@ -298,7 +295,7 @@ pub fn is_ty_param_lang_item(cx: &LateContext<'_>, qpath: &QPath<'tcx>, item: La
 }
 
 /// Checks if the first type parameter is a diagnostic item.
-pub fn is_ty_param_diagnostic_item(
+pub fn is_ty_param_diagnostic_item<'tcx>(
     cx: &LateContext<'_>,
     qpath: &QPath<'tcx>,
     item: Symbol,
@@ -375,7 +372,7 @@ pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
     }
 }
 
-pub fn get_qpath_generics(path: &QPath<'tcx>) -> Option<&'tcx GenericArgs<'tcx>> {
+pub fn get_qpath_generics<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx GenericArgs<'tcx>> {
     match path {
         QPath::Resolved(_, p) => p.segments.last().and_then(|s| s.args),
         QPath::TypeRelative(_, s) => s.args,
@@ -383,7 +380,7 @@ pub fn get_qpath_generics(path: &QPath<'tcx>) -> Option<&'tcx GenericArgs<'tcx>>
     }
 }
 
-pub fn get_qpath_generic_tys(path: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
+pub fn get_qpath_generic_tys<'tcx>(path: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
     get_qpath_generics(path)
         .map_or([].as_ref(), |a| a.args)
         .iter()
@@ -522,7 +519,7 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Res {
             }
         };
     }
-    fn item_child_by_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str) -> Option<Res> {
+    fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: &str) -> Option<Res> {
         match tcx.def_kind(def_id) {
             DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx
                 .module_children(def_id)
@@ -538,18 +535,34 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Res {
             _ => None,
         }
     }
+    fn find_primitive(tcx: TyCtxt<'_>, name: &str) -> Option<DefId> {
+        if let Some(&(index, Target::Impl)) = lang_items::ITEM_REFS.get(&Symbol::intern(name)) {
+            tcx.lang_items().items()[index]
+        } else {
+            None
+        }
+    }
+    fn find_crate(tcx: TyCtxt<'_>, name: &str) -> Option<DefId> {
+        tcx.crates(())
+            .iter()
+            .find(|&&num| tcx.crate_name(num).as_str() == name)
+            .map(CrateNum::as_def_id)
+    }
 
-    let (krate, first, path) = match *path {
-        [krate, first, ref path @ ..] => (krate, first, path),
+    let (base, first, path) = match *path {
+        [base, first, ref path @ ..] => (base, first, path),
         [primitive] => {
             return PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy);
         },
         _ => return Res::Err,
     };
     let tcx = cx.tcx;
-    let crates = tcx.crates(());
-    let krate = try_res!(crates.iter().find(|&&num| tcx.crate_name(num).as_str() == krate));
-    let first = try_res!(item_child_by_name(tcx, krate.as_def_id(), first));
+    let first = try_res!(
+        find_primitive(tcx, base)
+            .or_else(|| find_crate(tcx, base))
+            .and_then(|id| item_child_by_name(tcx, id, first))
+    );
+
     let last = path
         .iter()
         .copied()
@@ -628,6 +641,19 @@ fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'
     (result, root)
 }
 
+/// Gets the mutability of the custom deref adjustment, if any.
+pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
+    cx.typeck_results()
+        .expr_adjustments(e)
+        .iter()
+        .find_map(|a| match a.kind {
+            Adjust::Deref(Some(d)) => Some(Some(d.mutbl)),
+            Adjust::Deref(None) => None,
+            _ => Some(None),
+        })
+        .and_then(|x| x)
+}
+
 /// Checks if two expressions can be mutably borrowed simultaneously
 /// and they aren't dependent on borrowing same thing twice
 pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
@@ -636,7 +662,15 @@ pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -
     if !eq_expr_value(cx, r1, r2) {
         return true;
     }
+    if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
+        return false;
+    }
+
     for (x1, x2) in s1.iter().zip(s2.iter()) {
+        if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
+            return false;
+        }
+
         match (&x1.kind, &x2.kind) {
             (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
                 if i1 != i2 {
@@ -710,8 +744,7 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
             _ => false,
         },
         ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
-        ExprKind::Repeat(x, len) => if_chain! {
-            if let ArrayLen::Body(len) = len;
+        ExprKind::Repeat(x, ArrayLen::Body(len)) => if_chain! {
             if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind;
             if let LitKind::Int(v, _) = const_lit.node;
             if v <= 32 && is_default_equivalent(cx, x);
@@ -760,7 +793,7 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
 ///
 /// Note that this check is not recursive, so passing the `if` expression will always return true
 /// even though sub-expressions might return false.
-pub fn can_move_expr_to_closure_no_visit(
+pub fn can_move_expr_to_closure_no_visit<'tcx>(
     cx: &LateContext<'tcx>,
     expr: &'tcx Expr<'_>,
     loop_ids: &[HirId],
@@ -835,7 +868,7 @@ impl std::ops::BitOrAssign for CaptureKind {
 /// Note as this will walk up to parent expressions until the capture can be determined it should
 /// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or
 /// function argument (other than a receiver).
-pub fn capture_local_usage(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind {
+pub fn capture_local_usage<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind {
     fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
         let mut capture = CaptureKind::Ref(Mutability::Not);
         pat.each_binding_or_first(&mut |_, id, span, _| match cx
@@ -935,7 +968,7 @@ pub fn capture_local_usage(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind
 
 /// Checks if the expression can be moved into a closure as is. This will return a list of captures
 /// if so, otherwise, `None`.
-pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
+pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
     struct V<'cx, 'tcx> {
         cx: &'cx LateContext<'tcx>,
         // Stack of potential break targets contained in the expression.
@@ -948,7 +981,7 @@ pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) ->
         /// mutable reference.
         captures: HirIdMap<CaptureKind>,
     }
-    impl Visitor<'tcx> for V<'_, 'tcx> {
+    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
         type Map = ErasedMap<'tcx>;
         fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
             NestedVisitorMap::None
@@ -1146,19 +1179,6 @@ pub fn contains_return(expr: &hir::Expr<'_>) -> bool {
     found
 }
 
-/// Finds calls of the specified macros in a function body.
-pub fn find_macro_calls(names: &[&str], body: &Body<'_>) -> Vec<Span> {
-    let mut result = Vec::new();
-    expr_visitor_no_bodies(|expr| {
-        if names.iter().any(|fun| is_expn_of(expr.span, fun).is_some()) {
-            result.push(expr.span);
-        }
-        true
-    })
-    .visit_expr(&body.value);
-    result
-}
-
 /// Extends the span to the beginning of the spans line, incl. whitespaces.
 ///
 /// ```rust
@@ -1218,7 +1238,7 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio
 }
 
 /// Gets the loop or closure enclosing the given expression, if any.
-pub fn get_enclosing_loop_or_closure(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+pub fn get_enclosing_loop_or_closure<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
     for (_, node) in tcx.hir().parent_iter(expr.hir_id) {
         match node {
             Node::Expr(
@@ -1687,32 +1707,6 @@ pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool {
     path.first().map_or(false, |s| s.as_str() == "libc") && path.last().map_or(false, |s| s.as_str() == name)
 }
 
-pub fn match_panic_call(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
-    if let ExprKind::Call(func, [arg]) = expr.kind {
-        expr_path_res(cx, func)
-            .opt_def_id()
-            .map_or(false, |id| match_panic_def_id(cx, id))
-            .then(|| arg)
-    } else {
-        None
-    }
-}
-
-pub fn match_panic_def_id(cx: &LateContext<'_>, did: DefId) -> bool {
-    match_any_def_paths(
-        cx,
-        did,
-        &[
-            &paths::BEGIN_PANIC,
-            &paths::PANIC_ANY,
-            &paths::PANICKING_PANIC,
-            &paths::PANICKING_PANIC_FMT,
-            &paths::PANICKING_PANIC_STR,
-        ],
-    )
-    .is_some()
-}
-
 /// Returns the list of condition expressions and the list of blocks in a
 /// sequence of `if/else`.
 /// E.g., this returns `([a, b], [c, d, e])` for the expression
@@ -1752,7 +1746,7 @@ pub fn is_async_fn(kind: FnKind<'_>) -> bool {
 }
 
 /// Peels away all the compiler generated code surrounding the body of an async function,
-pub fn get_async_fn_body(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
+pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
     if let ExprKind::Call(
         _,
         &[
@@ -1856,7 +1850,7 @@ pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool
 }
 
 /// Gets the node where an expression is either used, or it's type is unified with another branch.
-pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> {
+pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> {
     let mut child_id = expr.hir_id;
     let mut iter = tcx.hir().parent_iter(child_id);
     loop {
@@ -2062,8 +2056,8 @@ where
 
 /// Peels off all references on the pattern. Returns the underlying pattern and the number of
 /// references removed.
-pub fn peel_hir_pat_refs(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
-    fn peel(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
+pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
+    fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
         if let PatKind::Ref(pat, _) = pat.kind {
             peel(pat, count + 1)
         } else {
@@ -2086,7 +2080,7 @@ pub fn peel_hir_expr_while<'tcx>(
 
 /// Peels off up to the given number of references on the expression. Returns the underlying
 /// expression and the number of references removed.
-pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
+pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
     let mut remaining = count;
     let e = peel_hir_expr_while(expr, |e| match e.kind {
         ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
@@ -2100,7 +2094,7 @@ pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>,
 
 /// Peels off all references on the expression. Returns the underlying expression and the number of
 /// references removed.
-pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
+pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
     let mut count = 0;
     let e = peel_hir_expr_while(expr, |e| match e.kind {
         ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
@@ -2183,7 +2177,7 @@ impl<'hir> ItemLikeVisitor<'hir> for TestItemNamesVisitor<'hir> {
 
 static TEST_ITEM_NAMES_CACHE: SyncOnceCell<Mutex<FxHashMap<LocalDefId, Vec<Symbol>>>> = SyncOnceCell::new();
 
-fn with_test_item_names(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
+fn with_test_item_names<'tcx>(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
     let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
     let mut map: MutexGuard<'_, FxHashMap<LocalDefId, Vec<Symbol>>> = cache.lock().unwrap();
     match map.entry(module) {
diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs
new file mode 100644
index 00000000000..b7a242cf90a
--- /dev/null
+++ b/clippy_utils/src/macros.rs
@@ -0,0 +1,539 @@
+#![allow(clippy::similar_names)] // `expr` and `expn`
+
+use crate::visitors::expr_visitor_no_bodies;
+
+use arrayvec::ArrayVec;
+use if_chain::if_chain;
+use rustc_ast::ast::LitKind;
+use rustc_hir::intravisit::Visitor;
+use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath};
+use rustc_lint::LateContext;
+use rustc_span::def_id::DefId;
+use rustc_span::hygiene::{MacroKind, SyntaxContext};
+use rustc_span::{sym, ExpnData, ExpnId, ExpnKind, Span, Symbol};
+use std::ops::ControlFlow;
+
+/// A macro call, like `vec![1, 2, 3]`.
+///
+/// Use `tcx.item_name(macro_call.def_id)` to get the macro name.
+/// Even better is to check if it is a diagnostic item.
+///
+/// This structure is similar to `ExpnData` but it precludes desugaring expansions.
+#[derive(Debug)]
+pub struct MacroCall {
+    /// Macro `DefId`
+    pub def_id: DefId,
+    /// Kind of macro
+    pub kind: MacroKind,
+    /// The expansion produced by the macro call
+    pub expn: ExpnId,
+    /// Span of the macro call site
+    pub span: Span,
+}
+
+impl MacroCall {
+    pub fn is_local(&self) -> bool {
+        span_is_local(self.span)
+    }
+}
+
+/// Returns an iterator of expansions that created the given span
+pub fn expn_backtrace(mut span: Span) -> impl Iterator<Item = (ExpnId, ExpnData)> {
+    std::iter::from_fn(move || {
+        let ctxt = span.ctxt();
+        if ctxt == SyntaxContext::root() {
+            return None;
+        }
+        let expn = ctxt.outer_expn();
+        let data = expn.expn_data();
+        span = data.call_site;
+        Some((expn, data))
+    })
+}
+
+/// Checks whether the span is from the root expansion or a locally defined macro
+pub fn span_is_local(span: Span) -> bool {
+    !span.from_expansion() || expn_is_local(span.ctxt().outer_expn())
+}
+
+/// Checks whether the expansion is the root expansion or a locally defined macro
+pub fn expn_is_local(expn: ExpnId) -> bool {
+    if expn == ExpnId::root() {
+        return true;
+    }
+    let data = expn.expn_data();
+    let backtrace = expn_backtrace(data.call_site);
+    std::iter::once((expn, data))
+        .chain(backtrace)
+        .find_map(|(_, data)| data.macro_def_id)
+        .map_or(true, DefId::is_local)
+}
+
+/// Returns an iterator of macro expansions that created the given span.
+/// Note that desugaring expansions are skipped.
+pub fn macro_backtrace(span: Span) -> impl Iterator<Item = MacroCall> {
+    expn_backtrace(span).filter_map(|(expn, data)| match data {
+        ExpnData {
+            kind: ExpnKind::Macro(kind, _),
+            macro_def_id: Some(def_id),
+            call_site: span,
+            ..
+        } => Some(MacroCall {
+            def_id,
+            kind,
+            expn,
+            span,
+        }),
+        _ => None,
+    })
+}
+
+/// If the macro backtrace of `span` has a macro call at the root expansion
+/// (i.e. not a nested macro call), returns `Some` with the `MacroCall`
+pub fn root_macro_call(span: Span) -> Option<MacroCall> {
+    macro_backtrace(span).last()
+}
+
+/// Like [`root_macro_call`], but only returns `Some` if `node` is the "first node"
+/// produced by the macro call, as in [`first_node_in_macro`].
+pub fn root_macro_call_first_node(cx: &LateContext<'_>, node: &impl HirNode) -> Option<MacroCall> {
+    if first_node_in_macro(cx, node) != Some(ExpnId::root()) {
+        return None;
+    }
+    root_macro_call(node.span())
+}
+
+/// Like [`macro_backtrace`], but only returns macro calls where `node` is the "first node" of the
+/// macro call, as in [`first_node_in_macro`].
+pub fn first_node_macro_backtrace(cx: &LateContext<'_>, node: &impl HirNode) -> impl Iterator<Item = MacroCall> {
+    let span = node.span();
+    first_node_in_macro(cx, node)
+        .into_iter()
+        .flat_map(move |expn| macro_backtrace(span).take_while(move |macro_call| macro_call.expn != expn))
+}
+
+/// If `node` is the "first node" in a macro expansion, returns `Some` with the `ExpnId` of the
+/// macro call site (i.e. the parent of the macro expansion). This generally means that `node`
+/// is the outermost node of an entire macro expansion, but there are some caveats noted below.
+/// This is useful for finding macro calls while visiting the HIR without processing the macro call
+/// at every node within its expansion.
+///
+/// If you already have immediate access to the parent node, it is simpler to
+/// just check the context of that span directly (e.g. `parent.span.from_expansion()`).
+///
+/// If a macro call is in statement position, it expands to one or more statements.
+/// In that case, each statement *and* their immediate descendants will all yield `Some`
+/// with the `ExpnId` of the containing block.
+///
+/// A node may be the "first node" of multiple macro calls in a macro backtrace.
+/// The expansion of the outermost macro call site is returned in such cases.
+pub fn first_node_in_macro(cx: &LateContext<'_>, node: &impl HirNode) -> Option<ExpnId> {
+    // get the macro expansion or return `None` if not found
+    // `macro_backtrace` importantly ignores desugaring expansions
+    let expn = macro_backtrace(node.span()).next()?.expn;
+
+    // get the parent node, possibly skipping over a statement
+    // if the parent is not found, it is sensible to return `Some(root)`
+    let hir = cx.tcx.hir();
+    let mut parent_iter = hir.parent_iter(node.hir_id());
+    let (parent_id, _) = match parent_iter.next() {
+        None => return Some(ExpnId::root()),
+        Some((_, Node::Stmt(_))) => match parent_iter.next() {
+            None => return Some(ExpnId::root()),
+            Some(next) => next,
+        },
+        Some(next) => next,
+    };
+
+    // get the macro expansion of the parent node
+    let parent_span = hir.span(parent_id);
+    let Some(parent_macro_call) = macro_backtrace(parent_span).next() else {
+        // the parent node is not in a macro
+        return Some(ExpnId::root());
+    };
+
+    if parent_macro_call.expn.is_descendant_of(expn) {
+        // `node` is input to a macro call
+        return None;
+    }
+
+    Some(parent_macro_call.expn)
+}
+
+/* Specific Macro Utils */
+
+/// Is `def_id` of `std::panic`, `core::panic` or any inner implementation macros
+pub fn is_panic(cx: &LateContext<'_>, def_id: DefId) -> bool {
+    let Some(name) = cx.tcx.get_diagnostic_name(def_id) else { return false };
+    matches!(
+        name.as_str(),
+        "core_panic_macro"
+            | "std_panic_macro"
+            | "core_panic_2015_macro"
+            | "std_panic_2015_macro"
+            | "core_panic_2021_macro"
+    )
+}
+
+pub enum PanicExpn<'a> {
+    /// No arguments - `panic!()`
+    Empty,
+    /// A string literal or any `&str` - `panic!("message")` or `panic!(message)`
+    Str(&'a Expr<'a>),
+    /// A single argument that implements `Display` - `panic!("{}", object)`
+    Display(&'a Expr<'a>),
+    /// Anything else - `panic!("error {}: {}", a, b)`
+    Format(FormatArgsExpn<'a>),
+}
+
+impl<'a> PanicExpn<'a> {
+    pub fn parse(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Self> {
+        if !macro_backtrace(expr.span).any(|macro_call| is_panic(cx, macro_call.def_id)) {
+            return None;
+        }
+        let ExprKind::Call(callee, [arg]) = &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() {
+            "panic" if arg.span.ctxt() == expr.span.ctxt() => Self::Empty,
+            "panic" | "panic_str" => Self::Str(arg),
+            "panic_display" => {
+                let ExprKind::AddrOf(_, _, e) = &arg.kind else { return None };
+                Self::Display(e)
+            },
+            "panic_fmt" => Self::Format(FormatArgsExpn::parse(cx, arg)?),
+            _ => return None,
+        };
+        Some(result)
+    }
+}
+
+/// Finds the arguments of an `assert!` or `debug_assert!` macro call within the macro expansion
+pub fn find_assert_args<'a>(
+    cx: &LateContext<'_>,
+    expr: &'a Expr<'a>,
+    expn: ExpnId,
+) -> Option<(&'a Expr<'a>, PanicExpn<'a>)> {
+    find_assert_args_inner(cx, expr, expn).map(|([e], p)| (e, p))
+}
+
+/// Finds the arguments of an `assert_eq!` or `debug_assert_eq!` macro call within the macro
+/// expansion
+pub fn find_assert_eq_args<'a>(
+    cx: &LateContext<'_>,
+    expr: &'a Expr<'a>,
+    expn: ExpnId,
+) -> Option<(&'a Expr<'a>, &'a Expr<'a>, PanicExpn<'a>)> {
+    find_assert_args_inner(cx, expr, expn).map(|([a, b], p)| (a, b, p))
+}
+
+fn find_assert_args_inner<'a, const N: usize>(
+    cx: &LateContext<'_>,
+    expr: &'a Expr<'a>,
+    expn: ExpnId,
+) -> Option<([&'a Expr<'a>; N], PanicExpn<'a>)> {
+    let macro_id = expn.expn_data().macro_def_id?;
+    let (expr, expn) = match cx.tcx.item_name(macro_id).as_str().strip_prefix("debug_") {
+        None => (expr, expn),
+        Some(inner_name) => find_assert_within_debug_assert(cx, expr, expn, Symbol::intern(inner_name))?,
+    };
+    let mut args = ArrayVec::new();
+    let mut panic_expn = None;
+    expr_visitor_no_bodies(|e| {
+        if args.is_full() {
+            if panic_expn.is_none() && e.span.ctxt() != expr.span.ctxt() {
+                panic_expn = PanicExpn::parse(cx, e);
+            }
+            panic_expn.is_none()
+        } else if is_assert_arg(cx, e, expn) {
+            args.push(e);
+            false
+        } else {
+            true
+        }
+    })
+    .visit_expr(expr);
+    let args = args.into_inner().ok()?;
+    // if no `panic!(..)` is found, use `PanicExpn::Empty`
+    // to indicate that the default assertion message is used
+    let panic_expn = panic_expn.unwrap_or(PanicExpn::Empty);
+    Some((args, panic_expn))
+}
+
+fn find_assert_within_debug_assert<'a>(
+    cx: &LateContext<'_>,
+    expr: &'a Expr<'a>,
+    expn: ExpnId,
+    assert_name: Symbol,
+) -> Option<(&'a Expr<'a>, ExpnId)> {
+    let mut found = None;
+    expr_visitor_no_bodies(|e| {
+        if found.is_some() || !e.span.from_expansion() {
+            return false;
+        }
+        let e_expn = e.span.ctxt().outer_expn();
+        if e_expn == expn {
+            return true;
+        }
+        if e_expn.expn_data().macro_def_id.map(|id| cx.tcx.item_name(id)) == Some(assert_name) {
+            found = Some((e, e_expn));
+        }
+        false
+    })
+    .visit_expr(expr);
+    found
+}
+
+fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) -> bool {
+    if !expr.span.from_expansion() {
+        return true;
+    }
+    let result = macro_backtrace(expr.span).try_for_each(|macro_call| {
+        if macro_call.expn == assert_expn {
+            ControlFlow::Break(false)
+        } else {
+            match cx.tcx.item_name(macro_call.def_id) {
+                // `cfg!(debug_assertions)` in `debug_assert!`
+                sym::cfg => ControlFlow::CONTINUE,
+                // assert!(other_macro!(..))
+                _ => ControlFlow::Break(true),
+            }
+        }
+    });
+    match result {
+        ControlFlow::Break(is_assert_arg) => is_assert_arg,
+        ControlFlow::Continue(()) => true,
+    }
+}
+
+/// A parsed `format_args!` expansion
+pub struct FormatArgsExpn<'tcx> {
+    /// Span of the first argument, the format string
+    pub format_string_span: Span,
+    /// The format string split by formatted args like `{..}`
+    pub format_string_parts: Vec<Symbol>,
+    /// Values passed after the format string
+    pub value_args: Vec<&'tcx Expr<'tcx>>,
+    /// Each element is a `value_args` index and a formatting trait (e.g. `sym::Debug`)
+    pub formatters: Vec<(usize, Symbol)>,
+    /// List of `fmt::v1::Argument { .. }` expressions. If this is empty,
+    /// then `formatters` represents the format args (`{..}`).
+    /// If this is non-empty, it represents the format args, and the `position`
+    /// parameters within the struct expressions are indexes of `formatters`.
+    pub specs: Vec<&'tcx Expr<'tcx>>,
+}
+
+impl<'tcx> FormatArgsExpn<'tcx> {
+    /// Parses an expanded `format_args!` or `format_args_nl!` invocation
+    pub fn parse(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<Self> {
+        macro_backtrace(expr.span).find(|macro_call| {
+            matches!(
+                cx.tcx.item_name(macro_call.def_id),
+                sym::const_format_args | sym::format_args | sym::format_args_nl
+            )
+        })?;
+        let mut format_string_span: Option<Span> = None;
+        let mut format_string_parts: Vec<Symbol> = Vec::new();
+        let mut value_args: Vec<&Expr<'_>> = Vec::new();
+        let mut formatters: Vec<(usize, Symbol)> = Vec::new();
+        let mut specs: Vec<&Expr<'_>> = Vec::new();
+        expr_visitor_no_bodies(|e| {
+            // if we're still inside of the macro definition...
+            if e.span.ctxt() == expr.span.ctxt() {
+                // ArgumnetV1::new(<value>, <format_trait>::fmt)
+                if_chain! {
+                    if let ExprKind::Call(callee, [val, fmt_path]) = e.kind;
+                    if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind;
+                    if seg.ident.name == sym::new;
+                    if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
+                    if path.segments.last().unwrap().ident.name == sym::ArgumentV1;
+                    if let ExprKind::Path(QPath::Resolved(_, path)) = fmt_path.kind;
+                    if let [.., fmt_trait, _fmt] = path.segments;
+                    then {
+                        let val_idx = if_chain! {
+                            if val.span.ctxt() == expr.span.ctxt();
+                            if let ExprKind::Field(_, field) = val.kind;
+                            if let Ok(idx) = field.name.as_str().parse();
+                            then {
+                                // tuple index
+                                idx
+                            } else {
+                                // assume the value expression is passed directly
+                                formatters.len()
+                            }
+                        };
+                        formatters.push((val_idx, fmt_trait.ident.name));
+                    }
+                }
+                if let ExprKind::Struct(QPath::Resolved(_, path), ..) = e.kind {
+                    if path.segments.last().unwrap().ident.name == sym::Argument {
+                        specs.push(e);
+                    }
+                }
+                // walk through the macro expansion
+                return true;
+            }
+            // assume that the first expr with a differing context represents
+            // (and has the span of) the format string
+            if format_string_span.is_none() {
+                format_string_span = Some(e.span);
+                let span = e.span;
+                // walk the expr and collect string literals which are format string parts
+                expr_visitor_no_bodies(|e| {
+                    if e.span.ctxt() != span.ctxt() {
+                        // defensive check, probably doesn't happen
+                        return false;
+                    }
+                    if let ExprKind::Lit(lit) = &e.kind {
+                        if let LitKind::Str(symbol, _s) = lit.node {
+                            format_string_parts.push(symbol);
+                        }
+                    }
+                    true
+                })
+                .visit_expr(e);
+            } else {
+                // assume that any further exprs with a differing context are value args
+                value_args.push(e);
+            }
+            // don't walk anything not from the macro expansion (e.a. inputs)
+            false
+        })
+        .visit_expr(expr);
+        Some(FormatArgsExpn {
+            format_string_span: format_string_span?,
+            format_string_parts,
+            value_args,
+            formatters,
+            specs,
+        })
+    }
+
+    /// Finds a nested call to `format_args!` within a `format!`-like macro call
+    pub fn find_nested(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expn_id: ExpnId) -> Option<Self> {
+        let mut format_args = None;
+        expr_visitor_no_bodies(|e| {
+            if format_args.is_some() {
+                return false;
+            }
+            let e_ctxt = e.span.ctxt();
+            if e_ctxt == expr.span.ctxt() {
+                return true;
+            }
+            if e_ctxt.outer_expn().is_descendant_of(expn_id) {
+                format_args = FormatArgsExpn::parse(cx, e);
+            }
+            false
+        })
+        .visit_expr(expr);
+        format_args
+    }
+
+    /// Returns a vector of `FormatArgsArg`.
+    pub fn args(&self) -> Option<Vec<FormatArgsArg<'tcx>>> {
+        if self.specs.is_empty() {
+            let args = std::iter::zip(&self.value_args, &self.formatters)
+                .map(|(value, &(_, format_trait))| FormatArgsArg {
+                    value,
+                    format_trait,
+                    spec: None,
+                })
+                .collect();
+            return Some(args);
+        }
+        self.specs
+            .iter()
+            .map(|spec| {
+                if_chain! {
+                    // struct `core::fmt::rt::v1::Argument`
+                    if let ExprKind::Struct(_, fields, _) = spec.kind;
+                    if let Some(position_field) = fields.iter().find(|f| f.ident.name == sym::position);
+                    if let ExprKind::Lit(lit) = &position_field.expr.kind;
+                    if let LitKind::Int(position, _) = lit.node;
+                    if let Ok(i) = usize::try_from(position);
+                    if let Some(&(j, format_trait)) = self.formatters.get(i);
+                    then {
+                        Some(FormatArgsArg { value: self.value_args[j], format_trait, spec: Some(spec) })
+                    } else {
+                        None
+                    }
+                }
+            })
+            .collect()
+    }
+
+    /// Span of all inputs
+    pub fn inputs_span(&self) -> Span {
+        match *self.value_args {
+            [] => self.format_string_span,
+            [.., last] => self.format_string_span.to(last.span),
+        }
+    }
+}
+
+/// Type representing a `FormatArgsExpn`'s format arguments
+pub struct FormatArgsArg<'tcx> {
+    /// An element of `value_args` according to `position`
+    pub value: &'tcx Expr<'tcx>,
+    /// An element of `args` according to `position`
+    pub format_trait: Symbol,
+    /// An element of `specs`
+    pub spec: Option<&'tcx Expr<'tcx>>,
+}
+
+impl<'tcx> FormatArgsArg<'tcx> {
+    /// Returns true if any formatting parameters are used that would have an effect on strings,
+    /// like `{:+2}` instead of just `{}`.
+    pub fn has_string_formatting(&self) -> bool {
+        self.spec.map_or(false, |spec| {
+            // `!` because these conditions check that `self` is unformatted.
+            !if_chain! {
+                // struct `core::fmt::rt::v1::Argument`
+                if let ExprKind::Struct(_, fields, _) = spec.kind;
+                if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format);
+                // struct `core::fmt::rt::v1::FormatSpec`
+                if let ExprKind::Struct(_, subfields, _) = format_field.expr.kind;
+                if subfields.iter().all(|field| match field.ident.name {
+                    sym::precision | sym::width => match field.expr.kind {
+                        ExprKind::Path(QPath::Resolved(_, path)) => {
+                            path.segments.last().unwrap().ident.name == sym::Implied
+                        }
+                        _ => false,
+                    }
+                    _ => true,
+                });
+                then { true } else { false }
+            }
+        })
+    }
+}
+
+/// A node with a `HirId` and a `Span`
+pub trait HirNode {
+    fn hir_id(&self) -> HirId;
+    fn span(&self) -> Span;
+}
+
+macro_rules! impl_hir_node {
+    ($($t:ident),*) => {
+        $(impl HirNode for hir::$t<'_> {
+            fn hir_id(&self) -> HirId {
+                self.hir_id
+            }
+            fn span(&self) -> Span {
+                self.span
+            }
+        })*
+    };
+}
+
+impl_hir_node!(Expr, Pat);
+
+impl HirNode for hir::Item<'_> {
+    fn hir_id(&self) -> HirId {
+        self.hir_id()
+    }
+
+    fn span(&self) -> Span {
+        self.span
+    }
+}
diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs
index 0cec7d6a5e4..a5b409ad96b 100644
--- a/clippy_utils/src/msrvs.rs
+++ b/clippy_utils/src/msrvs.rs
@@ -12,8 +12,9 @@ macro_rules! msrv_aliases {
 
 // names may refer to stabilized feature flags or library items
 msrv_aliases! {
-    1,53,0 { OR_PATTERNS }
+    1,53,0 { OR_PATTERNS, MANUAL_BITS }
     1,52,0 { STR_SPLIT_ONCE }
+    1,51,0 { BORROW_AS_PTR }
     1,50,0 { BOOL_THEN }
     1,47,0 { TAU }
     1,46,0 { CONST_IF_MATCH }
diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs
index 6171823abbb..288c56e9fd7 100644
--- a/clippy_utils/src/paths.rs
+++ b/clippy_utils/src/paths.rs
@@ -5,16 +5,16 @@
 //! See <https://github.com/rust-lang/rust-clippy/issues/5393> for more information.
 
 pub const ANY_TRAIT: [&str; 3] = ["core", "any", "Any"];
-#[cfg(feature = "metadata-collector-lint")]
+#[cfg(feature = "internal")]
 pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"];
-#[cfg(feature = "metadata-collector-lint")]
+#[cfg(feature = "internal")]
 pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [
     ["rustc_lint_defs", "Applicability", "Unspecified"],
     ["rustc_lint_defs", "Applicability", "HasPlaceholders"],
     ["rustc_lint_defs", "Applicability", "MaybeIncorrect"],
     ["rustc_lint_defs", "Applicability", "MachineApplicable"],
 ];
-#[cfg(feature = "metadata-collector-lint")]
+#[cfg(feature = "internal")]
 pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"];
 pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
 #[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros
@@ -25,7 +25,6 @@ pub const ASSERT_MACRO: [&str; 4] = ["core", "macros", "builtin", "assert"];
 pub const ASSERT_NE_MACRO: [&str; 3] = ["core", "macros", "assert_ne"];
 pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"];
 pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];
-pub(super) const BEGIN_PANIC: [&str; 3] = ["std", "panicking", "begin_panic"];
 /// Preferably use the diagnostic item `sym::Borrow` where possible
 pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"];
 pub const BORROW_MUT_TRAIT: [&str; 3] = ["core", "borrow", "BorrowMut"];
@@ -46,7 +45,7 @@ pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"];
 pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"];
 pub const DROP: [&str; 3] = ["core", "mem", "drop"];
 pub const DURATION: [&str; 3] = ["core", "time", "Duration"];
-#[cfg(feature = "internal-lints")]
+#[cfg(feature = "internal")]
 pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"];
 #[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros
 pub const EPRINT_MACRO: [&str; 3] = ["std", "macros", "eprint"];
@@ -64,13 +63,17 @@ pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "From
 pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"];
 pub const FROM_STR_METHOD: [&str; 5] = ["core", "str", "traits", "FromStr", "from_str"];
 pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];
+#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
+pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"];
+#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
+pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWriteExt"];
 pub const HASH: [&str; 3] = ["core", "hash", "Hash"];
 pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"];
 pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
 pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"];
-#[cfg(feature = "internal-lints")]
+#[cfg(feature = "internal")]
 pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"];
-#[cfg(feature = "internal-lints")]
+#[cfg(feature = "internal")]
 pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"];
 pub const INDEX: [&str; 3] = ["core", "ops", "Index"];
 pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"];
@@ -82,11 +85,11 @@ pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"];
 pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
 #[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
 pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
-#[cfg(feature = "internal-lints")]
+#[cfg(feature = "internal")]
 pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
-#[cfg(feature = "internal-lints")]
+#[cfg(feature = "internal")]
 pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
-#[cfg(any(feature = "internal-lints", feature = "metadata-collector-lint"))]
+#[cfg(feature = "internal")]
 pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
 pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
 pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"];
@@ -106,10 +109,6 @@ pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"];
 pub const ORD: [&str; 3] = ["core", "cmp", "Ord"];
 pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"];
 pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
-pub(super) const PANICKING_PANIC: [&str; 3] = ["core", "panicking", "panic"];
-pub(super) const PANICKING_PANIC_FMT: [&str; 3] = ["core", "panicking", "panic_fmt"];
-pub(super) const PANICKING_PANIC_STR: [&str; 3] = ["core", "panicking", "panic_str"];
-pub(super) const PANIC_ANY: [&str; 3] = ["std", "panic", "panic_any"];
 pub const PARKING_LOT_RAWMUTEX: [&str; 3] = ["parking_lot", "raw_mutex", "RawMutex"];
 pub const PARKING_LOT_RAWRWLOCK: [&str; 3] = ["parking_lot", "raw_rwlock", "RawRwLock"];
 pub const PARKING_LOT_MUTEX_GUARD: [&str; 2] = ["parking_lot", "MutexGuard"];
@@ -118,6 +117,7 @@ pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 2] = ["parking_lot", "RwLockWri
 pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"];
 pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
 pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"];
+#[cfg_attr(not(unix), allow(clippy::invalid_paths))]
 pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "PermissionsExt", "from_mode"];
 pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
 pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"];
@@ -180,20 +180,24 @@ pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
 pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"];
 pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
 pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"];
-#[cfg(feature = "internal-lints")]
+#[cfg(feature = "internal")]
 pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"];
-#[cfg(feature = "internal-lints")]
+#[cfg(feature = "internal")]
 pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"];
-#[cfg(feature = "internal-lints")]
+#[cfg(feature = "internal")]
 pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"];
-#[cfg(feature = "internal-lints")]
+#[cfg(feature = "internal")]
 pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"];
-#[cfg(feature = "internal-lints")]
+#[cfg(feature = "internal")]
 pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"];
-#[cfg(feature = "internal-lints")]
+#[cfg(feature = "internal")]
 pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
 pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];
 pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"];
+#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
+pub const TOKIO_IO_ASYNCREADEXT: [&str; 5] = ["tokio", "io", "util", "async_read_ext", "AsyncReadExt"];
+#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
+pub const TOKIO_IO_ASYNCWRITEEXT: [&str; 5] = ["tokio", "io", "util", "async_write_ext", "AsyncWriteExt"];
 pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"];
 pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"];
 pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"];
diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs
index 836558b07cb..8bdc9a9ea16 100644
--- a/clippy_utils/src/qualify_min_const_fn.rs
+++ b/clippy_utils/src/qualify_min_const_fn.rs
@@ -19,7 +19,7 @@ use std::borrow::Cow;
 
 type McfResult = Result<(), (Span, Cow<'static, str>)>;
 
-pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&RustcVersion>) -> McfResult {
+pub fn is_min_const_fn<'a, 'tcx>(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&RustcVersion>) -> McfResult {
     let def_id = body.source.def_id();
     let mut current = def_id;
     loop {
@@ -85,7 +85,7 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&Ru
     Ok(())
 }
 
-fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
+fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
     for arg in ty.walk(tcx) {
         let ty = match arg.unpack() {
             GenericArgKind::Type(ty) => ty,
@@ -133,7 +133,13 @@ fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
     Ok(())
 }
 
-fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rvalue<'tcx>, span: Span) -> McfResult {
+fn check_rvalue<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    body: &Body<'tcx>,
+    def_id: DefId,
+    rvalue: &Rvalue<'tcx>,
+    span: Span,
+) -> McfResult {
     match rvalue {
         Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())),
         Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => check_operand(tcx, operand, span, body),
@@ -210,7 +216,12 @@ fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rv
     }
 }
 
-fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statement: &Statement<'tcx>) -> McfResult {
+fn check_statement<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    body: &Body<'tcx>,
+    def_id: DefId,
+    statement: &Statement<'tcx>,
+) -> McfResult {
     let span = statement.source_info.span;
     match &statement.kind {
         StatementKind::Assign(box (place, rval)) => {
@@ -239,7 +250,7 @@ fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statemen
     }
 }
 
-fn check_operand(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
+fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
     match operand {
         Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body),
         Operand::Constant(c) => match c.check_static_ptr(tcx) {
@@ -249,7 +260,7 @@ fn check_operand(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &
     }
 }
 
-fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
+fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
     let mut cursor = place.projection.as_ref();
     while let [ref proj_base @ .., elem] = *cursor {
         cursor = proj_base;
@@ -274,7 +285,7 @@ fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'t
     Ok(())
 }
 
-fn check_terminator(
+fn check_terminator<'a, 'tcx>(
     tcx: TyCtxt<'tcx>,
     body: &'a Body<'tcx>,
     terminator: &Terminator<'tcx>,
diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs
index d928317259d..dbad607c58e 100644
--- a/clippy_utils/src/source.rs
+++ b/clippy_utils/src/source.rs
@@ -281,7 +281,7 @@ pub fn snippet_block_with_applicability<'a, T: LintContext>(
 /// correctly get a snippet of `vec![]`.
 ///
 /// This will also return whether or not the snippet is a macro call.
-pub fn snippet_with_context(
+pub fn snippet_with_context<'a>(
     cx: &LateContext<'_>,
     span: Span,
     outer: SyntaxContext,
diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs
index 92662c59226..87bc8232dde 100644
--- a/clippy_utils/src/sugg.rs
+++ b/clippy_utils/src/sugg.rs
@@ -461,7 +461,7 @@ impl Neg for Sugg<'_> {
     }
 }
 
-impl Not for Sugg<'a> {
+impl<'a> Not for Sugg<'a> {
     type Output = Sugg<'a>;
     fn not(self) -> Sugg<'a> {
         use AssocOp::{Equal, Greater, GreaterEqual, Less, LessEqual, NotEqual};
@@ -846,7 +846,7 @@ struct DerefDelegate<'a, 'tcx> {
     applicability: Applicability,
 }
 
-impl DerefDelegate<'_, 'tcx> {
+impl<'tcx> DerefDelegate<'_, 'tcx> {
     /// build final suggestion:
     /// - create the ending part of suggestion
     /// - concatenate starting and ending parts
diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs
index 6d191d4a59b..72317447159 100644
--- a/clippy_utils/src/ty.rs
+++ b/clippy_utils/src/ty.rs
@@ -25,7 +25,7 @@ pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 }
 
 /// Checks whether a type can be partially moved.
-pub fn can_partially_move_ty(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+pub fn can_partially_move_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
     if has_drop(cx, ty) || is_copy(cx, ty) {
         return false;
     }
@@ -366,7 +366,7 @@ pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) {
 
 /// Returns `true` if types `a` and `b` are same types having same `Const` generic args,
 /// otherwise returns `false`
-pub fn same_type_and_consts(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
+pub fn same_type_and_consts<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
     match (&a.kind(), &b.kind()) {
         (&ty::Adt(did_a, substs_a), &ty::Adt(did_b, substs_b)) => {
             if did_a != did_b {
diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs
index 4bfd3c64b9c..b60cd4736f3 100644
--- a/clippy_utils/src/visitors.rs
+++ b/clippy_utils/src/visitors.rs
@@ -173,7 +173,7 @@ pub trait Visitable<'tcx> {
 }
 macro_rules! visitable_ref {
     ($t:ident, $f:ident) => {
-        impl Visitable<'tcx> for &'tcx $t<'tcx> {
+        impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> {
             fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
                 visitor.$f(self);
             }
@@ -217,7 +217,7 @@ pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
 }
 
 /// Checks if the given local is used.
-pub fn is_local_used(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool {
+pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool {
     let mut is_used = false;
     let mut visitor = expr_visitor(cx, |expr| {
         if !is_used {
@@ -231,7 +231,7 @@ pub fn is_local_used(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id
 }
 
 /// Checks if the given expression is a constant.
-pub fn is_const_evaluatable(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
+pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
     struct V<'a, 'tcx> {
         cx: &'a LateContext<'tcx>,
         is_const: bool,
@@ -321,7 +321,7 @@ pub fn is_const_evaluatable(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
 }
 
 /// Checks if the given expression performs an unsafe operation outside of an unsafe block.
-pub fn is_expr_unsafe(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
+pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
     struct V<'a, 'tcx> {
         cx: &'a LateContext<'tcx>,
         is_unsafe: bool,
diff --git a/doc/common_tools_writing_lints.md b/doc/common_tools_writing_lints.md
index c7e51d53f51..207b0be1548 100644
--- a/doc/common_tools_writing_lints.md
+++ b/doc/common_tools_writing_lints.md
@@ -60,7 +60,7 @@ Two noticeable items here:
 Starting with an `expr`, you can check whether it is calling a specific method `some_method`:
 
 ```rust
-impl LateLintPass<'_> for MyStructLint {
+impl<'tcx> LateLintPass<'tcx> for MyStructLint {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
         if_chain! {
             // Check our expr is calling a method
diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs
index 53e669254cf..9af8dcc7726 100644
--- a/lintcheck/src/main.rs
+++ b/lintcheck/src/main.rs
@@ -665,16 +665,6 @@ fn lintcheck_needs_rerun(lintcheck_logs_path: &Path) -> bool {
     logs_modified < clippy_modified
 }
 
-fn is_in_clippy_root() -> bool {
-    if let Ok(pb) = std::env::current_dir() {
-        if let Some(file) = pb.file_name() {
-            return file == PathBuf::from("rust-clippy");
-        }
-    }
-
-    false
-}
-
 /// lintchecks `main()` function
 ///
 /// # Panics
@@ -683,7 +673,7 @@ fn is_in_clippy_root() -> bool {
 /// or if lintcheck is executed from the wrong directory (aka none-repo-root)
 pub fn main() {
     // assert that we launch lintcheck from the repo root (via cargo lintcheck)
-    if !is_in_clippy_root() {
+    if std::fs::metadata("lintcheck/Cargo.toml").is_err() {
         eprintln!("lintcheck needs to be run from clippys repo root!\nUse `cargo lintcheck` alternatively.");
         std::process::exit(3);
     }
diff --git a/rust-toolchain b/rust-toolchain
index 471ae40f1ac..e6a58e92072 100644
--- a/rust-toolchain
+++ b/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2021-12-30"
+channel = "nightly-2022-01-13"
 components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
diff --git a/src/main.rs b/src/main.rs
index 7ebdd947893..240e233420f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -96,7 +96,7 @@ impl ClippyCmd {
             clippy_args.push("--no-deps".into());
         }
 
-        ClippyCmd {
+        Self {
             cargo_subcommand,
             args,
             clippy_args,
diff --git a/tests/cargo/mod.rs b/tests/cargo/mod.rs
deleted file mode 100644
index 4dbe71e4b6a..00000000000
--- a/tests/cargo/mod.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-#[must_use]
-pub fn is_rustc_test_suite() -> bool {
-    option_env!("RUSTC_TEST_SUITE").is_some()
-}
diff --git a/tests/compile-test.rs b/tests/compile-test.rs
index a2d58491872..531890c863f 100644
--- a/tests/compile-test.rs
+++ b/tests/compile-test.rs
@@ -1,4 +1,5 @@
 #![feature(test)] // compiletest_rs requires this attribute
+#![feature(once_cell)]
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
 #![warn(rust_2018_idioms, unused_lifetimes)]
 
@@ -11,16 +12,18 @@ use std::ffi::{OsStr, OsString};
 use std::fs;
 use std::io;
 use std::path::{Path, PathBuf};
+use test_utils::IS_RUSTC_TEST_SUITE;
 
-mod cargo;
+mod test_utils;
 
 // whether to run internal tests or not
-const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal-lints");
+const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal");
 
 /// All crates used in UI tests are listed here
 static TEST_DEPENDENCIES: &[&str] = &[
     "clippy_utils",
     "derive_new",
+    "futures",
     "if_chain",
     "itertools",
     "quote",
@@ -28,6 +31,7 @@ static TEST_DEPENDENCIES: &[&str] = &[
     "serde",
     "serde_derive",
     "syn",
+    "tokio",
     "parking_lot",
 ];
 
@@ -38,6 +42,8 @@ extern crate clippy_utils;
 #[allow(unused_extern_crates)]
 extern crate derive_new;
 #[allow(unused_extern_crates)]
+extern crate futures;
+#[allow(unused_extern_crates)]
 extern crate if_chain;
 #[allow(unused_extern_crates)]
 extern crate itertools;
@@ -47,6 +53,8 @@ extern crate parking_lot;
 extern crate quote;
 #[allow(unused_extern_crates)]
 extern crate syn;
+#[allow(unused_extern_crates)]
+extern crate tokio;
 
 /// Produces a string with an `--extern` flag for all UI test crate
 /// dependencies.
@@ -298,7 +306,7 @@ fn run_ui_cargo(config: &mut compiletest::Config) {
         Ok(result)
     }
 
-    if cargo::is_rustc_test_suite() {
+    if IS_RUSTC_TEST_SUITE {
         return;
     }
 
diff --git a/tests/dogfood.rs b/tests/dogfood.rs
index a37cdfed126..67af9d05bf4 100644
--- a/tests/dogfood.rs
+++ b/tests/dogfood.rs
@@ -3,184 +3,31 @@
 //!
 //! See [Eating your own dog food](https://en.wikipedia.org/wiki/Eating_your_own_dog_food) for context
 
-// Dogfood cannot run on Windows
-#![cfg(not(windows))]
 #![feature(once_cell)]
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
 #![warn(rust_2018_idioms, unused_lifetimes)]
 
-use std::lazy::SyncLazy;
 use std::path::PathBuf;
 use std::process::Command;
+use test_utils::IS_RUSTC_TEST_SUITE;
 
-mod cargo;
-
-static CLIPPY_PATH: SyncLazy<PathBuf> = SyncLazy::new(|| {
-    let mut path = std::env::current_exe().unwrap();
-    assert!(path.pop()); // deps
-    path.set_file_name("cargo-clippy");
-    path
-});
+mod test_utils;
 
 #[test]
 fn dogfood_clippy() {
-    // run clippy on itself and fail the test if lint warnings are reported
-    if cargo::is_rustc_test_suite() {
-        return;
-    }
-    let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
-
-    let mut command = Command::new(&*CLIPPY_PATH);
-    command
-        .current_dir(root_dir)
-        .env("CARGO_INCREMENTAL", "0")
-        .arg("clippy")
-        .arg("--all-targets")
-        .arg("--all-features")
-        .arg("--")
-        .args(&["-D", "clippy::all"])
-        .args(&["-D", "clippy::pedantic"])
-        .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir
-
-    // internal lints only exist if we build with the internal-lints feature
-    if cfg!(feature = "internal-lints") {
-        command.args(&["-D", "clippy::internal"]);
-    }
-
-    let output = command.output().unwrap();
-
-    println!("status: {}", output.status);
-    println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
-    println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
-
-    assert!(output.status.success());
-}
-
-fn test_no_deps_ignores_path_deps_in_workspaces() {
-    if cargo::is_rustc_test_suite() {
-        return;
-    }
-    let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
-    let target_dir = root.join("target").join("dogfood");
-    let cwd = root.join("clippy_workspace_tests");
-
-    // Make sure we start with a clean state
-    Command::new("cargo")
-        .current_dir(&cwd)
-        .env("CARGO_TARGET_DIR", &target_dir)
-        .arg("clean")
-        .args(&["-p", "subcrate"])
-        .args(&["-p", "path_dep"])
-        .output()
-        .unwrap();
-
-    // `path_dep` is a path dependency of `subcrate` that would trigger a denied lint.
-    // Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`.
-    let output = Command::new(&*CLIPPY_PATH)
-        .current_dir(&cwd)
-        .env("CARGO_INCREMENTAL", "0")
-        .arg("clippy")
-        .args(&["-p", "subcrate"])
-        .arg("--no-deps")
-        .arg("--")
-        .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
-        .args(&["--cfg", r#"feature="primary_package_test""#])
-        .output()
-        .unwrap();
-    println!("status: {}", output.status);
-    println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
-    println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
-
-    assert!(output.status.success());
-
-    let lint_path_dep = || {
-        // Test that without the `--no-deps` argument, `path_dep` is linted.
-        let output = Command::new(&*CLIPPY_PATH)
-            .current_dir(&cwd)
-            .env("CARGO_INCREMENTAL", "0")
-            .arg("clippy")
-            .args(&["-p", "subcrate"])
-            .arg("--")
-            .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
-            .args(&["--cfg", r#"feature="primary_package_test""#])
-            .output()
-            .unwrap();
-        println!("status: {}", output.status);
-        println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
-        println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
-
-        assert!(!output.status.success());
-        assert!(
-            String::from_utf8(output.stderr)
-                .unwrap()
-                .contains("error: empty `loop {}` wastes CPU cycles")
-        );
-    };
-
-    // Make sure Cargo is aware of the removal of `--no-deps`.
-    lint_path_dep();
-
-    let successful_build = || {
-        let output = Command::new(&*CLIPPY_PATH)
-            .current_dir(&cwd)
-            .env("CARGO_INCREMENTAL", "0")
-            .arg("clippy")
-            .args(&["-p", "subcrate"])
-            .arg("--")
-            .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
-            .output()
-            .unwrap();
-        println!("status: {}", output.status);
-        println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
-        println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
-
-        assert!(output.status.success());
-
-        output
-    };
-
-    // Trigger a sucessful build, so Cargo would like to cache the build result.
-    successful_build();
-
-    // Make sure there's no spurious rebuild when nothing changes.
-    let stderr = String::from_utf8(successful_build().stderr).unwrap();
-    assert!(!stderr.contains("Compiling"));
-    assert!(!stderr.contains("Checking"));
-    assert!(stderr.contains("Finished"));
-
-    // Make sure Cargo is aware of the new `--cfg` flag.
-    lint_path_dep();
-}
-
-#[test]
-fn dogfood_subprojects() {
-    // run clippy on remaining subprojects and fail the test if lint warnings are reported
-    if cargo::is_rustc_test_suite() {
+    if IS_RUSTC_TEST_SUITE {
         return;
     }
 
-    // NOTE: `path_dep` crate is omitted on purpose here
-    for project in &[
-        "clippy_workspace_tests",
-        "clippy_workspace_tests/src",
-        "clippy_workspace_tests/subcrate",
-        "clippy_workspace_tests/subcrate/src",
-        "clippy_dev",
-        "clippy_lints",
-        "clippy_utils",
-        "rustc_tools_util",
-    ] {
-        run_clippy_for_project(project);
+    // "" is the root package
+    for package in &["", "clippy_dev", "clippy_lints", "clippy_utils", "rustc_tools_util"] {
+        run_clippy_for_package(package);
     }
-
-    // NOTE: Since tests run in parallel we can't run cargo commands on the same workspace at the
-    // same time, so we test this immediately after the dogfood for workspaces.
-    test_no_deps_ignores_path_deps_in_workspaces();
 }
 
 #[test]
 #[ignore]
-#[cfg(feature = "metadata-collector-lint")]
+#[cfg(feature = "internal")]
 fn run_metadata_collection_lint() {
     use std::fs::File;
     use std::time::SystemTime;
@@ -191,7 +38,7 @@ fn run_metadata_collection_lint() {
 
     // Run collection as is
     std::env::set_var("ENABLE_METADATA_COLLECTION", "1");
-    run_clippy_for_project("clippy_lints");
+    run_clippy_for_package("clippy_lints");
 
     // Check if cargo caching got in the way
     if let Ok(file) = File::open(metadata_output_path) {
@@ -214,13 +61,13 @@ fn run_metadata_collection_lint() {
     .unwrap();
 
     // Running the collection again
-    run_clippy_for_project("clippy_lints");
+    run_clippy_for_package("clippy_lints");
 }
 
-fn run_clippy_for_project(project: &str) {
+fn run_clippy_for_package(project: &str) {
     let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
 
-    let mut command = Command::new(&*CLIPPY_PATH);
+    let mut command = Command::new(&*test_utils::CARGO_CLIPPY_PATH);
 
     command
         .current_dir(root_dir.join(project))
@@ -233,8 +80,8 @@ fn run_clippy_for_project(project: &str) {
         .args(&["-D", "clippy::pedantic"])
         .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir
 
-    // internal lints only exist if we build with the internal-lints feature
-    if cfg!(feature = "internal-lints") {
+    // internal lints only exist if we build with the internal feature
+    if cfg!(feature = "internal") {
         command.args(&["-D", "clippy::internal"]);
     }
 
diff --git a/tests/fmt.rs b/tests/fmt.rs
index 383702dd439..0defd45b68b 100644
--- a/tests/fmt.rs
+++ b/tests/fmt.rs
@@ -10,14 +10,6 @@ fn fmt() {
         return;
     }
 
-    // Skip this test if nightly rustfmt is unavailable
-    let rustup_output = Command::new("rustup").args(&["component", "list"]).output().unwrap();
-    assert!(rustup_output.status.success());
-    let component_output = String::from_utf8_lossy(&rustup_output.stdout);
-    if !component_output.contains("rustfmt") {
-        return;
-    }
-
     let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
     let output = Command::new("cargo")
         .current_dir(root_dir)
diff --git a/tests/test_utils/mod.rs b/tests/test_utils/mod.rs
new file mode 100644
index 00000000000..8a4de3f6def
--- /dev/null
+++ b/tests/test_utils/mod.rs
@@ -0,0 +1,13 @@
+#![allow(dead_code)] // see https://github.com/rust-lang/rust/issues/46379
+
+use std::lazy::SyncLazy;
+use std::path::PathBuf;
+
+pub static CARGO_CLIPPY_PATH: SyncLazy<PathBuf> = SyncLazy::new(|| {
+    let mut path = std::env::current_exe().unwrap();
+    assert!(path.pop()); // deps
+    path.set_file_name("cargo-clippy");
+    path
+});
+
+pub const IS_RUSTC_TEST_SUITE: bool = option_env!("RUSTC_TEST_SUITE").is_some();
diff --git a/tests/ui-internal/invalid_paths.stderr b/tests/ui-internal/invalid_paths.stderr
index 0a8e5427978..0e850886917 100644
--- a/tests/ui-internal/invalid_paths.stderr
+++ b/tests/ui-internal/invalid_paths.stderr
@@ -1,10 +1,16 @@
+error: invalid path
+  --> $DIR/invalid_paths.rs:15:5
+   |
+LL |     pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::invalid-paths` implied by `-D warnings`
+
 error: invalid path
   --> $DIR/invalid_paths.rs:18:5
    |
 LL |     pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"];
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: `-D clippy::invalid-paths` implied by `-D warnings`
 
 error: invalid path
   --> $DIR/invalid_paths.rs:21:5
@@ -12,5 +18,5 @@ error: invalid path
 LL |     pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"];
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
 
diff --git a/tests/ui-toml/toml_disallowed_methods/clippy.toml b/tests/ui-toml/toml_disallowed_methods/clippy.toml
index f1d4a4619c5..c902d21123d 100644
--- a/tests/ui-toml/toml_disallowed_methods/clippy.toml
+++ b/tests/ui-toml/toml_disallowed_methods/clippy.toml
@@ -1,6 +1,8 @@
 disallowed-methods = [
     # just a string is shorthand for path only
     "std::iter::Iterator::sum",
+    "f32::clamp",
+    "slice::sort_unstable",
     # can give path and reason with an inline table
     { path = "regex::Regex::is_match", reason = "no matching allowed" },
     # can use an inline table but omit reason
diff --git a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs
index cb449b45bde..338b3b5b28f 100644
--- a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs
+++ b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs
@@ -7,6 +7,11 @@ fn main() {
     let re = Regex::new(r"ab.*c").unwrap();
     re.is_match("abc");
 
-    let a = vec![1, 2, 3, 4];
+    let mut a = vec![1, 2, 3, 4];
     a.iter().sum::<i32>();
+
+    a.sort_unstable();
+
+    let _ = 2.0f32.clamp(3.0f32, 4.0f32);
+    let _ = 2.0f64.clamp(3.0f64, 4.0f64);
 }
diff --git a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr
index 999ead10d51..5533676aea2 100644
--- a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr
+++ b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr
@@ -20,5 +20,17 @@ error: use of a disallowed method `std::iter::Iterator::sum`
 LL |     a.iter().sum::<i32>();
    |     ^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 3 previous errors
+error: use of a disallowed method `slice::sort_unstable`
+  --> $DIR/conf_disallowed_methods.rs:13:5
+   |
+LL |     a.sort_unstable();
+   |     ^^^^^^^^^^^^^^^^^
+
+error: use of a disallowed method `f32::clamp`
+  --> $DIR/conf_disallowed_methods.rs:15:13
+   |
+LL |     let _ = 2.0f32.clamp(3.0f32, 4.0f32);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 5 previous errors
 
diff --git a/tests/ui/as_conversions.rs b/tests/ui/as_conversions.rs
index cd745feec6d..ba4394defbf 100644
--- a/tests/ui/as_conversions.rs
+++ b/tests/ui/as_conversions.rs
@@ -1,6 +1,7 @@
 // aux-build:macro_rules.rs
 
 #![warn(clippy::as_conversions)]
+#![allow(clippy::borrow_as_ptr)]
 
 #[macro_use]
 extern crate macro_rules;
diff --git a/tests/ui/as_conversions.stderr b/tests/ui/as_conversions.stderr
index f5f75d3aee0..d11b56171b0 100644
--- a/tests/ui/as_conversions.stderr
+++ b/tests/ui/as_conversions.stderr
@@ -1,5 +1,5 @@
 error: using a potentially dangerous silent `as` conversion
-  --> $DIR/as_conversions.rs:14:13
+  --> $DIR/as_conversions.rs:15:13
    |
 LL |     let i = 0u32 as u64;
    |             ^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL |     let i = 0u32 as u64;
    = help: consider using a safe wrapper for this conversion
 
 error: using a potentially dangerous silent `as` conversion
-  --> $DIR/as_conversions.rs:16:13
+  --> $DIR/as_conversions.rs:17:13
    |
 LL |     let j = &i as *const u64 as *mut u64;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -16,7 +16,7 @@ LL |     let j = &i as *const u64 as *mut u64;
    = help: consider using a safe wrapper for this conversion
 
 error: using a potentially dangerous silent `as` conversion
-  --> $DIR/as_conversions.rs:16:13
+  --> $DIR/as_conversions.rs:17:13
    |
 LL |     let j = &i as *const u64 as *mut u64;
    |             ^^^^^^^^^^^^^^^^
diff --git a/tests/ui/assertions_on_constants.rs b/tests/ui/assertions_on_constants.rs
index cb516d0f977..7477c01ca78 100644
--- a/tests/ui/assertions_on_constants.rs
+++ b/tests/ui/assertions_on_constants.rs
@@ -1,4 +1,3 @@
-//FIXME: suggestions are wrongly expanded, this should be fixed along with #7843
 #![allow(non_fmt_panics)]
 
 macro_rules! assert_const {
diff --git a/tests/ui/assertions_on_constants.stderr b/tests/ui/assertions_on_constants.stderr
index ec80ec702fb..e1f818814d5 100644
--- a/tests/ui/assertions_on_constants.stderr
+++ b/tests/ui/assertions_on_constants.stderr
@@ -1,75 +1,75 @@
 error: `assert!(true)` will be optimized out by the compiler
-  --> $DIR/assertions_on_constants.rs:11:5
+  --> $DIR/assertions_on_constants.rs:10:5
    |
 LL |     assert!(true);
    |     ^^^^^^^^^^^^^
    |
    = note: `-D clippy::assertions-on-constants` implied by `-D warnings`
    = help: remove it
-   = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: `assert!(false)` should probably be replaced
-  --> $DIR/assertions_on_constants.rs:12:5
+  --> $DIR/assertions_on_constants.rs:11:5
    |
 LL |     assert!(false);
    |     ^^^^^^^^^^^^^^
    |
    = help: use `panic!()` or `unreachable!()`
-   = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: `assert!(true)` will be optimized out by the compiler
-  --> $DIR/assertions_on_constants.rs:13:5
+  --> $DIR/assertions_on_constants.rs:12:5
    |
 LL |     assert!(true, "true message");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: remove it
-   = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: `assert!(false, $crate::const_format_args!($($t)+))` should probably be replaced
-  --> $DIR/assertions_on_constants.rs:14:5
+error: `assert!(false, ..)` should probably be replaced
+  --> $DIR/assertions_on_constants.rs:13:5
    |
 LL |     assert!(false, "false message");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: use `panic!($crate::const_format_args!($($t)+))` or `unreachable!($crate::const_format_args!($($t)+))`
-   = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
+   = help: use `panic!(..)` or `unreachable!(..)`
+
+error: `assert!(false, ..)` should probably be replaced
+  --> $DIR/assertions_on_constants.rs:16:5
+   |
+LL |     assert!(false, "{}", msg.to_uppercase());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: use `panic!(..)` or `unreachable!(..)`
 
 error: `assert!(true)` will be optimized out by the compiler
-  --> $DIR/assertions_on_constants.rs:20:5
+  --> $DIR/assertions_on_constants.rs:19:5
    |
 LL |     assert!(B);
    |     ^^^^^^^^^^
    |
    = help: remove it
-   = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: `assert!(false)` should probably be replaced
-  --> $DIR/assertions_on_constants.rs:23:5
+  --> $DIR/assertions_on_constants.rs:22:5
    |
 LL |     assert!(C);
    |     ^^^^^^^^^^
    |
    = help: use `panic!()` or `unreachable!()`
-   = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: `assert!(false, $crate::const_format_args!($($t)+))` should probably be replaced
-  --> $DIR/assertions_on_constants.rs:24:5
+error: `assert!(false, ..)` should probably be replaced
+  --> $DIR/assertions_on_constants.rs:23:5
    |
 LL |     assert!(C, "C message");
    |     ^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: use `panic!($crate::const_format_args!($($t)+))` or `unreachable!($crate::const_format_args!($($t)+))`
-   = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
+   = help: use `panic!(..)` or `unreachable!(..)`
 
 error: `debug_assert!(true)` will be optimized out by the compiler
-  --> $DIR/assertions_on_constants.rs:26:5
+  --> $DIR/assertions_on_constants.rs:25:5
    |
 LL |     debug_assert!(true);
    |     ^^^^^^^^^^^^^^^^^^^
    |
    = help: remove it
-   = note: this error originates in the macro `$crate::assert` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to 8 previous errors
+error: aborting due to 9 previous errors
 
diff --git a/tests/ui/borrow_as_ptr.fixed b/tests/ui/borrow_as_ptr.fixed
new file mode 100644
index 00000000000..ff5c6a8c377
--- /dev/null
+++ b/tests/ui/borrow_as_ptr.fixed
@@ -0,0 +1,10 @@
+// run-rustfix
+#![warn(clippy::borrow_as_ptr)]
+
+fn main() {
+    let val = 1;
+    let _p = std::ptr::addr_of!(val);
+
+    let mut val_mut = 1;
+    let _p_mut = std::ptr::addr_of_mut!(val_mut);
+}
diff --git a/tests/ui/borrow_as_ptr.rs b/tests/ui/borrow_as_ptr.rs
new file mode 100644
index 00000000000..0f62ec6ee58
--- /dev/null
+++ b/tests/ui/borrow_as_ptr.rs
@@ -0,0 +1,10 @@
+// run-rustfix
+#![warn(clippy::borrow_as_ptr)]
+
+fn main() {
+    let val = 1;
+    let _p = &val as *const i32;
+
+    let mut val_mut = 1;
+    let _p_mut = &mut val_mut as *mut i32;
+}
diff --git a/tests/ui/borrow_as_ptr.stderr b/tests/ui/borrow_as_ptr.stderr
new file mode 100644
index 00000000000..be1ed733056
--- /dev/null
+++ b/tests/ui/borrow_as_ptr.stderr
@@ -0,0 +1,16 @@
+error: borrow as raw pointer
+  --> $DIR/borrow_as_ptr.rs:6:14
+   |
+LL |     let _p = &val as *const i32;
+   |              ^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::addr_of!(val)`
+   |
+   = note: `-D clippy::borrow-as-ptr` implied by `-D warnings`
+
+error: borrow as raw pointer
+  --> $DIR/borrow_as_ptr.rs:9:18
+   |
+LL |     let _p_mut = &mut val_mut as *mut i32;
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::addr_of_mut!(val_mut)`
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/borrow_as_ptr_no_std.fixed b/tests/ui/borrow_as_ptr_no_std.fixed
new file mode 100644
index 00000000000..eaba3b1c20c
--- /dev/null
+++ b/tests/ui/borrow_as_ptr_no_std.fixed
@@ -0,0 +1,22 @@
+// run-rustfix
+#![warn(clippy::borrow_as_ptr)]
+#![feature(lang_items, start, libc)]
+#![no_std]
+
+#[start]
+fn main(_argc: isize, _argv: *const *const u8) -> isize {
+    let val = 1;
+    let _p = core::ptr::addr_of!(val);
+
+    let mut val_mut = 1;
+    let _p_mut = core::ptr::addr_of_mut!(val_mut);
+    0
+}
+
+#[panic_handler]
+fn panic(_info: &core::panic::PanicInfo) -> ! {
+    loop {}
+}
+
+#[lang = "eh_personality"]
+extern "C" fn eh_personality() {}
diff --git a/tests/ui/borrow_as_ptr_no_std.rs b/tests/ui/borrow_as_ptr_no_std.rs
new file mode 100644
index 00000000000..d83f9d1f875
--- /dev/null
+++ b/tests/ui/borrow_as_ptr_no_std.rs
@@ -0,0 +1,22 @@
+// run-rustfix
+#![warn(clippy::borrow_as_ptr)]
+#![feature(lang_items, start, libc)]
+#![no_std]
+
+#[start]
+fn main(_argc: isize, _argv: *const *const u8) -> isize {
+    let val = 1;
+    let _p = &val as *const i32;
+
+    let mut val_mut = 1;
+    let _p_mut = &mut val_mut as *mut i32;
+    0
+}
+
+#[panic_handler]
+fn panic(_info: &core::panic::PanicInfo) -> ! {
+    loop {}
+}
+
+#[lang = "eh_personality"]
+extern "C" fn eh_personality() {}
diff --git a/tests/ui/borrow_as_ptr_no_std.stderr b/tests/ui/borrow_as_ptr_no_std.stderr
new file mode 100644
index 00000000000..84c8ba7d07f
--- /dev/null
+++ b/tests/ui/borrow_as_ptr_no_std.stderr
@@ -0,0 +1,16 @@
+error: borrow as raw pointer
+  --> $DIR/borrow_as_ptr_no_std.rs:9:14
+   |
+LL |     let _p = &val as *const i32;
+   |              ^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::addr_of!(val)`
+   |
+   = note: `-D clippy::borrow-as-ptr` implied by `-D warnings`
+
+error: borrow as raw pointer
+  --> $DIR/borrow_as_ptr_no_std.rs:12:18
+   |
+LL |     let _p_mut = &mut val_mut as *mut i32;
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::addr_of_mut!(val_mut)`
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/cast_alignment.rs b/tests/ui/cast_alignment.rs
index d011e84b115..659591fffbe 100644
--- a/tests/ui/cast_alignment.rs
+++ b/tests/ui/cast_alignment.rs
@@ -4,7 +4,13 @@
 extern crate libc;
 
 #[warn(clippy::cast_ptr_alignment)]
-#[allow(clippy::no_effect, clippy::unnecessary_operation, clippy::cast_lossless)]
+#[allow(
+    clippy::no_effect,
+    clippy::unnecessary_operation,
+    clippy::cast_lossless,
+    clippy::borrow_as_ptr
+)]
+
 fn main() {
     /* These should be warned against */
 
diff --git a/tests/ui/cast_alignment.stderr b/tests/ui/cast_alignment.stderr
index 7998b787b91..aedd3684455 100644
--- a/tests/ui/cast_alignment.stderr
+++ b/tests/ui/cast_alignment.stderr
@@ -1,5 +1,5 @@
 error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes)
-  --> $DIR/cast_alignment.rs:12:5
+  --> $DIR/cast_alignment.rs:18:5
    |
 LL |     (&1u8 as *const u8) as *const u16;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -7,19 +7,19 @@ LL |     (&1u8 as *const u8) as *const u16;
    = note: `-D clippy::cast-ptr-alignment` implied by `-D warnings`
 
 error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 < 2 bytes)
-  --> $DIR/cast_alignment.rs:13:5
+  --> $DIR/cast_alignment.rs:19:5
    |
 LL |     (&mut 1u8 as *mut u8) as *mut u16;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes)
-  --> $DIR/cast_alignment.rs:16:5
+  --> $DIR/cast_alignment.rs:22:5
    |
 LL |     (&1u8 as *const u8).cast::<u16>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 < 2 bytes)
-  --> $DIR/cast_alignment.rs:17:5
+  --> $DIR/cast_alignment.rs:23:5
    |
 LL |     (&mut 1u8 as *mut u8).cast::<u16>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/cast_ref_to_mut.rs b/tests/ui/cast_ref_to_mut.rs
index 089e5cfabe4..c48a734ba32 100644
--- a/tests/ui/cast_ref_to_mut.rs
+++ b/tests/ui/cast_ref_to_mut.rs
@@ -1,5 +1,5 @@
 #![warn(clippy::cast_ref_to_mut)]
-#![allow(clippy::no_effect)]
+#![allow(clippy::no_effect, clippy::borrow_as_ptr)]
 
 extern "C" {
     // N.B., mutability can be easily incorrect in FFI calls -- as
diff --git a/tests/ui/deref_addrof.fixed b/tests/ui/deref_addrof.fixed
index 0029fc673f1..2f489deb1ee 100644
--- a/tests/ui/deref_addrof.fixed
+++ b/tests/ui/deref_addrof.fixed
@@ -37,6 +37,8 @@ fn main() {
     let b = &a;
 
     let b = *aref;
+
+    let _ = unsafe { *core::ptr::addr_of!(a) };
 }
 
 #[rustfmt::skip]
diff --git a/tests/ui/deref_addrof.rs b/tests/ui/deref_addrof.rs
index f2f02dd5e72..49f360b9a7f 100644
--- a/tests/ui/deref_addrof.rs
+++ b/tests/ui/deref_addrof.rs
@@ -37,6 +37,8 @@ fn main() {
     let b = *&&a;
 
     let b = **&aref;
+
+    let _ = unsafe { *core::ptr::addr_of!(a) };
 }
 
 #[rustfmt::skip]
diff --git a/tests/ui/deref_addrof.stderr b/tests/ui/deref_addrof.stderr
index 5bc1cbfa215..75371fcdb96 100644
--- a/tests/ui/deref_addrof.stderr
+++ b/tests/ui/deref_addrof.stderr
@@ -49,7 +49,7 @@ LL |     let b = **&aref;
    |              ^^^^^^ help: try this: `aref`
 
 error: immediately dereferencing a reference
-  --> $DIR/deref_addrof.rs:45:9
+  --> $DIR/deref_addrof.rs:47:9
    |
 LL |         *& $visitor
    |         ^^^^^^^^^^^ help: try this: `$visitor`
@@ -60,7 +60,7 @@ LL |         m!(self)
    = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: immediately dereferencing a reference
-  --> $DIR/deref_addrof.rs:52:9
+  --> $DIR/deref_addrof.rs:54:9
    |
 LL |         *& mut $visitor
    |         ^^^^^^^^^^^^^^^ help: try this: `$visitor`
diff --git a/tests/ui/eq_op_macros.stderr b/tests/ui/eq_op_macros.stderr
index 885415b42c7..cd9f1826e59 100644
--- a/tests/ui/eq_op_macros.stderr
+++ b/tests/ui/eq_op_macros.stderr
@@ -21,6 +21,28 @@ LL |     assert_in_macro_def!();
    |
    = note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info)
 
+error: identical args used in this `debug_assert_eq!` macro call
+  --> $DIR/eq_op_macros.rs:9:26
+   |
+LL |         debug_assert_eq!(a, a);
+   |                          ^^^^
+...
+LL |     assert_in_macro_def!();
+   |     ---------------------- in this macro invocation
+   |
+   = note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: identical args used in this `debug_assert_ne!` macro call
+  --> $DIR/eq_op_macros.rs:10:26
+   |
+LL |         debug_assert_ne!(a, a);
+   |                          ^^^^
+...
+LL |     assert_in_macro_def!();
+   |     ---------------------- in this macro invocation
+   |
+   = note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info)
+
 error: identical args used in this `assert_eq!` macro call
   --> $DIR/eq_op_macros.rs:22:16
    |
@@ -45,28 +67,6 @@ error: identical args used in this `assert_ne!` macro call
 LL |     assert_ne!(a + 1, a + 1);
    |                ^^^^^^^^^^^^
 
-error: identical args used in this `debug_assert_eq!` macro call
-  --> $DIR/eq_op_macros.rs:9:26
-   |
-LL |         debug_assert_eq!(a, a);
-   |                          ^^^^
-...
-LL |     assert_in_macro_def!();
-   |     ---------------------- in this macro invocation
-   |
-   = note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: identical args used in this `debug_assert_ne!` macro call
-  --> $DIR/eq_op_macros.rs:10:26
-   |
-LL |         debug_assert_ne!(a, a);
-   |                          ^^^^
-...
-LL |     assert_in_macro_def!();
-   |     ---------------------- in this macro invocation
-   |
-   = note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info)
-
 error: identical args used in this `debug_assert_eq!` macro call
   --> $DIR/eq_op_macros.rs:38:22
    |
diff --git a/tests/ui/erasing_op.rs b/tests/ui/erasing_op.rs
index 1540062a4bc..ae2fad0086d 100644
--- a/tests/ui/erasing_op.rs
+++ b/tests/ui/erasing_op.rs
@@ -1,3 +1,34 @@
+struct Length(u8);
+struct Meter;
+
+impl core::ops::Mul<Meter> for u8 {
+    type Output = Length;
+    fn mul(self, _: Meter) -> Length {
+        Length(self)
+    }
+}
+
+#[derive(Clone, Default, PartialEq, Eq, Hash)]
+struct Vec1 {
+    x: i32,
+}
+
+impl core::ops::Mul<Vec1> for i32 {
+    type Output = Vec1;
+    fn mul(self, mut right: Vec1) -> Vec1 {
+        right.x *= self;
+        right
+    }
+}
+
+impl core::ops::Mul<i32> for Vec1 {
+    type Output = Vec1;
+    fn mul(mut self, right: i32) -> Vec1 {
+        self.x *= right;
+        self
+    }
+}
+
 #[allow(clippy::no_effect)]
 #[warn(clippy::erasing_op)]
 fn main() {
@@ -6,4 +37,7 @@ fn main() {
     x * 0;
     0 & x;
     0 / x;
+    0 * Meter; // no error: Output type is different from the non-zero argument
+    0 * Vec1 { x: 5 };
+    Vec1 { x: 5 } * 0;
 }
diff --git a/tests/ui/erasing_op.stderr b/tests/ui/erasing_op.stderr
index e54ce85f98e..165ed9bfe58 100644
--- a/tests/ui/erasing_op.stderr
+++ b/tests/ui/erasing_op.stderr
@@ -1,5 +1,5 @@
 error: this operation will always return zero. This is likely not the intended outcome
-  --> $DIR/erasing_op.rs:6:5
+  --> $DIR/erasing_op.rs:37:5
    |
 LL |     x * 0;
    |     ^^^^^
@@ -7,16 +7,28 @@ LL |     x * 0;
    = note: `-D clippy::erasing-op` implied by `-D warnings`
 
 error: this operation will always return zero. This is likely not the intended outcome
-  --> $DIR/erasing_op.rs:7:5
+  --> $DIR/erasing_op.rs:38:5
    |
 LL |     0 & x;
    |     ^^^^^
 
 error: this operation will always return zero. This is likely not the intended outcome
-  --> $DIR/erasing_op.rs:8:5
+  --> $DIR/erasing_op.rs:39:5
    |
 LL |     0 / x;
    |     ^^^^^
 
-error: aborting due to 3 previous errors
+error: this operation will always return zero. This is likely not the intended outcome
+  --> $DIR/erasing_op.rs:41:5
+   |
+LL |     0 * Vec1 { x: 5 };
+   |     ^^^^^^^^^^^^^^^^^
+
+error: this operation will always return zero. This is likely not the intended outcome
+  --> $DIR/erasing_op.rs:42:5
+   |
+LL |     Vec1 { x: 5 } * 0;
+   |     ^^^^^^^^^^^^^^^^^
+
+error: aborting due to 5 previous errors
 
diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed
index 1de79667f55..f938f710688 100644
--- a/tests/ui/eta.fixed
+++ b/tests/ui/eta.fixed
@@ -248,3 +248,14 @@ mod type_param_bound {
         take(X::fun as fn());
     }
 }
+
+// #8073 Don't replace closure with `Arc<F>` or `Rc<F>`
+fn arc_fp() {
+    let rc = std::rc::Rc::new(|| 7);
+    let arc = std::sync::Arc::new(|n| n + 1);
+    let ref_arc = &std::sync::Arc::new(|_| 5);
+
+    true.then(|| rc());
+    (0..5).map(|n| arc(n));
+    Some(4).map(|n| ref_arc(n));
+}
diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs
index 86abd347baa..075bbc74922 100644
--- a/tests/ui/eta.rs
+++ b/tests/ui/eta.rs
@@ -248,3 +248,14 @@ mod type_param_bound {
         take(X::fun as fn());
     }
 }
+
+// #8073 Don't replace closure with `Arc<F>` or `Rc<F>`
+fn arc_fp() {
+    let rc = std::rc::Rc::new(|| 7);
+    let arc = std::sync::Arc::new(|n| n + 1);
+    let ref_arc = &std::sync::Arc::new(|_| 5);
+
+    true.then(|| rc());
+    (0..5).map(|n| arc(n));
+    Some(4).map(|n| ref_arc(n));
+}
diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs
index 2ed4b5db574..12bbda71f43 100644
--- a/tests/ui/identity_op.rs
+++ b/tests/ui/identity_op.rs
@@ -11,6 +11,17 @@ impl std::ops::Shl<i32> for A {
         self
     }
 }
+
+struct Length(u8);
+struct Meter;
+
+impl core::ops::Mul<Meter> for u8 {
+    type Output = Length;
+    fn mul(self, _: Meter) -> Length {
+        Length(self)
+    }
+}
+
 #[allow(
     clippy::eq_op,
     clippy::no_effect,
@@ -53,4 +64,6 @@ fn main() {
 
     let mut a = A("".into());
     let b = a << 0; // no error: non-integer
+
+    1 * Meter; // no error: non-integer
 }
diff --git a/tests/ui/identity_op.stderr b/tests/ui/identity_op.stderr
index ff34b38db01..0103cf5457e 100644
--- a/tests/ui/identity_op.stderr
+++ b/tests/ui/identity_op.stderr
@@ -1,5 +1,5 @@
 error: the operation is ineffective. Consider reducing it to `x`
-  --> $DIR/identity_op.rs:26:5
+  --> $DIR/identity_op.rs:37:5
    |
 LL |     x + 0;
    |     ^^^^^
@@ -7,73 +7,73 @@ LL |     x + 0;
    = note: `-D clippy::identity-op` implied by `-D warnings`
 
 error: the operation is ineffective. Consider reducing it to `x`
-  --> $DIR/identity_op.rs:27:5
+  --> $DIR/identity_op.rs:38:5
    |
 LL |     x + (1 - 1);
    |     ^^^^^^^^^^^
 
 error: the operation is ineffective. Consider reducing it to `x`
-  --> $DIR/identity_op.rs:29:5
+  --> $DIR/identity_op.rs:40:5
    |
 LL |     0 + x;
    |     ^^^^^
 
 error: the operation is ineffective. Consider reducing it to `x`
-  --> $DIR/identity_op.rs:32:5
+  --> $DIR/identity_op.rs:43:5
    |
 LL |     x | (0);
    |     ^^^^^^^
 
 error: the operation is ineffective. Consider reducing it to `x`
-  --> $DIR/identity_op.rs:35:5
+  --> $DIR/identity_op.rs:46:5
    |
 LL |     x * 1;
    |     ^^^^^
 
 error: the operation is ineffective. Consider reducing it to `x`
-  --> $DIR/identity_op.rs:36:5
+  --> $DIR/identity_op.rs:47:5
    |
 LL |     1 * x;
    |     ^^^^^
 
 error: the operation is ineffective. Consider reducing it to `x`
-  --> $DIR/identity_op.rs:42:5
+  --> $DIR/identity_op.rs:53:5
    |
 LL |     -1 & x;
    |     ^^^^^^
 
 error: the operation is ineffective. Consider reducing it to `u`
-  --> $DIR/identity_op.rs:45:5
+  --> $DIR/identity_op.rs:56:5
    |
 LL |     u & 255;
    |     ^^^^^^^
 
 error: the operation is ineffective. Consider reducing it to `42`
-  --> $DIR/identity_op.rs:48:5
+  --> $DIR/identity_op.rs:59:5
    |
 LL |     42 << 0;
    |     ^^^^^^^
 
 error: the operation is ineffective. Consider reducing it to `1`
-  --> $DIR/identity_op.rs:49:5
+  --> $DIR/identity_op.rs:60:5
    |
 LL |     1 >> 0;
    |     ^^^^^^
 
 error: the operation is ineffective. Consider reducing it to `42`
-  --> $DIR/identity_op.rs:50:5
+  --> $DIR/identity_op.rs:61:5
    |
 LL |     42 >> 0;
    |     ^^^^^^^
 
 error: the operation is ineffective. Consider reducing it to `&x`
-  --> $DIR/identity_op.rs:51:5
+  --> $DIR/identity_op.rs:62:5
    |
 LL |     &x >> 0;
    |     ^^^^^^^
 
 error: the operation is ineffective. Consider reducing it to `x`
-  --> $DIR/identity_op.rs:52:5
+  --> $DIR/identity_op.rs:63:5
    |
 LL |     x >> &0;
    |     ^^^^^^^
diff --git a/tests/ui/issue_4266.stderr b/tests/ui/issue_4266.stderr
index 20419457b47..e5042aaa776 100644
--- a/tests/ui/issue_4266.stderr
+++ b/tests/ui/issue_4266.stderr
@@ -12,5 +12,14 @@ error: explicit lifetimes given in parameter types where they could be elided (o
 LL | async fn one_to_one<'a>(s: &'a str) -> &'a str {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 2 previous errors
+error: methods called `new` usually take no `self`
+  --> $DIR/issue_4266.rs:27:22
+   |
+LL |     pub async fn new(&mut self) -> Self {
+   |                      ^^^^^^^^^
+   |
+   = note: `-D clippy::wrong-self-convention` implied by `-D warnings`
+   = help: consider choosing a less ambiguous name
+
+error: aborting due to 3 previous errors
 
diff --git a/tests/ui/iter_not_returning_iterator.rs b/tests/ui/iter_not_returning_iterator.rs
index 377f760b3c4..2c91e02e842 100644
--- a/tests/ui/iter_not_returning_iterator.rs
+++ b/tests/ui/iter_not_returning_iterator.rs
@@ -44,4 +44,24 @@ impl Iterator for Counter {
     }
 }
 
+// Issue #8225
+trait Iter {
+    type I;
+    fn iter(&self) -> Self::I;
+}
+
+impl Iter for () {
+    type I = core::slice::Iter<'static, ()>;
+    fn iter(&self) -> Self::I {
+        [].iter()
+    }
+}
+
+struct S;
+impl S {
+    fn iter(&self) -> <() as Iter>::I {
+        ().iter()
+    }
+}
+
 fn main() {}
diff --git a/tests/ui/iter_not_returning_iterator.stderr b/tests/ui/iter_not_returning_iterator.stderr
index 2273cd0be66..44f02955836 100644
--- a/tests/ui/iter_not_returning_iterator.stderr
+++ b/tests/ui/iter_not_returning_iterator.stderr
@@ -12,5 +12,11 @@ error: this method is named `iter_mut` but its return type does not implement `I
 LL |     fn iter_mut(&self) -> Counter2 {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 2 previous errors
+error: this method is named `iter` but its return type does not implement `Iterator`
+  --> $DIR/iter_not_returning_iterator.rs:50:5
+   |
+LL |     fn iter(&self) -> Self::I;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
 
diff --git a/tests/ui/manual_bits.fixed b/tests/ui/manual_bits.fixed
new file mode 100644
index 00000000000..4f1b19b75b8
--- /dev/null
+++ b/tests/ui/manual_bits.fixed
@@ -0,0 +1,48 @@
+// run-rustfix
+
+#![warn(clippy::manual_bits)]
+#![allow(clippy::no_effect, path_statements, unused_must_use, clippy::unnecessary_operation)]
+
+use std::mem::{size_of, size_of_val};
+
+fn main() {
+    i8::BITS;
+    i16::BITS;
+    i32::BITS;
+    i64::BITS;
+    i128::BITS;
+    isize::BITS;
+
+    u8::BITS;
+    u16::BITS;
+    u32::BITS;
+    u64::BITS;
+    u128::BITS;
+    usize::BITS;
+
+    i8::BITS;
+    i16::BITS;
+    i32::BITS;
+    i64::BITS;
+    i128::BITS;
+    isize::BITS;
+
+    u8::BITS;
+    u16::BITS;
+    u32::BITS;
+    u64::BITS;
+    u128::BITS;
+    usize::BITS;
+
+    size_of::<usize>() * 4;
+    4 * size_of::<usize>();
+    size_of::<bool>() * 8;
+    8 * size_of::<bool>();
+
+    size_of_val(&0u32) * 8;
+
+    type Word = u32;
+    Word::BITS;
+    type Bool = bool;
+    size_of::<Bool>() * 8;
+}
diff --git a/tests/ui/manual_bits.rs b/tests/ui/manual_bits.rs
new file mode 100644
index 00000000000..f8a01313e6a
--- /dev/null
+++ b/tests/ui/manual_bits.rs
@@ -0,0 +1,48 @@
+// run-rustfix
+
+#![warn(clippy::manual_bits)]
+#![allow(clippy::no_effect, path_statements, unused_must_use, clippy::unnecessary_operation)]
+
+use std::mem::{size_of, size_of_val};
+
+fn main() {
+    size_of::<i8>() * 8;
+    size_of::<i16>() * 8;
+    size_of::<i32>() * 8;
+    size_of::<i64>() * 8;
+    size_of::<i128>() * 8;
+    size_of::<isize>() * 8;
+
+    size_of::<u8>() * 8;
+    size_of::<u16>() * 8;
+    size_of::<u32>() * 8;
+    size_of::<u64>() * 8;
+    size_of::<u128>() * 8;
+    size_of::<usize>() * 8;
+
+    8 * size_of::<i8>();
+    8 * size_of::<i16>();
+    8 * size_of::<i32>();
+    8 * size_of::<i64>();
+    8 * size_of::<i128>();
+    8 * size_of::<isize>();
+
+    8 * size_of::<u8>();
+    8 * size_of::<u16>();
+    8 * size_of::<u32>();
+    8 * size_of::<u64>();
+    8 * size_of::<u128>();
+    8 * size_of::<usize>();
+
+    size_of::<usize>() * 4;
+    4 * size_of::<usize>();
+    size_of::<bool>() * 8;
+    8 * size_of::<bool>();
+
+    size_of_val(&0u32) * 8;
+
+    type Word = u32;
+    size_of::<Word>() * 8;
+    type Bool = bool;
+    size_of::<Bool>() * 8;
+}
diff --git a/tests/ui/manual_bits.stderr b/tests/ui/manual_bits.stderr
new file mode 100644
index 00000000000..c4f5af2dcb0
--- /dev/null
+++ b/tests/ui/manual_bits.stderr
@@ -0,0 +1,154 @@
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+  --> $DIR/manual_bits.rs:9:5
+   |
+LL |     size_of::<i8>() * 8;
+   |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS`
+   |
+   = note: `-D clippy::manual-bits` implied by `-D warnings`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+  --> $DIR/manual_bits.rs:10:5
+   |
+LL |     size_of::<i16>() * 8;
+   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+  --> $DIR/manual_bits.rs:11:5
+   |
+LL |     size_of::<i32>() * 8;
+   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+  --> $DIR/manual_bits.rs:12:5
+   |
+LL |     size_of::<i64>() * 8;
+   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+  --> $DIR/manual_bits.rs:13:5
+   |
+LL |     size_of::<i128>() * 8;
+   |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+  --> $DIR/manual_bits.rs:14:5
+   |
+LL |     size_of::<isize>() * 8;
+   |     ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+  --> $DIR/manual_bits.rs:16:5
+   |
+LL |     size_of::<u8>() * 8;
+   |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+  --> $DIR/manual_bits.rs:17:5
+   |
+LL |     size_of::<u16>() * 8;
+   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+  --> $DIR/manual_bits.rs:18:5
+   |
+LL |     size_of::<u32>() * 8;
+   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+  --> $DIR/manual_bits.rs:19:5
+   |
+LL |     size_of::<u64>() * 8;
+   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+  --> $DIR/manual_bits.rs:20:5
+   |
+LL |     size_of::<u128>() * 8;
+   |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+  --> $DIR/manual_bits.rs:21:5
+   |
+LL |     size_of::<usize>() * 8;
+   |     ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+  --> $DIR/manual_bits.rs:23:5
+   |
+LL |     8 * size_of::<i8>();
+   |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+  --> $DIR/manual_bits.rs:24:5
+   |
+LL |     8 * size_of::<i16>();
+   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+  --> $DIR/manual_bits.rs:25:5
+   |
+LL |     8 * size_of::<i32>();
+   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+  --> $DIR/manual_bits.rs:26:5
+   |
+LL |     8 * size_of::<i64>();
+   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+  --> $DIR/manual_bits.rs:27:5
+   |
+LL |     8 * size_of::<i128>();
+   |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+  --> $DIR/manual_bits.rs:28:5
+   |
+LL |     8 * size_of::<isize>();
+   |     ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+  --> $DIR/manual_bits.rs:30:5
+   |
+LL |     8 * size_of::<u8>();
+   |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+  --> $DIR/manual_bits.rs:31:5
+   |
+LL |     8 * size_of::<u16>();
+   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+  --> $DIR/manual_bits.rs:32:5
+   |
+LL |     8 * size_of::<u32>();
+   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+  --> $DIR/manual_bits.rs:33:5
+   |
+LL |     8 * size_of::<u64>();
+   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+  --> $DIR/manual_bits.rs:34:5
+   |
+LL |     8 * size_of::<u128>();
+   |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+  --> $DIR/manual_bits.rs:35:5
+   |
+LL |     8 * size_of::<usize>();
+   |     ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+  --> $DIR/manual_bits.rs:45:5
+   |
+LL |     size_of::<Word>() * 8;
+   |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `Word::BITS`
+
+error: aborting due to 25 previous errors
+
diff --git a/tests/ui/manual_memcpy/with_loop_counters.stderr b/tests/ui/manual_memcpy/with_loop_counters.stderr
index 2e3ebadd7b5..79d40c0bcb1 100644
--- a/tests/ui/manual_memcpy/with_loop_counters.stderr
+++ b/tests/ui/manual_memcpy/with_loop_counters.stderr
@@ -5,7 +5,7 @@ LL | /     for i in 3..src.len() {
 LL | |         dst[i] = src[count];
 LL | |         count += 1;
 LL | |     }
-   | |_____^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)]);`
+   | |_____^ help: try replacing the loop by: `dst[3..src.len()].copy_from_slice(&src[..(src.len() - 3)]);`
    |
    = note: `-D clippy::manual-memcpy` implied by `-D warnings`
 
@@ -16,7 +16,7 @@ LL | /     for i in 3..src.len() {
 LL | |         dst[count] = src[i];
 LL | |         count += 1;
 LL | |     }
-   | |_____^ help: try replacing the loop by: `dst[..(src.len() - 3)].clone_from_slice(&src[3..]);`
+   | |_____^ help: try replacing the loop by: `dst[..(src.len() - 3)].copy_from_slice(&src[3..]);`
 
 error: it looks like you're manually copying between slices
   --> $DIR/with_loop_counters.rs:17:5
@@ -25,7 +25,7 @@ LL | /     for i in 0..src.len() {
 LL | |         dst[count] = src[i];
 LL | |         count += 1;
 LL | |     }
-   | |_____^ help: try replacing the loop by: `dst[3..(src.len() + 3)].clone_from_slice(&src[..]);`
+   | |_____^ help: try replacing the loop by: `dst[3..(src.len() + 3)].copy_from_slice(&src[..]);`
 
 error: it looks like you're manually copying between slices
   --> $DIR/with_loop_counters.rs:23:5
@@ -34,7 +34,7 @@ LL | /     for i in 0..src.len() {
 LL | |         dst[i] = src[count];
 LL | |         count += 1;
 LL | |     }
-   | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[3..(src.len() + 3)]);`
+   | |_____^ help: try replacing the loop by: `dst[..src.len()].copy_from_slice(&src[3..(src.len() + 3)]);`
 
 error: it looks like you're manually copying between slices
   --> $DIR/with_loop_counters.rs:29:5
@@ -43,7 +43,7 @@ LL | /     for i in 3..(3 + src.len()) {
 LL | |         dst[i] = src[count];
 LL | |         count += 1;
 LL | |     }
-   | |_____^ help: try replacing the loop by: `dst[3..(3 + src.len())].clone_from_slice(&src[..(3 + src.len() - 3)]);`
+   | |_____^ help: try replacing the loop by: `dst[3..(3 + src.len())].copy_from_slice(&src[..(3 + src.len() - 3)]);`
 
 error: it looks like you're manually copying between slices
   --> $DIR/with_loop_counters.rs:35:5
@@ -52,7 +52,7 @@ LL | /     for i in 5..src.len() {
 LL | |         dst[i] = src[count - 2];
 LL | |         count += 1;
 LL | |     }
-   | |_____^ help: try replacing the loop by: `dst[5..src.len()].clone_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)]);`
+   | |_____^ help: try replacing the loop by: `dst[5..src.len()].copy_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)]);`
 
 error: it looks like you're manually copying between slices
   --> $DIR/with_loop_counters.rs:41:5
@@ -61,7 +61,7 @@ LL | /     for i in 0..dst.len() {
 LL | |         dst[i] = src[count];
 LL | |         count += 1;
 LL | |     }
-   | |_____^ help: try replacing the loop by: `dst.clone_from_slice(&src[2..(dst.len() + 2)]);`
+   | |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[2..(dst.len() + 2)]);`
 
 error: it looks like you're manually copying between slices
   --> $DIR/with_loop_counters.rs:47:5
@@ -70,7 +70,7 @@ LL | /     for i in 3..10 {
 LL | |         dst[i] = src[count];
 LL | |         count += 1;
 LL | |     }
-   | |_____^ help: try replacing the loop by: `dst[3..10].clone_from_slice(&src[5..(10 + 5 - 3)]);`
+   | |_____^ help: try replacing the loop by: `dst[3..10].copy_from_slice(&src[5..(10 + 5 - 3)]);`
 
 error: it looks like you're manually copying between slices
   --> $DIR/with_loop_counters.rs:54:5
@@ -85,8 +85,8 @@ LL | |     }
    |
 help: try replacing the loop by
    |
-LL ~     dst[3..(src.len() + 3)].clone_from_slice(&src[..]);
-LL +     dst2[30..(src.len() + 30)].clone_from_slice(&src[..]);
+LL ~     dst[3..(src.len() + 3)].copy_from_slice(&src[..]);
+LL +     dst2[30..(src.len() + 30)].copy_from_slice(&src[..]);
    |
 
 error: it looks like you're manually copying between slices
@@ -96,7 +96,7 @@ LL | /     for i in 0..1 << 1 {
 LL | |         dst[count] = src[i + 2];
 LL | |         count += 1;
 LL | |     }
-   | |_____^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)]);`
+   | |_____^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].copy_from_slice(&src[2..((1 << 1) + 2)]);`
 
 error: it looks like you're manually copying between slices
   --> $DIR/with_loop_counters.rs:71:5
@@ -105,7 +105,7 @@ LL | /     for i in 3..src.len() {
 LL | |         dst[i] = src[count];
 LL | |         count += 1
 LL | |     }
-   | |_____^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)]);`
+   | |_____^ help: try replacing the loop by: `dst[3..src.len()].copy_from_slice(&src[..(src.len() - 3)]);`
 
 error: aborting due to 11 previous errors
 
diff --git a/tests/ui/manual_memcpy/without_loop_counters.rs b/tests/ui/manual_memcpy/without_loop_counters.rs
index 0083f94798f..ea0535d076b 100644
--- a/tests/ui/manual_memcpy/without_loop_counters.rs
+++ b/tests/ui/manual_memcpy/without_loop_counters.rs
@@ -113,6 +113,17 @@ pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) {
     for i in 0.. {
         dst[i] = src[i];
     }
+
+    // VecDeque - ideally this would work, but would require something like `range_as_slices`
+    let mut dst = std::collections::VecDeque::from_iter([0; 5]);
+    let src = std::collections::VecDeque::from_iter([0, 1, 2, 3, 4]);
+    for i in 0..dst.len() {
+        dst[i] = src[i];
+    }
+    let src = vec![0, 1, 2, 3, 4];
+    for i in 0..dst.len() {
+        dst[i] = src[i];
+    }
 }
 
 #[warn(clippy::needless_range_loop, clippy::manual_memcpy)]
diff --git a/tests/ui/manual_memcpy/without_loop_counters.stderr b/tests/ui/manual_memcpy/without_loop_counters.stderr
index 8ff0137a812..c163ae061df 100644
--- a/tests/ui/manual_memcpy/without_loop_counters.stderr
+++ b/tests/ui/manual_memcpy/without_loop_counters.stderr
@@ -4,7 +4,7 @@ error: it looks like you're manually copying between slices
 LL | /     for i in 0..src.len() {
 LL | |         dst[i] = src[i];
 LL | |     }
-   | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..]);`
+   | |_____^ help: try replacing the loop by: `dst[..src.len()].copy_from_slice(&src[..]);`
    |
    = note: `-D clippy::manual-memcpy` implied by `-D warnings`
 
@@ -14,7 +14,7 @@ error: it looks like you're manually copying between slices
 LL | /     for i in 0..src.len() {
 LL | |         dst[i + 10] = src[i];
 LL | |     }
-   | |_____^ help: try replacing the loop by: `dst[10..(src.len() + 10)].clone_from_slice(&src[..]);`
+   | |_____^ help: try replacing the loop by: `dst[10..(src.len() + 10)].copy_from_slice(&src[..]);`
 
 error: it looks like you're manually copying between slices
   --> $DIR/without_loop_counters.rs:17:5
@@ -22,7 +22,7 @@ error: it looks like you're manually copying between slices
 LL | /     for i in 0..src.len() {
 LL | |         dst[i] = src[i + 10];
 LL | |     }
-   | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[10..(src.len() + 10)]);`
+   | |_____^ help: try replacing the loop by: `dst[..src.len()].copy_from_slice(&src[10..(src.len() + 10)]);`
 
 error: it looks like you're manually copying between slices
   --> $DIR/without_loop_counters.rs:22:5
@@ -30,7 +30,7 @@ error: it looks like you're manually copying between slices
 LL | /     for i in 11..src.len() {
 LL | |         dst[i] = src[i - 10];
 LL | |     }
-   | |_____^ help: try replacing the loop by: `dst[11..src.len()].clone_from_slice(&src[(11 - 10)..(src.len() - 10)]);`
+   | |_____^ help: try replacing the loop by: `dst[11..src.len()].copy_from_slice(&src[(11 - 10)..(src.len() - 10)]);`
 
 error: it looks like you're manually copying between slices
   --> $DIR/without_loop_counters.rs:27:5
@@ -38,7 +38,7 @@ error: it looks like you're manually copying between slices
 LL | /     for i in 0..dst.len() {
 LL | |         dst[i] = src[i];
 LL | |     }
-   | |_____^ help: try replacing the loop by: `dst.clone_from_slice(&src[..dst.len()]);`
+   | |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[..dst.len()]);`
 
 error: it looks like you're manually copying between slices
   --> $DIR/without_loop_counters.rs:40:5
@@ -51,8 +51,8 @@ LL | |     }
    |
 help: try replacing the loop by
    |
-LL ~     dst[10..256].clone_from_slice(&src[(10 - 5)..(256 - 5)]);
-LL +     dst2[(10 + 500)..(256 + 500)].clone_from_slice(&src[10..256]);
+LL ~     dst[10..256].copy_from_slice(&src[(10 - 5)..(256 - 5)]);
+LL +     dst2[(10 + 500)..(256 + 500)].copy_from_slice(&src[10..256]);
    |
 
 error: it looks like you're manually copying between slices
@@ -61,7 +61,7 @@ error: it looks like you're manually copying between slices
 LL | /     for i in 10..LOOP_OFFSET {
 LL | |         dst[i + LOOP_OFFSET] = src[i - some_var];
 LL | |     }
-   | |_____^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].clone_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)]);`
+   | |_____^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].copy_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)]);`
 
 error: it looks like you're manually copying between slices
   --> $DIR/without_loop_counters.rs:65:5
@@ -69,7 +69,7 @@ error: it looks like you're manually copying between slices
 LL | /     for i in 0..src_vec.len() {
 LL | |         dst_vec[i] = src_vec[i];
 LL | |     }
-   | |_____^ help: try replacing the loop by: `dst_vec[..src_vec.len()].clone_from_slice(&src_vec[..]);`
+   | |_____^ help: try replacing the loop by: `dst_vec[..src_vec.len()].copy_from_slice(&src_vec[..]);`
 
 error: it looks like you're manually copying between slices
   --> $DIR/without_loop_counters.rs:94:5
@@ -77,7 +77,7 @@ error: it looks like you're manually copying between slices
 LL | /     for i in from..from + src.len() {
 LL | |         dst[i] = src[i - from];
 LL | |     }
-   | |_____^ help: try replacing the loop by: `dst[from..(from + src.len())].clone_from_slice(&src[..(from + src.len() - from)]);`
+   | |_____^ help: try replacing the loop by: `dst[from..(from + src.len())].copy_from_slice(&src[..(from + src.len() - from)]);`
 
 error: it looks like you're manually copying between slices
   --> $DIR/without_loop_counters.rs:98:5
@@ -85,7 +85,7 @@ error: it looks like you're manually copying between slices
 LL | /     for i in from..from + 3 {
 LL | |         dst[i] = src[i - from];
 LL | |     }
-   | |_____^ help: try replacing the loop by: `dst[from..(from + 3)].clone_from_slice(&src[..(from + 3 - from)]);`
+   | |_____^ help: try replacing the loop by: `dst[from..(from + 3)].copy_from_slice(&src[..(from + 3 - from)]);`
 
 error: it looks like you're manually copying between slices
   --> $DIR/without_loop_counters.rs:103:5
@@ -93,7 +93,7 @@ error: it looks like you're manually copying between slices
 LL | /     for i in 0..5 {
 LL | |         dst[i - 0] = src[i];
 LL | |     }
-   | |_____^ help: try replacing the loop by: `dst[..5].clone_from_slice(&src[..5]);`
+   | |_____^ help: try replacing the loop by: `dst[..5].copy_from_slice(&src[..5]);`
 
 error: it looks like you're manually copying between slices
   --> $DIR/without_loop_counters.rs:108:5
@@ -101,10 +101,10 @@ error: it looks like you're manually copying between slices
 LL | /     for i in 0..0 {
 LL | |         dst[i] = src[i];
 LL | |     }
-   | |_____^ help: try replacing the loop by: `dst[..0].clone_from_slice(&src[..0]);`
+   | |_____^ help: try replacing the loop by: `dst[..0].copy_from_slice(&src[..0]);`
 
 error: it looks like you're manually copying between slices
-  --> $DIR/without_loop_counters.rs:120:5
+  --> $DIR/without_loop_counters.rs:131:5
    |
 LL | /     for i in 0..src.len() {
 LL | |         dst[i] = src[i].clone();
diff --git a/tests/ui/missing_panics_doc.stderr b/tests/ui/missing_panics_doc.stderr
index 8bccbaefe23..91ebd695238 100644
--- a/tests/ui/missing_panics_doc.stderr
+++ b/tests/ui/missing_panics_doc.stderr
@@ -27,7 +27,6 @@ note: first possible panic found here
    |
 LL |     panic!("This function panics")
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: docs for function which may panic missing `# Panics` section
   --> $DIR/missing_panics_doc.rs:17:1
@@ -42,7 +41,6 @@ note: first possible panic found here
    |
 LL |     todo!()
    |     ^^^^^^^
-   = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: docs for function which may panic missing `# Panics` section
   --> $DIR/missing_panics_doc.rs:22:1
@@ -61,7 +59,6 @@ note: first possible panic found here
    |
 LL |             panic!()
    |             ^^^^^^^^
-   = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: docs for function which may panic missing `# Panics` section
   --> $DIR/missing_panics_doc.rs:31:1
@@ -76,7 +73,6 @@ note: first possible panic found here
    |
 LL |     if true { unreachable!() } else { panic!() }
    |                                       ^^^^^^^^
-   = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: docs for function which may panic missing `# Panics` section
   --> $DIR/missing_panics_doc.rs:36:1
@@ -92,7 +88,6 @@ note: first possible panic found here
    |
 LL |     assert_eq!(x, 0);
    |     ^^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: docs for function which may panic missing `# Panics` section
   --> $DIR/missing_panics_doc.rs:42:1
@@ -108,7 +103,6 @@ note: first possible panic found here
    |
 LL |     assert_ne!(x, 0);
    |     ^^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `assert_ne` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 7 previous errors
 
diff --git a/tests/ui/mutex_atomic.rs b/tests/ui/mutex_atomic.rs
index b9d78b7f479..47b3dad3989 100644
--- a/tests/ui/mutex_atomic.rs
+++ b/tests/ui/mutex_atomic.rs
@@ -1,5 +1,7 @@
 #![warn(clippy::all)]
 #![warn(clippy::mutex_integer)]
+#![warn(clippy::mutex_atomic)]
+#![allow(clippy::borrow_as_ptr)]
 
 fn main() {
     use std::sync::Mutex;
diff --git a/tests/ui/mutex_atomic.stderr b/tests/ui/mutex_atomic.stderr
index a3511ba708a..262028a8723 100644
--- a/tests/ui/mutex_atomic.stderr
+++ b/tests/ui/mutex_atomic.stderr
@@ -1,5 +1,5 @@
 error: consider using an `AtomicBool` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>`
-  --> $DIR/mutex_atomic.rs:6:5
+  --> $DIR/mutex_atomic.rs:8:5
    |
 LL |     Mutex::new(true);
    |     ^^^^^^^^^^^^^^^^
@@ -7,31 +7,31 @@ LL |     Mutex::new(true);
    = note: `-D clippy::mutex-atomic` implied by `-D warnings`
 
 error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>`
-  --> $DIR/mutex_atomic.rs:7:5
+  --> $DIR/mutex_atomic.rs:9:5
    |
 LL |     Mutex::new(5usize);
    |     ^^^^^^^^^^^^^^^^^^
 
 error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>`
-  --> $DIR/mutex_atomic.rs:8:5
+  --> $DIR/mutex_atomic.rs:10:5
    |
 LL |     Mutex::new(9isize);
    |     ^^^^^^^^^^^^^^^^^^
 
 error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>`
-  --> $DIR/mutex_atomic.rs:10:5
+  --> $DIR/mutex_atomic.rs:12:5
    |
 LL |     Mutex::new(&x as *const u32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>`
-  --> $DIR/mutex_atomic.rs:11:5
+  --> $DIR/mutex_atomic.rs:13:5
    |
 LL |     Mutex::new(&mut x as *mut u32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>`
-  --> $DIR/mutex_atomic.rs:12:5
+  --> $DIR/mutex_atomic.rs:14:5
    |
 LL |     Mutex::new(0u32);
    |     ^^^^^^^^^^^^^^^^
@@ -39,7 +39,7 @@ LL |     Mutex::new(0u32);
    = note: `-D clippy::mutex-integer` implied by `-D warnings`
 
 error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>`
-  --> $DIR/mutex_atomic.rs:13:5
+  --> $DIR/mutex_atomic.rs:15:5
    |
 LL |     Mutex::new(0i32);
    |     ^^^^^^^^^^^^^^^^
diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed
index d6d6ab49734..87cdb3ace47 100644
--- a/tests/ui/or_fun_call.fixed
+++ b/tests/ui/or_fun_call.fixed
@@ -2,7 +2,7 @@
 
 #![warn(clippy::or_fun_call)]
 #![allow(dead_code)]
-#![allow(clippy::unnecessary_wraps)]
+#![allow(clippy::unnecessary_wraps, clippy::borrow_as_ptr)]
 
 use std::collections::BTreeMap;
 use std::collections::HashMap;
diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs
index 8eadc6ce3b4..3f69cef301c 100644
--- a/tests/ui/or_fun_call.rs
+++ b/tests/ui/or_fun_call.rs
@@ -2,7 +2,7 @@
 
 #![warn(clippy::or_fun_call)]
 #![allow(dead_code)]
-#![allow(clippy::unnecessary_wraps)]
+#![allow(clippy::unnecessary_wraps, clippy::borrow_as_ptr)]
 
 use std::collections::BTreeMap;
 use std::collections::HashMap;
diff --git a/tests/ui/panic_in_result_fn.stderr b/tests/ui/panic_in_result_fn.stderr
index 78d09b8b210..561503ae54f 100644
--- a/tests/ui/panic_in_result_fn.stderr
+++ b/tests/ui/panic_in_result_fn.stderr
@@ -14,7 +14,6 @@ note: return Err() instead of panicking
    |
 LL |         panic!("error");
    |         ^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
   --> $DIR/panic_in_result_fn.rs:11:5
@@ -31,7 +30,6 @@ note: return Err() instead of panicking
    |
 LL |         unimplemented!();
    |         ^^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
   --> $DIR/panic_in_result_fn.rs:16:5
@@ -48,7 +46,6 @@ note: return Err() instead of panicking
    |
 LL |         unreachable!();
    |         ^^^^^^^^^^^^^^
-   = note: this error originates in the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
   --> $DIR/panic_in_result_fn.rs:21:5
@@ -65,7 +62,6 @@ note: return Err() instead of panicking
    |
 LL |         todo!("Finish this");
    |         ^^^^^^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
   --> $DIR/panic_in_result_fn.rs:52:1
@@ -82,7 +78,6 @@ note: return Err() instead of panicking
    |
 LL |     panic!("error");
    |     ^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
   --> $DIR/panic_in_result_fn.rs:67:1
@@ -99,7 +94,6 @@ note: return Err() instead of panicking
    |
 LL |     todo!("finish main method");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 6 previous errors
 
diff --git a/tests/ui/panic_in_result_fn_assertions.stderr b/tests/ui/panic_in_result_fn_assertions.stderr
index 7501d6d85ed..b6aa005e7b5 100644
--- a/tests/ui/panic_in_result_fn_assertions.stderr
+++ b/tests/ui/panic_in_result_fn_assertions.stderr
@@ -15,7 +15,6 @@ note: return Err() instead of panicking
    |
 LL |         assert!(x == 5, "wrong argument");
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
   --> $DIR/panic_in_result_fn_assertions.rs:13:5
@@ -33,7 +32,6 @@ note: return Err() instead of panicking
    |
 LL |         assert_eq!(x, 5);
    |         ^^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
   --> $DIR/panic_in_result_fn_assertions.rs:19:5
@@ -51,7 +49,6 @@ note: return Err() instead of panicking
    |
 LL |         assert_ne!(x, 1);
    |         ^^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `assert_ne` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/panicking_macros.stderr b/tests/ui/panicking_macros.stderr
index 2b607ff5888..bfd1c7a3801 100644
--- a/tests/ui/panicking_macros.stderr
+++ b/tests/ui/panicking_macros.stderr
@@ -25,23 +25,18 @@ LL |     todo!();
    |     ^^^^^^^
    |
    = note: `-D clippy::todo` implied by `-D warnings`
-   = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: `todo` should not be present in production code
   --> $DIR/panicking_macros.rs:17:5
    |
 LL |     todo!("message");
    |     ^^^^^^^^^^^^^^^^
-   |
-   = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: `todo` should not be present in production code
   --> $DIR/panicking_macros.rs:18:5
    |
 LL |     todo!("{} {}", "panic with", "multiple arguments");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: `unimplemented` should not be present in production code
   --> $DIR/panicking_macros.rs:24:5
@@ -50,23 +45,18 @@ LL |     unimplemented!();
    |     ^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::unimplemented` implied by `-D warnings`
-   = note: this error originates in the macro `unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: `unimplemented` should not be present in production code
   --> $DIR/panicking_macros.rs:25:5
    |
 LL |     unimplemented!("message");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: this error originates in the macro `unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: `unimplemented` should not be present in production code
   --> $DIR/panicking_macros.rs:26:5
    |
 LL |     unimplemented!("{} {}", "panic with", "multiple arguments");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: this error originates in the macro `unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: usage of the `unreachable!` macro
   --> $DIR/panicking_macros.rs:32:5
@@ -75,23 +65,18 @@ LL |     unreachable!();
    |     ^^^^^^^^^^^^^^
    |
    = note: `-D clippy::unreachable` implied by `-D warnings`
-   = note: this error originates in the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: usage of the `unreachable!` macro
   --> $DIR/panicking_macros.rs:33:5
    |
 LL |     unreachable!("message");
    |     ^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: this error originates in the macro `$crate::unreachable` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: usage of the `unreachable!` macro
   --> $DIR/panicking_macros.rs:34:5
    |
 LL |     unreachable!("{} {}", "panic with", "multiple arguments");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: this error originates in the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: `panic` should not be present in production code
   --> $DIR/panicking_macros.rs:40:5
@@ -104,24 +89,18 @@ error: `todo` should not be present in production code
    |
 LL |     todo!();
    |     ^^^^^^^
-   |
-   = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: `unimplemented` should not be present in production code
   --> $DIR/panicking_macros.rs:42:5
    |
 LL |     unimplemented!();
    |     ^^^^^^^^^^^^^^^^
-   |
-   = note: this error originates in the macro `unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: usage of the `unreachable!` macro
   --> $DIR/panicking_macros.rs:43:5
    |
 LL |     unreachable!();
    |     ^^^^^^^^^^^^^^
-   |
-   = note: this error originates in the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 16 previous errors
 
diff --git a/tests/ui/return_self_not_must_use.stderr b/tests/ui/return_self_not_must_use.stderr
index 3793a5559ba..8af10cb65c4 100644
--- a/tests/ui/return_self_not_must_use.stderr
+++ b/tests/ui/return_self_not_must_use.stderr
@@ -5,6 +5,7 @@ LL |     fn what(&self) -> Self;
    |     ^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::return-self-not-must-use` implied by `-D warnings`
+   = help: consider adding the `#[must_use]` attribute to the method or directly to the `Self` type
 
 error: missing `#[must_use]` attribute on a method returning `Self`
   --> $DIR/return_self_not_must_use.rs:17:5
@@ -13,6 +14,8 @@ LL | /     pub fn foo(&self) -> Self {
 LL | |         Self
 LL | |     }
    | |_____^
+   |
+   = help: consider adding the `#[must_use]` attribute to the method or directly to the `Self` type
 
 error: missing `#[must_use]` attribute on a method returning `Self`
   --> $DIR/return_self_not_must_use.rs:20:5
@@ -21,6 +24,8 @@ LL | /     pub fn bar(self) -> Self {
 LL | |         self
 LL | |     }
    | |_____^
+   |
+   = help: consider adding the `#[must_use]` attribute to the method or directly to the `Self` type
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/single_char_lifetime_names.rs b/tests/ui/single_char_lifetime_names.rs
new file mode 100644
index 00000000000..261d8bc7260
--- /dev/null
+++ b/tests/ui/single_char_lifetime_names.rs
@@ -0,0 +1,43 @@
+#![warn(clippy::single_char_lifetime_names)]
+
+// Lifetimes should only be linted when they're introduced
+struct DiagnosticCtx<'a, 'b>
+where
+    'a: 'b,
+{
+    _source: &'a str,
+    _unit: &'b (),
+}
+
+// Only the lifetimes on the `impl`'s generics should be linted
+impl<'a, 'b> DiagnosticCtx<'a, 'b> {
+    fn new(source: &'a str, unit: &'b ()) -> DiagnosticCtx<'a, 'b> {
+        Self {
+            _source: source,
+            _unit: unit,
+        }
+    }
+}
+
+// No lifetimes should be linted here
+impl<'src, 'unit> DiagnosticCtx<'src, 'unit> {
+    fn new_pass(source: &'src str, unit: &'unit ()) -> DiagnosticCtx<'src, 'unit> {
+        Self {
+            _source: source,
+            _unit: unit,
+        }
+    }
+}
+
+// Only 'a should be linted here
+fn split_once<'a>(base: &'a str, other: &'_ str) -> (&'a str, Option<&'a str>) {
+    base.split_once(other)
+        .map(|(left, right)| (left, Some(right)))
+        .unwrap_or((base, None))
+}
+
+fn main() {
+    let src = "loop {}";
+    let unit = ();
+    DiagnosticCtx::new(src, &unit);
+}
diff --git a/tests/ui/single_char_lifetime_names.stderr b/tests/ui/single_char_lifetime_names.stderr
new file mode 100644
index 00000000000..013b64f46a8
--- /dev/null
+++ b/tests/ui/single_char_lifetime_names.stderr
@@ -0,0 +1,43 @@
+error: single-character lifetime names are likely uninformative
+  --> $DIR/single_char_lifetime_names.rs:4:22
+   |
+LL | struct DiagnosticCtx<'a, 'b>
+   |                      ^^
+   |
+   = note: `-D clippy::single-char-lifetime-names` implied by `-D warnings`
+   = help: use a more informative name
+
+error: single-character lifetime names are likely uninformative
+  --> $DIR/single_char_lifetime_names.rs:4:26
+   |
+LL | struct DiagnosticCtx<'a, 'b>
+   |                          ^^
+   |
+   = help: use a more informative name
+
+error: single-character lifetime names are likely uninformative
+  --> $DIR/single_char_lifetime_names.rs:13:6
+   |
+LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> {
+   |      ^^
+   |
+   = help: use a more informative name
+
+error: single-character lifetime names are likely uninformative
+  --> $DIR/single_char_lifetime_names.rs:13:10
+   |
+LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> {
+   |          ^^
+   |
+   = help: use a more informative name
+
+error: single-character lifetime names are likely uninformative
+  --> $DIR/single_char_lifetime_names.rs:33:15
+   |
+LL | fn split_once<'a>(base: &'a str, other: &'_ str) -> (&'a str, Option<&'a str>) {
+   |               ^^
+   |
+   = help: use a more informative name
+
+error: aborting due to 5 previous errors
+
diff --git a/tests/ui/swap.fixed b/tests/ui/swap.fixed
index ef518359ec5..3329efbd4ff 100644
--- a/tests/ui/swap.fixed
+++ b/tests/ui/swap.fixed
@@ -122,3 +122,36 @@ fn main() {
 
     ; std::mem::swap(&mut c.0, &mut a);
 }
+
+fn issue_8154() {
+    struct S1 {
+        x: i32,
+        y: i32,
+    }
+    struct S2(S1);
+    struct S3<'a, 'b>(&'a mut &'b mut S1);
+
+    impl std::ops::Deref for S2 {
+        type Target = S1;
+        fn deref(&self) -> &Self::Target {
+            &self.0
+        }
+    }
+    impl std::ops::DerefMut for S2 {
+        fn deref_mut(&mut self) -> &mut Self::Target {
+            &mut self.0
+        }
+    }
+
+    // Don't lint. `s.0` is mutably borrowed by `s.x` and `s.y` via the deref impl.
+    let mut s = S2(S1 { x: 0, y: 0 });
+    let t = s.x;
+    s.x = s.y;
+    s.y = t;
+
+    // Accessing through a mutable reference is fine
+    let mut s = S1 { x: 0, y: 0 };
+    let mut s = &mut s;
+    let s = S3(&mut s);
+    std::mem::swap(&mut s.0.x, &mut s.0.y);
+}
diff --git a/tests/ui/swap.rs b/tests/ui/swap.rs
index 8518659ccf3..8179ac1f2ab 100644
--- a/tests/ui/swap.rs
+++ b/tests/ui/swap.rs
@@ -144,3 +144,38 @@ fn main() {
     c.0 = a;
     a = t;
 }
+
+fn issue_8154() {
+    struct S1 {
+        x: i32,
+        y: i32,
+    }
+    struct S2(S1);
+    struct S3<'a, 'b>(&'a mut &'b mut S1);
+
+    impl std::ops::Deref for S2 {
+        type Target = S1;
+        fn deref(&self) -> &Self::Target {
+            &self.0
+        }
+    }
+    impl std::ops::DerefMut for S2 {
+        fn deref_mut(&mut self) -> &mut Self::Target {
+            &mut self.0
+        }
+    }
+
+    // Don't lint. `s.0` is mutably borrowed by `s.x` and `s.y` via the deref impl.
+    let mut s = S2(S1 { x: 0, y: 0 });
+    let t = s.x;
+    s.x = s.y;
+    s.y = t;
+
+    // Accessing through a mutable reference is fine
+    let mut s = S1 { x: 0, y: 0 };
+    let mut s = &mut s;
+    let s = S3(&mut s);
+    let t = s.0.x;
+    s.0.x = s.0.y;
+    s.0.y = t;
+}
diff --git a/tests/ui/swap.stderr b/tests/ui/swap.stderr
index 614d16ced40..2b556b475ce 100644
--- a/tests/ui/swap.stderr
+++ b/tests/ui/swap.stderr
@@ -108,5 +108,15 @@ LL | |     a = c.0;
    |
    = note: or maybe you should use `std::mem::replace`?
 
-error: aborting due to 12 previous errors
+error: this looks like you are swapping `s.0.x` and `s.0.y` manually
+  --> $DIR/swap.rs:178:5
+   |
+LL | /     let t = s.0.x;
+LL | |     s.0.x = s.0.y;
+LL | |     s.0.y = t;
+   | |_____________^ help: try: `std::mem::swap(&mut s.0.x, &mut s.0.y)`
+   |
+   = note: or maybe you should use `std::mem::replace`?
+
+error: aborting due to 13 previous errors
 
diff --git a/tests/ui/trait_duplication_in_bounds.rs b/tests/ui/trait_duplication_in_bounds.rs
index cb2b0054e35..2edb202892a 100644
--- a/tests/ui/trait_duplication_in_bounds.rs
+++ b/tests/ui/trait_duplication_in_bounds.rs
@@ -28,4 +28,49 @@ where
     unimplemented!();
 }
 
+trait T: Default {
+    fn f()
+    where
+        Self: Default;
+}
+
+trait U: Default {
+    fn f()
+    where
+        Self: Clone;
+}
+
+trait ZZ: Default {
+    fn g();
+    fn h();
+    fn f()
+    where
+        Self: Default + Clone;
+}
+
+trait BadTrait: Default + Clone {
+    fn f()
+    where
+        Self: Default + Clone;
+    fn g()
+    where
+        Self: Default;
+    fn h()
+    where
+        Self: Copy;
+}
+
+#[derive(Default, Clone)]
+struct Life {}
+
+impl T for Life {
+    // this should not warn
+    fn f() {}
+}
+
+impl U for Life {
+    // this should not warn
+    fn f() {}
+}
+
 fn main() {}
diff --git a/tests/ui/trait_duplication_in_bounds.stderr b/tests/ui/trait_duplication_in_bounds.stderr
index 027e1c75204..e0c7a7ec618 100644
--- a/tests/ui/trait_duplication_in_bounds.stderr
+++ b/tests/ui/trait_duplication_in_bounds.stderr
@@ -19,5 +19,45 @@ LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
    |
    = help: consider removing this trait bound
 
-error: aborting due to 2 previous errors
+error: this trait bound is already specified in trait declaration
+  --> $DIR/trait_duplication_in_bounds.rs:34:15
+   |
+LL |         Self: Default;
+   |               ^^^^^^^
+   |
+   = help: consider removing this trait bound
+
+error: this trait bound is already specified in trait declaration
+  --> $DIR/trait_duplication_in_bounds.rs:48:15
+   |
+LL |         Self: Default + Clone;
+   |               ^^^^^^^
+   |
+   = help: consider removing this trait bound
+
+error: this trait bound is already specified in trait declaration
+  --> $DIR/trait_duplication_in_bounds.rs:54:15
+   |
+LL |         Self: Default + Clone;
+   |               ^^^^^^^
+   |
+   = help: consider removing this trait bound
+
+error: this trait bound is already specified in trait declaration
+  --> $DIR/trait_duplication_in_bounds.rs:54:25
+   |
+LL |         Self: Default + Clone;
+   |                         ^^^^^
+   |
+   = help: consider removing this trait bound
+
+error: this trait bound is already specified in trait declaration
+  --> $DIR/trait_duplication_in_bounds.rs:57:15
+   |
+LL |         Self: Default;
+   |               ^^^^^^^
+   |
+   = help: consider removing this trait bound
+
+error: aborting due to 7 previous errors
 
diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs
index 6a7037d8f38..9b681a79aae 100644
--- a/tests/ui/transmute.rs
+++ b/tests/ui/transmute.rs
@@ -1,4 +1,4 @@
-#![allow(dead_code)]
+#![allow(dead_code, clippy::borrow_as_ptr)]
 
 extern crate core;
 
diff --git a/tests/ui/transmute_ptr_to_ptr.rs b/tests/ui/transmute_ptr_to_ptr.rs
index 9e213aab68c..f06ffab5d9b 100644
--- a/tests/ui/transmute_ptr_to_ptr.rs
+++ b/tests/ui/transmute_ptr_to_ptr.rs
@@ -1,4 +1,5 @@
 #![warn(clippy::transmute_ptr_to_ptr)]
+#![allow(clippy::borrow_as_ptr)]
 
 // Make sure we can modify lifetimes, which is one of the recommended uses
 // of transmute
diff --git a/tests/ui/transmute_ptr_to_ptr.stderr b/tests/ui/transmute_ptr_to_ptr.stderr
index 4d1b8fcc199..49a8a3347e4 100644
--- a/tests/ui/transmute_ptr_to_ptr.stderr
+++ b/tests/ui/transmute_ptr_to_ptr.stderr
@@ -1,5 +1,5 @@
 error: transmute from a pointer to a pointer
-  --> $DIR/transmute_ptr_to_ptr.rs:29:29
+  --> $DIR/transmute_ptr_to_ptr.rs:30:29
    |
 LL |         let _: *const f32 = std::mem::transmute(ptr);
    |                             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr as *const f32`
@@ -7,31 +7,31 @@ LL |         let _: *const f32 = std::mem::transmute(ptr);
    = note: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings`
 
 error: transmute from a pointer to a pointer
-  --> $DIR/transmute_ptr_to_ptr.rs:30:27
+  --> $DIR/transmute_ptr_to_ptr.rs:31:27
    |
 LL |         let _: *mut f32 = std::mem::transmute(mut_ptr);
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `mut_ptr as *mut f32`
 
 error: transmute from a reference to a reference
-  --> $DIR/transmute_ptr_to_ptr.rs:32:23
+  --> $DIR/transmute_ptr_to_ptr.rs:33:23
    |
 LL |         let _: &f32 = std::mem::transmute(&1u32);
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1u32 as *const u32 as *const f32)`
 
 error: transmute from a reference to a reference
-  --> $DIR/transmute_ptr_to_ptr.rs:33:23
+  --> $DIR/transmute_ptr_to_ptr.rs:34:23
    |
 LL |         let _: &f64 = std::mem::transmute(&1f32);
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1f32 as *const f32 as *const f64)`
 
 error: transmute from a reference to a reference
-  --> $DIR/transmute_ptr_to_ptr.rs:36:27
+  --> $DIR/transmute_ptr_to_ptr.rs:37:27
    |
 LL |         let _: &mut f32 = std::mem::transmute(&mut 1u32);
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(&mut 1u32 as *mut u32 as *mut f32)`
 
 error: transmute from a reference to a reference
-  --> $DIR/transmute_ptr_to_ptr.rs:37:37
+  --> $DIR/transmute_ptr_to_ptr.rs:38:37
    |
 LL |         let _: &GenericParam<f32> = std::mem::transmute(&GenericParam { t: 1u32 });
    |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&GenericParam { t: 1u32 } as *const GenericParam<u32> as *const GenericParam<f32>)`
diff --git a/tests/ui/transmutes_expressible_as_ptr_casts.fixed b/tests/ui/transmutes_expressible_as_ptr_casts.fixed
index b6f1e83181c..9ae0ed0b13f 100644
--- a/tests/ui/transmutes_expressible_as_ptr_casts.fixed
+++ b/tests/ui/transmutes_expressible_as_ptr_casts.fixed
@@ -4,8 +4,7 @@
 // would otherwise be responsible for
 #![warn(clippy::useless_transmute)]
 #![warn(clippy::transmute_ptr_to_ptr)]
-#![allow(unused_unsafe)]
-#![allow(dead_code)]
+#![allow(dead_code, unused_unsafe, clippy::borrow_as_ptr)]
 
 use std::mem::{size_of, transmute};
 
diff --git a/tests/ui/transmutes_expressible_as_ptr_casts.rs b/tests/ui/transmutes_expressible_as_ptr_casts.rs
index 0205d1ece60..985cf9a075d 100644
--- a/tests/ui/transmutes_expressible_as_ptr_casts.rs
+++ b/tests/ui/transmutes_expressible_as_ptr_casts.rs
@@ -4,8 +4,7 @@
 // would otherwise be responsible for
 #![warn(clippy::useless_transmute)]
 #![warn(clippy::transmute_ptr_to_ptr)]
-#![allow(unused_unsafe)]
-#![allow(dead_code)]
+#![allow(dead_code, unused_unsafe, clippy::borrow_as_ptr)]
 
 use std::mem::{size_of, transmute};
 
diff --git a/tests/ui/transmutes_expressible_as_ptr_casts.stderr b/tests/ui/transmutes_expressible_as_ptr_casts.stderr
index 1157b179317..e8496a325d6 100644
--- a/tests/ui/transmutes_expressible_as_ptr_casts.stderr
+++ b/tests/ui/transmutes_expressible_as_ptr_casts.stderr
@@ -1,5 +1,5 @@
 error: transmute from an integer to a pointer
-  --> $DIR/transmutes_expressible_as_ptr_casts.rs:19:39
+  --> $DIR/transmutes_expressible_as_ptr_casts.rs:18:39
    |
 LL |     let _ptr_i32_transmute = unsafe { transmute::<usize, *const i32>(usize::MAX) };
    |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `usize::MAX as *const i32`
@@ -7,7 +7,7 @@ LL |     let _ptr_i32_transmute = unsafe { transmute::<usize, *const i32>(usize:
    = note: `-D clippy::useless-transmute` implied by `-D warnings`
 
 error: transmute from a pointer to a pointer
-  --> $DIR/transmutes_expressible_as_ptr_casts.rs:23:38
+  --> $DIR/transmutes_expressible_as_ptr_casts.rs:22:38
    |
 LL |     let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr_i32) };
    |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as *const i8`
@@ -15,13 +15,13 @@ LL |     let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr
    = note: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings`
 
 error: transmute from a pointer to a pointer
-  --> $DIR/transmutes_expressible_as_ptr_casts.rs:29:46
+  --> $DIR/transmutes_expressible_as_ptr_casts.rs:28:46
    |
 LL |     let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u16]>(slice_ptr) };
    |                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slice_ptr as *const [u16]`
 
 error: transmute from `*const i32` to `usize` which could be expressed as a pointer cast instead
-  --> $DIR/transmutes_expressible_as_ptr_casts.rs:35:50
+  --> $DIR/transmutes_expressible_as_ptr_casts.rs:34:50
    |
 LL |     let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, usize>(ptr_i32) };
    |                                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as usize`
@@ -29,25 +29,25 @@ LL |     let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, us
    = note: `-D clippy::transmutes-expressible-as-ptr-casts` implied by `-D warnings`
 
 error: transmute from a reference to a pointer
-  --> $DIR/transmutes_expressible_as_ptr_casts.rs:41:41
+  --> $DIR/transmutes_expressible_as_ptr_casts.rs:40:41
    |
 LL |     let _array_ptr_transmute = unsafe { transmute::<&[i32; 4], *const [i32; 4]>(array_ref) };
    |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `array_ref as *const [i32; 4]`
 
 error: transmute from `fn(usize) -> u8 {main::foo}` to `*const usize` which could be expressed as a pointer cast instead
-  --> $DIR/transmutes_expressible_as_ptr_casts.rs:49:41
+  --> $DIR/transmutes_expressible_as_ptr_casts.rs:48:41
    |
 LL |     let _usize_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, *const usize>(foo) };
    |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as *const usize`
 
 error: transmute from `fn(usize) -> u8 {main::foo}` to `usize` which could be expressed as a pointer cast instead
-  --> $DIR/transmutes_expressible_as_ptr_casts.rs:53:49
+  --> $DIR/transmutes_expressible_as_ptr_casts.rs:52:49
    |
 LL |     let _usize_from_fn_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, usize>(foo) };
    |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as usize`
 
 error: transmute from a reference to a pointer
-  --> $DIR/transmutes_expressible_as_ptr_casts.rs:65:14
+  --> $DIR/transmutes_expressible_as_ptr_casts.rs:64:14
    |
 LL |     unsafe { transmute::<&[i32; 1], *const u8>(in_param) }
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `in_param as *const [i32; 1] as *const u8`
diff --git a/tests/ui/type_repetition_in_bounds.rs b/tests/ui/type_repetition_in_bounds.rs
index 766190f2099..fc740ee11d6 100644
--- a/tests/ui/type_repetition_in_bounds.rs
+++ b/tests/ui/type_repetition_in_bounds.rs
@@ -69,4 +69,14 @@ mod issue4326 {
     }
 }
 
+// Issue #7360
+struct Foo<T, U>
+where
+    T: Clone,
+    U: Clone,
+{
+    t: T,
+    u: U,
+}
+
 fn main() {}
diff --git a/tests/ui/unit_cmp.stderr b/tests/ui/unit_cmp.stderr
index 2b5a7b348b9..824506a4257 100644
--- a/tests/ui/unit_cmp.stderr
+++ b/tests/ui/unit_cmp.stderr
@@ -33,8 +33,6 @@ LL | |         },
 LL | |         }
 LL | |     );
    | |_____^
-   |
-   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: `debug_assert_eq` of unit values detected. This will always succeed
   --> $DIR/unit_cmp.rs:32:5
@@ -47,8 +45,6 @@ LL | |         },
 LL | |         }
 LL | |     );
    | |_____^
-   |
-   = note: this error originates in the macro `$crate::assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: `assert_ne` of unit values detected. This will always fail
   --> $DIR/unit_cmp.rs:41:5
@@ -61,8 +57,6 @@ LL | |         },
 LL | |         }
 LL | |     );
    | |_____^
-   |
-   = note: this error originates in the macro `assert_ne` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: `debug_assert_ne` of unit values detected. This will always fail
   --> $DIR/unit_cmp.rs:49:5
@@ -75,8 +69,6 @@ LL | |         },
 LL | |         }
 LL | |     );
    | |_____^
-   |
-   = note: this error originates in the macro `$crate::assert_ne` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 6 previous errors
 
diff --git a/tests/ui/unnecessary_cast_fixable.fixed b/tests/ui/unnecessary_cast_fixable.fixed
index bda0f2c47cd..3332f49c80c 100644
--- a/tests/ui/unnecessary_cast_fixable.fixed
+++ b/tests/ui/unnecessary_cast_fixable.fixed
@@ -1,7 +1,12 @@
 // run-rustfix
 
 #![warn(clippy::unnecessary_cast)]
-#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::nonstandard_macro_braces)]
+#![allow(
+    clippy::no_effect,
+    clippy::unnecessary_operation,
+    clippy::nonstandard_macro_braces,
+    clippy::borrow_as_ptr
+)]
 
 fn main() {
     // casting integer literal to float is unnecessary
diff --git a/tests/ui/unnecessary_cast_fixable.rs b/tests/ui/unnecessary_cast_fixable.rs
index f7a4f2a5988..ec01e938779 100644
--- a/tests/ui/unnecessary_cast_fixable.rs
+++ b/tests/ui/unnecessary_cast_fixable.rs
@@ -1,7 +1,12 @@
 // run-rustfix
 
 #![warn(clippy::unnecessary_cast)]
-#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::nonstandard_macro_braces)]
+#![allow(
+    clippy::no_effect,
+    clippy::unnecessary_operation,
+    clippy::nonstandard_macro_braces,
+    clippy::borrow_as_ptr
+)]
 
 fn main() {
     // casting integer literal to float is unnecessary
diff --git a/tests/ui/unnecessary_cast_fixable.stderr b/tests/ui/unnecessary_cast_fixable.stderr
index 3695a8f819c..a281143281b 100644
--- a/tests/ui/unnecessary_cast_fixable.stderr
+++ b/tests/ui/unnecessary_cast_fixable.stderr
@@ -1,5 +1,5 @@
 error: casting integer literal to `f32` is unnecessary
-  --> $DIR/unnecessary_cast_fixable.rs:8:5
+  --> $DIR/unnecessary_cast_fixable.rs:13:5
    |
 LL |     100 as f32;
    |     ^^^^^^^^^^ help: try: `100_f32`
@@ -7,97 +7,97 @@ LL |     100 as f32;
    = note: `-D clippy::unnecessary-cast` implied by `-D warnings`
 
 error: casting integer literal to `f64` is unnecessary
-  --> $DIR/unnecessary_cast_fixable.rs:9:5
+  --> $DIR/unnecessary_cast_fixable.rs:14:5
    |
 LL |     100 as f64;
    |     ^^^^^^^^^^ help: try: `100_f64`
 
 error: casting integer literal to `f64` is unnecessary
-  --> $DIR/unnecessary_cast_fixable.rs:10:5
+  --> $DIR/unnecessary_cast_fixable.rs:15:5
    |
 LL |     100_i32 as f64;
    |     ^^^^^^^^^^^^^^ help: try: `100_f64`
 
 error: casting integer literal to `f32` is unnecessary
-  --> $DIR/unnecessary_cast_fixable.rs:11:13
+  --> $DIR/unnecessary_cast_fixable.rs:16:13
    |
 LL |     let _ = -100 as f32;
    |             ^^^^^^^^^^^ help: try: `-100_f32`
 
 error: casting integer literal to `f64` is unnecessary
-  --> $DIR/unnecessary_cast_fixable.rs:12:13
+  --> $DIR/unnecessary_cast_fixable.rs:17:13
    |
 LL |     let _ = -100 as f64;
    |             ^^^^^^^^^^^ help: try: `-100_f64`
 
 error: casting integer literal to `f64` is unnecessary
-  --> $DIR/unnecessary_cast_fixable.rs:13:13
+  --> $DIR/unnecessary_cast_fixable.rs:18:13
    |
 LL |     let _ = -100_i32 as f64;
    |             ^^^^^^^^^^^^^^^ help: try: `-100_f64`
 
 error: casting float literal to `f32` is unnecessary
-  --> $DIR/unnecessary_cast_fixable.rs:14:5
+  --> $DIR/unnecessary_cast_fixable.rs:19:5
    |
 LL |     100. as f32;
    |     ^^^^^^^^^^^ help: try: `100_f32`
 
 error: casting float literal to `f64` is unnecessary
-  --> $DIR/unnecessary_cast_fixable.rs:15:5
+  --> $DIR/unnecessary_cast_fixable.rs:20:5
    |
 LL |     100. as f64;
    |     ^^^^^^^^^^^ help: try: `100_f64`
 
 error: casting integer literal to `u32` is unnecessary
-  --> $DIR/unnecessary_cast_fixable.rs:27:5
+  --> $DIR/unnecessary_cast_fixable.rs:32:5
    |
 LL |     1 as u32;
    |     ^^^^^^^^ help: try: `1_u32`
 
 error: casting integer literal to `i32` is unnecessary
-  --> $DIR/unnecessary_cast_fixable.rs:28:5
+  --> $DIR/unnecessary_cast_fixable.rs:33:5
    |
 LL |     0x10 as i32;
    |     ^^^^^^^^^^^ help: try: `0x10_i32`
 
 error: casting integer literal to `usize` is unnecessary
-  --> $DIR/unnecessary_cast_fixable.rs:29:5
+  --> $DIR/unnecessary_cast_fixable.rs:34:5
    |
 LL |     0b10 as usize;
    |     ^^^^^^^^^^^^^ help: try: `0b10_usize`
 
 error: casting integer literal to `u16` is unnecessary
-  --> $DIR/unnecessary_cast_fixable.rs:30:5
+  --> $DIR/unnecessary_cast_fixable.rs:35:5
    |
 LL |     0o73 as u16;
    |     ^^^^^^^^^^^ help: try: `0o73_u16`
 
 error: casting integer literal to `u32` is unnecessary
-  --> $DIR/unnecessary_cast_fixable.rs:31:5
+  --> $DIR/unnecessary_cast_fixable.rs:36:5
    |
 LL |     1_000_000_000 as u32;
    |     ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32`
 
 error: casting float literal to `f64` is unnecessary
-  --> $DIR/unnecessary_cast_fixable.rs:33:5
+  --> $DIR/unnecessary_cast_fixable.rs:38:5
    |
 LL |     1.0 as f64;
    |     ^^^^^^^^^^ help: try: `1.0_f64`
 
 error: casting float literal to `f32` is unnecessary
-  --> $DIR/unnecessary_cast_fixable.rs:34:5
+  --> $DIR/unnecessary_cast_fixable.rs:39:5
    |
 LL |     0.5 as f32;
    |     ^^^^^^^^^^ help: try: `0.5_f32`
 
 error: casting integer literal to `i32` is unnecessary
-  --> $DIR/unnecessary_cast_fixable.rs:38:13
+  --> $DIR/unnecessary_cast_fixable.rs:43:13
    |
 LL |     let _ = -1 as i32;
    |             ^^^^^^^^^ help: try: `-1_i32`
 
 error: casting float literal to `f32` is unnecessary
-  --> $DIR/unnecessary_cast_fixable.rs:39:13
+  --> $DIR/unnecessary_cast_fixable.rs:44:13
    |
 LL |     let _ = -1.0 as f32;
    |             ^^^^^^^^^^^ help: try: `-1.0_f32`
diff --git a/tests/ui/unused_io_amount.rs b/tests/ui/unused_io_amount.rs
index 8b141e25942..4b059558173 100644
--- a/tests/ui/unused_io_amount.rs
+++ b/tests/ui/unused_io_amount.rs
@@ -1,6 +1,8 @@
 #![allow(dead_code)]
 #![warn(clippy::unused_io_amount)]
 
+extern crate futures;
+use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
 use std::io::{self, Read};
 
 fn question_mark<T: io::Read + io::Write>(s: &mut T) -> io::Result<()> {
@@ -61,4 +63,55 @@ fn combine_or(file: &str) -> Result<(), Error> {
     Ok(())
 }
 
+async fn bad_async_write<W: AsyncWrite + Unpin>(w: &mut W) {
+    w.write(b"hello world").await.unwrap();
+}
+
+async fn bad_async_read<R: AsyncRead + Unpin>(r: &mut R) {
+    let mut buf = [0u8; 0];
+    r.read(&mut buf[..]).await.unwrap();
+}
+
+async fn io_not_ignored_async_write<W: AsyncWrite + Unpin>(mut w: W) {
+    // Here we're forgetting to await the future, so we should get a
+    // warning about _that_ (or we would, if it were enabled), but we
+    // won't get one about ignoring the return value.
+    w.write(b"hello world");
+}
+
+fn bad_async_write_closure<W: AsyncWrite + Unpin + 'static>(w: W) -> impl futures::Future<Output = io::Result<()>> {
+    let mut w = w;
+    async move {
+        w.write(b"hello world").await?;
+        Ok(())
+    }
+}
+
+async fn async_read_nested_or<R: AsyncRead + Unpin>(r: &mut R, do_it: bool) -> Result<[u8; 1], Error> {
+    let mut buf = [0u8; 1];
+    if do_it {
+        r.read(&mut buf[..]).await.or(Err(Error::Kind))?;
+    }
+    Ok(buf)
+}
+
+use tokio::io::{AsyncRead as TokioAsyncRead, AsyncReadExt as _, AsyncWrite as TokioAsyncWrite, AsyncWriteExt as _};
+
+async fn bad_async_write_tokio<W: TokioAsyncWrite + Unpin>(w: &mut W) {
+    w.write(b"hello world").await.unwrap();
+}
+
+async fn bad_async_read_tokio<R: TokioAsyncRead + Unpin>(r: &mut R) {
+    let mut buf = [0u8; 0];
+    r.read(&mut buf[..]).await.unwrap();
+}
+
+async fn undetected_bad_async_write<W: AsyncWrite + Unpin>(w: &mut W) {
+    // It would be good to detect this case some day, but the current lint
+    // doesn't handle it. (The documentation says that this lint "detects
+    // only common patterns".)
+    let future = w.write(b"Hello world");
+    future.await.unwrap();
+}
+
 fn main() {}
diff --git a/tests/ui/unused_io_amount.stderr b/tests/ui/unused_io_amount.stderr
index d8dfc0e5a79..e5bdd993aa1 100644
--- a/tests/ui/unused_io_amount.stderr
+++ b/tests/ui/unused_io_amount.stderr
@@ -1,61 +1,74 @@
-error: written amount is not handled. Use `Write::write_all` instead
-  --> $DIR/unused_io_amount.rs:7:5
+error: written amount is not handled
+  --> $DIR/unused_io_amount.rs:9:5
    |
 LL |     s.write(b"test")?;
    |     ^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::unused-io-amount` implied by `-D warnings`
+   = help: use `Write::write_all` instead, or handle partial writes
 
-error: read amount is not handled. Use `Read::read_exact` instead
-  --> $DIR/unused_io_amount.rs:9:5
+error: read amount is not handled
+  --> $DIR/unused_io_amount.rs:11:5
    |
 LL |     s.read(&mut buf)?;
    |     ^^^^^^^^^^^^^^^^^
+   |
+   = help: use `Read::read_exact` instead, or handle partial reads
 
-error: written amount is not handled. Use `Write::write_all` instead
-  --> $DIR/unused_io_amount.rs:14:5
+error: written amount is not handled
+  --> $DIR/unused_io_amount.rs:16:5
    |
 LL |     s.write(b"test").unwrap();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: use `Write::write_all` instead, or handle partial writes
 
-error: read amount is not handled. Use `Read::read_exact` instead
-  --> $DIR/unused_io_amount.rs:16:5
+error: read amount is not handled
+  --> $DIR/unused_io_amount.rs:18:5
    |
 LL |     s.read(&mut buf).unwrap();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: use `Read::read_exact` instead, or handle partial reads
 
 error: read amount is not handled
-  --> $DIR/unused_io_amount.rs:20:5
+  --> $DIR/unused_io_amount.rs:22:5
    |
 LL |     s.read_vectored(&mut [io::IoSliceMut::new(&mut [])])?;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: written amount is not handled
-  --> $DIR/unused_io_amount.rs:21:5
+  --> $DIR/unused_io_amount.rs:23:5
    |
 LL |     s.write_vectored(&[io::IoSlice::new(&[])])?;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: read amount is not handled. Use `Read::read_exact` instead
-  --> $DIR/unused_io_amount.rs:28:5
+error: read amount is not handled
+  --> $DIR/unused_io_amount.rs:30:5
    |
 LL |     reader.read(&mut result).ok()?;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: use `Read::read_exact` instead, or handle partial reads
 
-error: read amount is not handled. Use `Read::read_exact` instead
-  --> $DIR/unused_io_amount.rs:37:5
+error: read amount is not handled
+  --> $DIR/unused_io_amount.rs:39:5
    |
 LL |     reader.read(&mut result).or_else(|err| Err(err))?;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: use `Read::read_exact` instead, or handle partial reads
 
-error: read amount is not handled. Use `Read::read_exact` instead
-  --> $DIR/unused_io_amount.rs:49:5
+error: read amount is not handled
+  --> $DIR/unused_io_amount.rs:51:5
    |
 LL |     reader.read(&mut result).or(Err(Error::Kind))?;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: use `Read::read_exact` instead, or handle partial reads
 
-error: read amount is not handled. Use `Read::read_exact` instead
-  --> $DIR/unused_io_amount.rs:56:5
+error: read amount is not handled
+  --> $DIR/unused_io_amount.rs:58:5
    |
 LL | /     reader
 LL | |         .read(&mut result)
@@ -63,6 +76,56 @@ LL | |         .or(Err(Error::Kind))
 LL | |         .or(Err(Error::Kind))
 LL | |         .expect("error");
    | |________________________^
+   |
+   = help: use `Read::read_exact` instead, or handle partial reads
 
-error: aborting due to 10 previous errors
+error: written amount is not handled
+  --> $DIR/unused_io_amount.rs:67:5
+   |
+LL |     w.write(b"hello world").await.unwrap();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: use `AsyncWriteExt::write_all` instead, or handle partial writes
+
+error: read amount is not handled
+  --> $DIR/unused_io_amount.rs:72:5
+   |
+LL |     r.read(&mut buf[..]).await.unwrap();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: use `AsyncReadExt::read_exact` instead, or handle partial reads
+
+error: written amount is not handled
+  --> $DIR/unused_io_amount.rs:85:9
+   |
+LL |         w.write(b"hello world").await?;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: use `AsyncWriteExt::write_all` instead, or handle partial writes
+
+error: read amount is not handled
+  --> $DIR/unused_io_amount.rs:93:9
+   |
+LL |         r.read(&mut buf[..]).await.or(Err(Error::Kind))?;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: use `AsyncReadExt::read_exact` instead, or handle partial reads
+
+error: written amount is not handled
+  --> $DIR/unused_io_amount.rs:101:5
+   |
+LL |     w.write(b"hello world").await.unwrap();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: use `AsyncWriteExt::write_all` instead, or handle partial writes
+
+error: read amount is not handled
+  --> $DIR/unused_io_amount.rs:106:5
+   |
+LL |     r.read(&mut buf[..]).await.unwrap();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: use `AsyncReadExt::read_exact` instead, or handle partial reads
+
+error: aborting due to 16 previous errors
 
diff --git a/tests/ui/vtable_address_comparisons.rs b/tests/ui/vtable_address_comparisons.rs
index c91d96ee18a..a9a4a0f5a6b 100644
--- a/tests/ui/vtable_address_comparisons.rs
+++ b/tests/ui/vtable_address_comparisons.rs
@@ -4,6 +4,8 @@ use std::rc::Rc;
 use std::sync::Arc;
 
 #[warn(clippy::vtable_address_comparisons)]
+#[allow(clippy::borrow_as_ptr)]
+
 fn main() {
     let a: *const dyn Debug = &1 as &dyn Debug;
     let b: *const dyn Debug = &1 as &dyn Debug;
diff --git a/tests/ui/vtable_address_comparisons.stderr b/tests/ui/vtable_address_comparisons.stderr
index 76bd57217d7..2f1be61e5df 100644
--- a/tests/ui/vtable_address_comparisons.stderr
+++ b/tests/ui/vtable_address_comparisons.stderr
@@ -1,5 +1,5 @@
 error: comparing trait object pointers compares a non-unique vtable address
-  --> $DIR/vtable_address_comparisons.rs:12:13
+  --> $DIR/vtable_address_comparisons.rs:14:13
    |
 LL |     let _ = a == b;
    |             ^^^^^^
@@ -8,7 +8,7 @@ LL |     let _ = a == b;
    = help: consider extracting and comparing data pointers only
 
 error: comparing trait object pointers compares a non-unique vtable address
-  --> $DIR/vtable_address_comparisons.rs:13:13
+  --> $DIR/vtable_address_comparisons.rs:15:13
    |
 LL |     let _ = a != b;
    |             ^^^^^^
@@ -16,7 +16,7 @@ LL |     let _ = a != b;
    = help: consider extracting and comparing data pointers only
 
 error: comparing trait object pointers compares a non-unique vtable address
-  --> $DIR/vtable_address_comparisons.rs:14:13
+  --> $DIR/vtable_address_comparisons.rs:16:13
    |
 LL |     let _ = a < b;
    |             ^^^^^
@@ -24,7 +24,7 @@ LL |     let _ = a < b;
    = help: consider extracting and comparing data pointers only
 
 error: comparing trait object pointers compares a non-unique vtable address
-  --> $DIR/vtable_address_comparisons.rs:15:13
+  --> $DIR/vtable_address_comparisons.rs:17:13
    |
 LL |     let _ = a <= b;
    |             ^^^^^^
@@ -32,7 +32,7 @@ LL |     let _ = a <= b;
    = help: consider extracting and comparing data pointers only
 
 error: comparing trait object pointers compares a non-unique vtable address
-  --> $DIR/vtable_address_comparisons.rs:16:13
+  --> $DIR/vtable_address_comparisons.rs:18:13
    |
 LL |     let _ = a > b;
    |             ^^^^^
@@ -40,7 +40,7 @@ LL |     let _ = a > b;
    = help: consider extracting and comparing data pointers only
 
 error: comparing trait object pointers compares a non-unique vtable address
-  --> $DIR/vtable_address_comparisons.rs:17:13
+  --> $DIR/vtable_address_comparisons.rs:19:13
    |
 LL |     let _ = a >= b;
    |             ^^^^^^
@@ -48,7 +48,7 @@ LL |     let _ = a >= b;
    = help: consider extracting and comparing data pointers only
 
 error: comparing trait object pointers compares a non-unique vtable address
-  --> $DIR/vtable_address_comparisons.rs:18:5
+  --> $DIR/vtable_address_comparisons.rs:20:5
    |
 LL |     ptr::eq(a, b);
    |     ^^^^^^^^^^^^^
@@ -56,7 +56,7 @@ LL |     ptr::eq(a, b);
    = help: consider extracting and comparing data pointers only
 
 error: comparing trait object pointers compares a non-unique vtable address
-  --> $DIR/vtable_address_comparisons.rs:22:5
+  --> $DIR/vtable_address_comparisons.rs:24:5
    |
 LL |     ptr::eq(a, b);
    |     ^^^^^^^^^^^^^
@@ -64,7 +64,7 @@ LL |     ptr::eq(a, b);
    = help: consider extracting and comparing data pointers only
 
 error: comparing trait object pointers compares a non-unique vtable address
-  --> $DIR/vtable_address_comparisons.rs:25:5
+  --> $DIR/vtable_address_comparisons.rs:27:5
    |
 LL |     Rc::ptr_eq(&a, &a);
    |     ^^^^^^^^^^^^^^^^^^
@@ -72,7 +72,7 @@ LL |     Rc::ptr_eq(&a, &a);
    = help: consider extracting and comparing data pointers only
 
 error: comparing trait object pointers compares a non-unique vtable address
-  --> $DIR/vtable_address_comparisons.rs:28:5
+  --> $DIR/vtable_address_comparisons.rs:30:5
    |
 LL |     Arc::ptr_eq(&a, &a);
    |     ^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/while_let_on_iterator.fixed b/tests/ui/while_let_on_iterator.fixed
index 1e74ad2de65..cb8892a3f00 100644
--- a/tests/ui/while_let_on_iterator.fixed
+++ b/tests/ui/while_let_on_iterator.fixed
@@ -372,6 +372,36 @@ fn exact_match_with_single_field() {
     }
 }
 
+fn custom_deref() {
+    struct S1<T> {
+        x: T,
+    }
+    struct S2<T>(S1<T>);
+    impl<T> core::ops::Deref for S2<T> {
+        type Target = S1<T>;
+        fn deref(&self) -> &Self::Target {
+            &self.0
+        }
+    }
+    impl<T> core::ops::DerefMut for S2<T> {
+        fn deref_mut(&mut self) -> &mut Self::Target {
+            &mut self.0
+        }
+    }
+
+    let mut s = S2(S1 { x: 0..10 });
+    for x in s.x.by_ref() {
+        println!("{}", x);
+    }
+}
+
+fn issue_8113() {
+    let mut x = [0..10];
+    for x in x[0].by_ref() {
+        println!("{}", x);
+    }
+}
+
 fn main() {
     let mut it = 0..20;
     for _ in it {
diff --git a/tests/ui/while_let_on_iterator.rs b/tests/ui/while_let_on_iterator.rs
index 69cb636cee8..d9157184495 100644
--- a/tests/ui/while_let_on_iterator.rs
+++ b/tests/ui/while_let_on_iterator.rs
@@ -372,6 +372,36 @@ fn exact_match_with_single_field() {
     }
 }
 
+fn custom_deref() {
+    struct S1<T> {
+        x: T,
+    }
+    struct S2<T>(S1<T>);
+    impl<T> core::ops::Deref for S2<T> {
+        type Target = S1<T>;
+        fn deref(&self) -> &Self::Target {
+            &self.0
+        }
+    }
+    impl<T> core::ops::DerefMut for S2<T> {
+        fn deref_mut(&mut self) -> &mut Self::Target {
+            &mut self.0
+        }
+    }
+
+    let mut s = S2(S1 { x: 0..10 });
+    while let Some(x) = s.x.next() {
+        println!("{}", x);
+    }
+}
+
+fn issue_8113() {
+    let mut x = [0..10];
+    while let Some(x) = x[0].next() {
+        println!("{}", x);
+    }
+}
+
 fn main() {
     let mut it = 0..20;
     while let Some(..) = it.next() {
diff --git a/tests/ui/while_let_on_iterator.stderr b/tests/ui/while_let_on_iterator.stderr
index 1a11ba26eef..fb2b0f2467c 100644
--- a/tests/ui/while_let_on_iterator.stderr
+++ b/tests/ui/while_let_on_iterator.stderr
@@ -123,10 +123,22 @@ LL |     while let Some(x) = it.0.next() {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.0.by_ref()`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:377:5
+  --> $DIR/while_let_on_iterator.rs:393:5
+   |
+LL |     while let Some(x) = s.x.next() {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in s.x.by_ref()`
+
+error: this loop could be written as a `for` loop
+  --> $DIR/while_let_on_iterator.rs:400:5
+   |
+LL |     while let Some(x) = x[0].next() {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in x[0].by_ref()`
+
+error: this loop could be written as a `for` loop
+  --> $DIR/while_let_on_iterator.rs:407:5
    |
 LL |     while let Some(..) = it.next() {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it`
 
-error: aborting due to 21 previous errors
+error: aborting due to 23 previous errors
 
diff --git a/tests/ui/wrong_self_convention.rs b/tests/ui/wrong_self_convention.rs
index 1b9da8a55e5..f8fee4b3ab2 100644
--- a/tests/ui/wrong_self_convention.rs
+++ b/tests/ui/wrong_self_convention.rs
@@ -188,3 +188,24 @@ mod issue6727 {
         }
     }
 }
+
+pub mod issue8142 {
+    struct S;
+
+    impl S {
+        // Should lint: is_ methods should only take &self, or no self at all.
+        fn is_still_buggy(&mut self) -> bool {
+            false
+        }
+
+        // Should not lint: "no self at all" is allowed.
+        fn is_forty_two(x: u32) -> bool {
+            x == 42
+        }
+
+        // Should not lint: &self is allowed.
+        fn is_test_code(&self) -> bool {
+            true
+        }
+    }
+}
diff --git a/tests/ui/wrong_self_convention.stderr b/tests/ui/wrong_self_convention.stderr
index 590ee6d9c52..5493a99572e 100644
--- a/tests/ui/wrong_self_convention.stderr
+++ b/tests/ui/wrong_self_convention.stderr
@@ -191,5 +191,13 @@ LL |         fn to_u64(self) -> u64 {
    |
    = help: consider choosing a less ambiguous name
 
-error: aborting due to 24 previous errors
+error: methods called `is_*` usually take `self` by reference or no `self`
+  --> $DIR/wrong_self_convention.rs:197:27
+   |
+LL |         fn is_still_buggy(&mut self) -> bool {
+   |                           ^^^^^^^^^
+   |
+   = help: consider choosing a less ambiguous name
+
+error: aborting due to 25 previous errors
 
diff --git a/tests/ui/zero_offset.rs b/tests/ui/zero_offset.rs
index 6c190a4c86c..fd9ac1fa766 100644
--- a/tests/ui/zero_offset.rs
+++ b/tests/ui/zero_offset.rs
@@ -1,3 +1,4 @@
+#[allow(clippy::borrow_as_ptr)]
 fn main() {
     unsafe {
         let m = &mut () as *mut ();
diff --git a/tests/ui/zero_offset.stderr b/tests/ui/zero_offset.stderr
index b12c8e9a73c..481a446571a 100644
--- a/tests/ui/zero_offset.stderr
+++ b/tests/ui/zero_offset.stderr
@@ -1,5 +1,5 @@
 error: offset calculation on zero-sized value
-  --> $DIR/zero_offset.rs:4:9
+  --> $DIR/zero_offset.rs:5:9
    |
 LL |         m.offset(0);
    |         ^^^^^^^^^^^
@@ -7,43 +7,43 @@ LL |         m.offset(0);
    = note: `#[deny(clippy::zst_offset)]` on by default
 
 error: offset calculation on zero-sized value
-  --> $DIR/zero_offset.rs:5:9
+  --> $DIR/zero_offset.rs:6:9
    |
 LL |         m.wrapping_add(0);
    |         ^^^^^^^^^^^^^^^^^
 
 error: offset calculation on zero-sized value
-  --> $DIR/zero_offset.rs:6:9
+  --> $DIR/zero_offset.rs:7:9
    |
 LL |         m.sub(0);
    |         ^^^^^^^^
 
 error: offset calculation on zero-sized value
-  --> $DIR/zero_offset.rs:7:9
+  --> $DIR/zero_offset.rs:8:9
    |
 LL |         m.wrapping_sub(0);
    |         ^^^^^^^^^^^^^^^^^
 
 error: offset calculation on zero-sized value
-  --> $DIR/zero_offset.rs:10:9
+  --> $DIR/zero_offset.rs:11:9
    |
 LL |         c.offset(0);
    |         ^^^^^^^^^^^
 
 error: offset calculation on zero-sized value
-  --> $DIR/zero_offset.rs:11:9
+  --> $DIR/zero_offset.rs:12:9
    |
 LL |         c.wrapping_add(0);
    |         ^^^^^^^^^^^^^^^^^
 
 error: offset calculation on zero-sized value
-  --> $DIR/zero_offset.rs:12:9
+  --> $DIR/zero_offset.rs:13:9
    |
 LL |         c.sub(0);
    |         ^^^^^^^^
 
 error: offset calculation on zero-sized value
-  --> $DIR/zero_offset.rs:13:9
+  --> $DIR/zero_offset.rs:14:9
    |
 LL |         c.wrapping_sub(0);
    |         ^^^^^^^^^^^^^^^^^
diff --git a/tests/workspace.rs b/tests/workspace.rs
new file mode 100644
index 00000000000..677b4a4d569
--- /dev/null
+++ b/tests/workspace.rs
@@ -0,0 +1,107 @@
+#![feature(once_cell)]
+
+use std::path::PathBuf;
+use std::process::Command;
+use test_utils::{CARGO_CLIPPY_PATH, IS_RUSTC_TEST_SUITE};
+
+mod test_utils;
+
+#[test]
+fn test_no_deps_ignores_path_deps_in_workspaces() {
+    if IS_RUSTC_TEST_SUITE {
+        return;
+    }
+    let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
+    let target_dir = root.join("target").join("workspace_test");
+    let cwd = root.join("tests/workspace_test");
+
+    // Make sure we start with a clean state
+    Command::new("cargo")
+        .current_dir(&cwd)
+        .env("CARGO_TARGET_DIR", &target_dir)
+        .arg("clean")
+        .args(&["-p", "subcrate"])
+        .args(&["-p", "path_dep"])
+        .output()
+        .unwrap();
+
+    // `path_dep` is a path dependency of `subcrate` that would trigger a denied lint.
+    // Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`.
+    let output = Command::new(&*CARGO_CLIPPY_PATH)
+        .current_dir(&cwd)
+        .env("CARGO_INCREMENTAL", "0")
+        .env("CARGO_TARGET_DIR", &target_dir)
+        .arg("clippy")
+        .args(&["-p", "subcrate"])
+        .arg("--no-deps")
+        .arg("--")
+        .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
+        .args(&["--cfg", r#"feature="primary_package_test""#])
+        .output()
+        .unwrap();
+    println!("status: {}", output.status);
+    println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
+    println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
+
+    assert!(output.status.success());
+
+    let lint_path_dep = || {
+        // Test that without the `--no-deps` argument, `path_dep` is linted.
+        let output = Command::new(&*CARGO_CLIPPY_PATH)
+            .current_dir(&cwd)
+            .env("CARGO_INCREMENTAL", "0")
+            .env("CARGO_TARGET_DIR", &target_dir)
+            .arg("clippy")
+            .args(&["-p", "subcrate"])
+            .arg("--")
+            .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
+            .args(&["--cfg", r#"feature="primary_package_test""#])
+            .output()
+            .unwrap();
+        println!("status: {}", output.status);
+        println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
+        println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
+
+        assert!(!output.status.success());
+        assert!(
+            String::from_utf8(output.stderr)
+                .unwrap()
+                .contains("error: empty `loop {}` wastes CPU cycles")
+        );
+    };
+
+    // Make sure Cargo is aware of the removal of `--no-deps`.
+    lint_path_dep();
+
+    let successful_build = || {
+        let output = Command::new(&*CARGO_CLIPPY_PATH)
+            .current_dir(&cwd)
+            .env("CARGO_INCREMENTAL", "0")
+            .env("CARGO_TARGET_DIR", &target_dir)
+            .arg("clippy")
+            .args(&["-p", "subcrate"])
+            .arg("--")
+            .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
+            .output()
+            .unwrap();
+        println!("status: {}", output.status);
+        println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
+        println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
+
+        assert!(output.status.success());
+
+        output
+    };
+
+    // Trigger a sucessful build, so Cargo would like to cache the build result.
+    successful_build();
+
+    // Make sure there's no spurious rebuild when nothing changes.
+    let stderr = String::from_utf8(successful_build().stderr).unwrap();
+    assert!(!stderr.contains("Compiling"));
+    assert!(!stderr.contains("Checking"));
+    assert!(stderr.contains("Finished"));
+
+    // Make sure Cargo is aware of the new `--cfg` flag.
+    lint_path_dep();
+}
diff --git a/clippy_workspace_tests/Cargo.toml b/tests/workspace_test/Cargo.toml
similarity index 71%
rename from clippy_workspace_tests/Cargo.toml
rename to tests/workspace_test/Cargo.toml
index 7a235b215d3..bf5b4ca5288 100644
--- a/clippy_workspace_tests/Cargo.toml
+++ b/tests/workspace_test/Cargo.toml
@@ -1,5 +1,5 @@
 [package]
-name = "clippy_workspace_tests"
+name = "workspace_test"
 version = "0.1.0"
 edition = "2018"
 
diff --git a/clippy_workspace_tests/build.rs b/tests/workspace_test/build.rs
similarity index 100%
rename from clippy_workspace_tests/build.rs
rename to tests/workspace_test/build.rs
diff --git a/clippy_workspace_tests/path_dep/Cargo.toml b/tests/workspace_test/path_dep/Cargo.toml
similarity index 100%
rename from clippy_workspace_tests/path_dep/Cargo.toml
rename to tests/workspace_test/path_dep/Cargo.toml
diff --git a/clippy_workspace_tests/path_dep/src/lib.rs b/tests/workspace_test/path_dep/src/lib.rs
similarity index 100%
rename from clippy_workspace_tests/path_dep/src/lib.rs
rename to tests/workspace_test/path_dep/src/lib.rs
diff --git a/clippy_workspace_tests/src/main.rs b/tests/workspace_test/src/main.rs
similarity index 100%
rename from clippy_workspace_tests/src/main.rs
rename to tests/workspace_test/src/main.rs
diff --git a/clippy_workspace_tests/subcrate/Cargo.toml b/tests/workspace_test/subcrate/Cargo.toml
similarity index 100%
rename from clippy_workspace_tests/subcrate/Cargo.toml
rename to tests/workspace_test/subcrate/Cargo.toml
diff --git a/clippy_workspace_tests/subcrate/src/lib.rs b/tests/workspace_test/subcrate/src/lib.rs
similarity index 100%
rename from clippy_workspace_tests/subcrate/src/lib.rs
rename to tests/workspace_test/subcrate/src/lib.rs