about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock8
-rw-r--r--src/tools/clippy/CHANGELOG.md153
-rw-r--r--src/tools/clippy/Cargo.toml2
-rw-r--r--src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md14
-rw-r--r--src/tools/clippy/clippy_lints/Cargo.toml2
-rw-r--r--src/tools/clippy/clippy_lints/src/almost_complete_range.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/collection_is_never_read.rs122
-rw-r--r--src/tools/clippy/clippy_lints/src/declared_lints.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/dereference.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/derivable_impls.rs21
-rw-r--r--src/tools/clippy/clippy_lints/src/exit.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/fn_null_check.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/format.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/instant_subtraction.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/len_zero.rs125
-rw-r--r--src/tools/clippy/clippy_lints/src/let_underscore.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs45
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_bits.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs15
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/match_result_ok.rs25
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_assert_message.rs82
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_doc.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs37
-rw-r--r--src/tools/clippy/clippy_lints/src/neg_multiply.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_async_block.rs84
-rw-r--r--src/tools/clippy/clippy_lints/src/size_of_ref.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/swap.rs111
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/use_self.rs15
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/author.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs23
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/mod.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/write.rs148
-rw-r--r--src/tools/clippy/clippy_utils/Cargo.toml2
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/macros.rs117
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs6
-rw-r--r--src/tools/clippy/declare_clippy_lint/Cargo.toml2
-rw-r--r--src/tools/clippy/lintcheck/Cargo.toml14
-rw-r--r--src/tools/clippy/lintcheck/README.md9
-rw-r--r--src/tools/clippy/lintcheck/src/config.rs160
-rw-r--r--src/tools/clippy/lintcheck/src/popular-crates.rs65
-rw-r--r--src/tools/clippy/rust-toolchain2
-rw-r--r--src/tools/clippy/src/driver.rs2
-rw-r--r--src/tools/clippy/src/main.rs2
-rw-r--r--src/tools/clippy/tests/dogfood.rs19
-rw-r--r--src/tools/clippy/tests/ui/arithmetic_side_effects.rs28
-rw-r--r--src/tools/clippy/tests/ui/arithmetic_side_effects.stderr288
-rw-r--r--src/tools/clippy/tests/ui/async_yields_async.fixed1
-rw-r--r--src/tools/clippy/tests/ui/async_yields_async.rs1
-rw-r--r--src/tools/clippy/tests/ui/async_yields_async.stderr12
-rw-r--r--src/tools/clippy/tests/ui/collection_is_never_read.rs165
-rw-r--r--src/tools/clippy/tests/ui/collection_is_never_read.stderr52
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-10148.rs9
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-10148.stderr12
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6179.rs2
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-rust-107877.rs17
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed3
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs3
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr48
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed3
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs3
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr52
-rw-r--r--src/tools/clippy/tests/ui/derivable_impls.fixed37
-rw-r--r--src/tools/clippy/tests/ui/derivable_impls.rs37
-rw-r--r--src/tools/clippy/tests/ui/format.fixed6
-rw-r--r--src/tools/clippy/tests/ui/format.rs6
-rw-r--r--src/tools/clippy/tests/ui/format.stderr30
-rw-r--r--src/tools/clippy/tests/ui/impl_trait_in_params.stderr4
-rw-r--r--src/tools/clippy/tests/ui/implicit_clone.fixed2
-rw-r--r--src/tools/clippy/tests/ui/implicit_clone.rs2
-rw-r--r--src/tools/clippy/tests/ui/len_without_is_empty.rs92
-rw-r--r--src/tools/clippy/tests/ui/len_without_is_empty.stderr20
-rw-r--r--src/tools/clippy/tests/ui/let_unit.fixed4
-rw-r--r--src/tools/clippy/tests/ui/let_unit.rs4
-rw-r--r--src/tools/clippy/tests/ui/let_with_type_underscore.rs19
-rw-r--r--src/tools/clippy/tests/ui/let_with_type_underscore.stderr39
-rw-r--r--src/tools/clippy/tests/ui/manual_rem_euclid.fixed1
-rw-r--r--src/tools/clippy/tests/ui/manual_rem_euclid.rs1
-rw-r--r--src/tools/clippy/tests/ui/manual_rem_euclid.stderr20
-rw-r--r--src/tools/clippy/tests/ui/match_result_ok.fixed2
-rw-r--r--src/tools/clippy/tests/ui/match_result_ok.stderr2
-rw-r--r--src/tools/clippy/tests/ui/missing_assert_message.rs84
-rw-r--r--src/tools/clippy/tests/ui/missing_assert_message.stderr131
-rw-r--r--src/tools/clippy/tests/ui/missing_doc.stderr70
-rw-r--r--src/tools/clippy/tests/ui/missing_doc_impl.stderr56
-rw-r--r--src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs28
-rw-r--r--src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr62
-rw-r--r--src/tools/clippy/tests/ui/new_ret_no_self.rs2
-rw-r--r--src/tools/clippy/tests/ui/redundant_async_block.fixed64
-rw-r--r--src/tools/clippy/tests/ui/redundant_async_block.rs64
-rw-r--r--src/tools/clippy/tests/ui/redundant_async_block.stderr28
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed1
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs1
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr12
-rw-r--r--src/tools/clippy/tests/ui/swap.fixed6
-rw-r--r--src/tools/clippy/tests/ui/swap.stderr24
-rw-r--r--src/tools/clippy/tests/ui/trailing_empty_array.rs2
-rw-r--r--src/tools/clippy/tests/ui/use_self.fixed10
-rw-r--r--src/tools/clippy/tests/ui/use_self.rs10
115 files changed, 2512 insertions, 779 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 7776964adf9..51332919fe7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -738,7 +738,7 @@ dependencies = [
 
 [[package]]
 name = "clippy"
-version = "0.1.69"
+version = "0.1.70"
 dependencies = [
  "clap 4.1.4",
  "clippy_lints",
@@ -781,7 +781,7 @@ dependencies = [
 
 [[package]]
 name = "clippy_lints"
-version = "0.1.69"
+version = "0.1.70"
 dependencies = [
  "cargo_metadata 0.15.3",
  "clippy_utils",
@@ -804,7 +804,7 @@ dependencies = [
 
 [[package]]
 name = "clippy_utils"
-version = "0.1.69"
+version = "0.1.70"
 dependencies = [
  "arrayvec 0.7.0",
  "if_chain",
@@ -1150,7 +1150,7 @@ checksum = "a0afaad2b26fa326569eb264b1363e8ae3357618c43982b3f285f0774ce76b69"
 
 [[package]]
 name = "declare_clippy_lint"
-version = "0.1.69"
+version = "0.1.70"
 dependencies = [
  "itertools",
  "quote",
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index 765826ed867..0abe234fc8f 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -6,11 +6,156 @@ document.
 
 ## Unreleased / Beta / In Rust Nightly
 
-[d822110d...master](https://github.com/rust-lang/rust-clippy/compare/d822110d...master)
+[7f27e2e7...master](https://github.com/rust-lang/rust-clippy/compare/7f27e2e7...master)
+
+## Rust 1.68
+
+Current stable, released 2023-03-09
+
+[d822110d...7f27e2e7](https://github.com/rust-lang/rust-clippy/compare/d822110d...7f27e2e7)
+
+### New Lints
+
+* [`permissions_set_readonly_false`]
+  [#10063](https://github.com/rust-lang/rust-clippy/pull/10063)
+* [`almost_complete_range`]
+  [#10043](https://github.com/rust-lang/rust-clippy/pull/10043)
+* [`size_of_ref`]
+  [#10098](https://github.com/rust-lang/rust-clippy/pull/10098)
+* [`semicolon_outside_block`]
+  [#9826](https://github.com/rust-lang/rust-clippy/pull/9826)
+* [`semicolon_inside_block`]
+  [#9826](https://github.com/rust-lang/rust-clippy/pull/9826)
+* [`transmute_null_to_fn`]
+  [#10099](https://github.com/rust-lang/rust-clippy/pull/10099)
+* [`fn_null_check`]
+  [#10099](https://github.com/rust-lang/rust-clippy/pull/10099)
+
+### Moves and Deprecations
+
+* Moved [`manual_clamp`] to `nursery` (Now allow-by-default)
+  [#10101](https://github.com/rust-lang/rust-clippy/pull/10101)
+* Moved [`mutex_atomic`] to `restriction`
+  [#10115](https://github.com/rust-lang/rust-clippy/pull/10115)
+* Renamed `derive_hash_xor_eq` to [`derived_hash_with_manual_eq`]
+  [#10184](https://github.com/rust-lang/rust-clippy/pull/10184)
+
+### Enhancements
+
+* [`collapsible_str_replace`]: Now takes MSRV into consideration. The minimal version is 1.58
+  [#10047](https://github.com/rust-lang/rust-clippy/pull/10047)
+* [`unused_self`]: No longer lints, if the method body contains a `todo!()` call
+  [#10166](https://github.com/rust-lang/rust-clippy/pull/10166)
+* [`derivable_impls`]: Now suggests deriving `Default` for enums with default unit variants
+  [#10161](https://github.com/rust-lang/rust-clippy/pull/10161)
+* [`arithmetic_side_effects`]: Added two new config values
+  `arithmetic-side-effects-allowed-binary` and `arithmetic-side-effects-allowed-unary`
+  to allow operation on user types
+  [#9840](https://github.com/rust-lang/rust-clippy/pull/9840)
+* [`large_const_arrays`], [`large_stack_arrays`]: avoid integer overflow when calculating
+  total array size
+  [#10103](https://github.com/rust-lang/rust-clippy/pull/10103)
+* [`indexing_slicing`]: add new config `suppress-restriction-lint-in-const` to enable
+  restriction lints, even if the suggestion might not be applicable
+  [#9920](https://github.com/rust-lang/rust-clippy/pull/9920)
+* [`needless_borrow`], [`redundant_clone`]: Now track references better and detect more cases
+  [#9701](https://github.com/rust-lang/rust-clippy/pull/9701)
+* [`derived_hash_with_manual_eq`]: Now allows `#[derive(PartialEq)]` with custom `Hash`
+  implementations
+  [#10184](https://github.com/rust-lang/rust-clippy/pull/10184)
+* [`manual_is_ascii_check`]: Now detects ranges with `.contains()` calls
+  [#10053](https://github.com/rust-lang/rust-clippy/pull/10053)
+* [`transmuting_null`]: Now detects `const` pointers to all types
+  [#10099](https://github.com/rust-lang/rust-clippy/pull/10099)
+* [`needless_return`]: Now detects more cases for returns of owned values
+  [#10110](https://github.com/rust-lang/rust-clippy/pull/10110)
+
+### False Positive Fixes
+
+* [`field_reassign_with_default`]: No longer lints cases, where values are initializes from
+  closures capturing struct values
+  [#10143](https://github.com/rust-lang/rust-clippy/pull/10143)
+* [`seek_to_start_instead_of_rewind`]: No longer lints, if the return of `seek` is used.
+  [#10096](https://github.com/rust-lang/rust-clippy/pull/10096)
+* [`manual_filter`]: Now ignores if expressions where the else branch has side effects or
+  doesn't return `None`
+  [#10091](https://github.com/rust-lang/rust-clippy/pull/10091)
+* [`implicit_clone`]: No longer lints if the type doesn't implement clone
+  [#10022](https://github.com/rust-lang/rust-clippy/pull/10022)
+* [`match_wildcard_for_single_variants`]: No longer lints on wildcards with a guard
+  [#10056](https://github.com/rust-lang/rust-clippy/pull/10056)
+* [`drop_ref`]: No longer lints idiomatic expression in `match` arms
+  [#10142](https://github.com/rust-lang/rust-clippy/pull/10142)
+* [`arithmetic_side_effects`]: No longer lints on corner cases with negative number literals
+  [#9867](https://github.com/rust-lang/rust-clippy/pull/9867)
+* [`string_lit_as_bytes`]: No longer lints in scrutinies of `match` statements
+  [#10012](https://github.com/rust-lang/rust-clippy/pull/10012)
+* [`manual_assert`]: No longer lints in `else if` statements
+  [#10013](https://github.com/rust-lang/rust-clippy/pull/10013)
+* [`needless_return`]: don't lint when using `do yeet`
+  [#10109](https://github.com/rust-lang/rust-clippy/pull/10109)
+* All lints: No longer lint in enum discriminant values when the suggestion won't work in a
+  const context
+  [#10008](https://github.com/rust-lang/rust-clippy/pull/10008)
+* [`single_element_loop`]: No longer lints, if the loop contains a `break` or `continue`
+  [#10162](https://github.com/rust-lang/rust-clippy/pull/10162)
+* [`uninlined_format_args`]: No longer suggests inlining arguments in `assert!` and
+  `debug_assert!` macros before 2021 edition
+  [#10055](https://github.com/rust-lang/rust-clippy/pull/10055)
+* [`explicit_counter_loop`]: No longer ignores counter changes after `continue` expressions
+  [#10094](https://github.com/rust-lang/rust-clippy/pull/10094)
+* [`from_over_into`]: No longer lints on opaque types
+  [#9982](https://github.com/rust-lang/rust-clippy/pull/9982)
+* [`expl_impl_clone_on_copy`]: No longer lints on `#[repr(packed)]` structs with generic
+  parameters
+  [#10189](https://github.com/rust-lang/rust-clippy/pull/10189)
+
+### Suggestion Fixes/Improvements
+
+* [`zero_ptr`]: Now suggests `core::` paths for `no_std` crates
+  [#10023](https://github.com/rust-lang/rust-clippy/pull/10023)
+* [`useless_conversion`]: Now suggests removing calls to `into_iter()` on an expression
+  implementing `Iterator`
+  [#10020](https://github.com/rust-lang/rust-clippy/pull/10020)
+* [`box_default`]: The suggestion now uses short paths
+  [#10153](https://github.com/rust-lang/rust-clippy/pull/10153)
+* [`default_trait_access`], [`clone_on_copy`]: The suggestion now uses short paths
+  [#10160](https://github.com/rust-lang/rust-clippy/pull/10160)
+* [`comparison_to_empty`]: The suggestion now removes unused deref operations
+  [#9962](https://github.com/rust-lang/rust-clippy/pull/9962)
+* [`manual_let_else`]: Suggestions for or-patterns now include required brackets.
+  [#9966](https://github.com/rust-lang/rust-clippy/pull/9966)
+* [`match_single_binding`]: suggestion no longer introduces unneeded semicolons
+  [#10060](https://github.com/rust-lang/rust-clippy/pull/10060)
+* [`case_sensitive_file_extension_comparisons`]: Now displays a suggestion with `Path`
+  [#10107](https://github.com/rust-lang/rust-clippy/pull/10107)
+* [`empty_structs_with_brackets`]: The suggestion is no longer machine applicable, to avoid
+  errors when accessing struct fields
+  [#10141](https://github.com/rust-lang/rust-clippy/pull/10141)
+* [`identity_op`]: Removes borrows in the suggestion when needed
+  [#10004](https://github.com/rust-lang/rust-clippy/pull/10004)
+* [`suboptimal_flops`]: The suggestion now includes parentheses when required
+  [#10113](https://github.com/rust-lang/rust-clippy/pull/10113)
+* [`iter_kv_map`]: Now handles `mut` and reference annotations in the suggestion
+  [#10159](https://github.com/rust-lang/rust-clippy/pull/10159)
+* [`redundant_static_lifetimes`]: The suggestion no longer removes `mut` from references
+  [#10006](https://github.com/rust-lang/rust-clippy/pull/10006)
+
+### ICE Fixes
+
+* [`new_ret_no_self`]: Now avoids a stack overflow for `impl Trait` types
+  [#10086](https://github.com/rust-lang/rust-clippy/pull/10086)
+* [`unnecessary_to_owned`]: Now handles compiler generated notes better
+  [#10027](https://github.com/rust-lang/rust-clippy/pull/10027)
+
+### Others
+
+* `SYSROOT` and `--sysroot` can now be set at the same time
+  [#10149](https://github.com/rust-lang/rust-clippy/pull/10149)
 
 ## Rust 1.67
 
-Current stable, released 2023-01-26
+Released 2023-01-26
 
 [4f142aa1...d822110d](https://github.com/rust-lang/rust-clippy/compare/4f142aa1...d822110d)
 
@@ -4307,6 +4452,7 @@ Released 2018-09-13
 [`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
 [`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match
 [`collapsible_str_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace
+[`collection_is_never_read`]: https://rust-lang.github.io/rust-clippy/master/index.html#collection_is_never_read
 [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
 [`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
 [`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime
@@ -4497,6 +4643,7 @@ Released 2018-09-13
 [`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use
 [`let_underscore_untyped`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped
 [`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
+[`let_with_type_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_with_type_underscore
 [`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
 [`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
 [`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
@@ -4560,6 +4707,7 @@ Released 2018-09-13
 [`mismatching_type_param_order`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatching_type_param_order
 [`misnamed_getters`]: https://rust-lang.github.io/rust-clippy/master/index.html#misnamed_getters
 [`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op
+[`missing_assert_message`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_assert_message
 [`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
 [`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items
 [`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames
@@ -4689,6 +4837,7 @@ Released 2018-09-13
 [`read_zero_byte_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#read_zero_byte_vec
 [`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl
 [`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
+[`redundant_async_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_async_block
 [`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone
 [`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
 [`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call
diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml
index 70d1268090f..c35dfcbd8c4 100644
--- a/src/tools/clippy/Cargo.toml
+++ b/src/tools/clippy/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy"
-version = "0.1.69"
+version = "0.1.70"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
diff --git a/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md b/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md
index c5587c4bf90..ea4978011b1 100644
--- a/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md
+++ b/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md
@@ -68,13 +68,13 @@ The second part of the motivation is clippy's dependence on unstable
 compiler-internal data structures. Clippy lints are currently written against
 the compiler's AST / HIR which means that even small changes in these data
 structures might break a lot of lints. The second goal of this RFC is to **make
-lints independant of the compiler's AST / HIR data structures**.
+lints independent of the compiler's AST / HIR data structures**.
 
 # Approach
 
 A lot of complexity in writing lints currently seems to come from having to
 manually implement the matching logic (see code samples above). It's an
-imparative style that describes *how* to match a syntax tree node instead of
+imperative style that describes *how* to match a syntax tree node instead of
 specifying *what* should be matched against declaratively. In other areas, it's
 common to use declarative patterns to describe desired information and let the
 implementation do the actual matching. A well-known example of this approach are
@@ -270,7 +270,7 @@ pattern!{
     // matches if expressions that **may or may not** have an else block
     // Attn: `If(_, _, _)` matches only ifs that **have** an else block
     //
-    //              | if with else block | if witout else block
+    //              | if with else block | if without else block
     // If(_, _, _)  |       match        |       no match
     // If(_, _, _?) |       match        |        match
     // If(_, _, ()) |      no match      |        match
@@ -568,7 +568,7 @@ another example, `Array( Lit(_)* )` is a valid pattern because the parameter of
 
 ## The IsMatch Trait
 
-The pattern syntax and the *PatternTree* are independant of specific syntax tree
+The pattern syntax and the *PatternTree* are independent of specific syntax tree
 implementations (rust ast / hir, syn, ...). When looking at the different
 pattern examples in the previous sections, it can be seen that the patterns
 don't contain any information specific to a certain syntax tree implementation.
@@ -717,7 +717,7 @@ if false {
 #### Problems
 
 Extending Rust syntax (which is quite complex by itself) with additional syntax
-needed for specifying patterns (alternations, sequences, repetisions, named
+needed for specifying patterns (alternations, sequences, repetitions, named
 submatches, ...) might become difficult to read and really hard to parse
 properly.
 
@@ -858,7 +858,7 @@ would be evaluated as soon as the `Block(_)#then` was matched.
 Another idea in this area would be to introduce a syntax for backreferences.
 They could be used to require that multiple parts of a pattern should match the
 same value. For example, the `assign_op_pattern` lint that searches for `a = a
-op b` and recommends changing it to `a op= b` requires that both occurrances of
+op b` and recommends changing it to `a op= b` requires that both occurrences of
 `a` are the same. Using `=#...` as syntax for backreferences, the lint could be
 implemented like this:
 
@@ -882,7 +882,7 @@ least two return statements" could be a practical addition.
 For patterns like "a literal that is not a boolean literal" one currently needs
 to list all alternatives except the boolean case. Introducing a negation
 operator that allows to write `Lit(!Bool(_))` might be a good idea. This pattern
-would be eqivalent to `Lit( Char(_) | Int(_) )` (given that currently only three
+would be equivalent to `Lit( Char(_) | Int(_) )` (given that currently only three
 literal types are implemented).
 
 #### Functional composition
diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml
index 796f1ff1695..0b3846c1316 100644
--- a/src/tools/clippy/clippy_lints/Cargo.toml
+++ b/src/tools/clippy/clippy_lints/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_lints"
-version = "0.1.69"
+version = "0.1.70"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
diff --git a/src/tools/clippy/clippy_lints/src/almost_complete_range.rs b/src/tools/clippy/clippy_lints/src/almost_complete_range.rs
index 42e14b5cd94..32d80f42e7e 100644
--- a/src/tools/clippy/clippy_lints/src/almost_complete_range.rs
+++ b/src/tools/clippy/clippy_lints/src/almost_complete_range.rs
@@ -24,7 +24,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let _ = 'a'..='z';
     /// ```
-    #[clippy::version = "1.63.0"]
+    #[clippy::version = "1.68.0"]
     pub ALMOST_COMPLETE_RANGE,
     suspicious,
     "almost complete range"
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs b/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
index 627b795d6ed..1233c632a79 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
 use clippy_utils::{match_def_path, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
@@ -34,6 +34,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
         if let ExprKind::Path(ref qpath) = fun.kind;
         if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
         if let Some(rpk) = raw_parts_kind(cx, fun_def_id);
+        let ctxt = expr.span.ctxt();
+        if cast_expr.span.ctxt() == ctxt;
         then {
             let func = match rpk {
                 RawPartsKind::Immutable => "from_raw_parts",
@@ -41,8 +43,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
             };
             let span = expr.span;
             let mut applicability = Applicability::MachineApplicable;
-            let ptr = snippet_with_applicability(cx, ptr_arg.span, "ptr", &mut applicability);
-            let len = snippet_with_applicability(cx, len_arg.span, "len", &mut applicability);
+            let ptr = snippet_with_context(cx, ptr_arg.span, ctxt, "ptr", &mut applicability).0;
+            let len = snippet_with_context(cx, len_arg.span, ctxt, "len", &mut applicability).0;
             span_lint_and_sugg(
                 cx,
                 CAST_SLICE_FROM_RAW_PARTS,
diff --git a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs
new file mode 100644
index 00000000000..10f2bef268a
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs
@@ -0,0 +1,122 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::visitors::for_each_expr_with_closures;
+use clippy_utils::{get_enclosing_block, get_parent_node, path_to_local_id};
+use core::ops::ControlFlow;
+use rustc_hir::{Block, ExprKind, HirId, Local, Node, PatKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::sym;
+use rustc_span::Symbol;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for collections that are never queried.
+    ///
+    /// ### Why is this bad?
+    /// Putting effort into constructing a collection but then never querying it might indicate that
+    /// the author forgot to do whatever they intended to do with the collection. Example: Clone
+    /// a vector, sort it for iteration, but then mistakenly iterate the original vector
+    /// instead.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let samples = vec![3, 1, 2];
+    /// let mut sorted_samples = samples.clone();
+    /// sorted_samples.sort();
+    /// for sample in &samples { // Oops, meant to use `sorted_samples`.
+    ///     println!("{sample}");
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # let samples = vec![3, 1, 2];
+    /// let mut sorted_samples = samples.clone();
+    /// sorted_samples.sort();
+    /// for sample in &sorted_samples {
+    ///     println!("{sample}");
+    /// }
+    /// ```
+    #[clippy::version = "1.69.0"]
+    pub COLLECTION_IS_NEVER_READ,
+    nursery,
+    "a collection is never queried"
+}
+declare_lint_pass!(CollectionIsNeverRead => [COLLECTION_IS_NEVER_READ]);
+
+static COLLECTIONS: [Symbol; 10] = [
+    sym::BTreeMap,
+    sym::BTreeSet,
+    sym::BinaryHeap,
+    sym::HashMap,
+    sym::HashSet,
+    sym::LinkedList,
+    sym::Option,
+    sym::String,
+    sym::Vec,
+    sym::VecDeque,
+];
+
+impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead {
+    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
+        // Look for local variables whose type is a container. Search surrounding bock for read access.
+        let ty = cx.typeck_results().pat_ty(local.pat);
+        if COLLECTIONS.iter().any(|&sym| is_type_diagnostic_item(cx, ty, sym))
+            && let PatKind::Binding(_, local_id, _, _) = local.pat.kind
+            && let Some(enclosing_block) = get_enclosing_block(cx, local.hir_id)
+            && has_no_read_access(cx, local_id, enclosing_block)
+        {
+            span_lint(cx, COLLECTION_IS_NEVER_READ, local.span, "collection is never read");
+        }
+    }
+}
+
+fn has_no_read_access<'tcx>(cx: &LateContext<'tcx>, id: HirId, block: &'tcx Block<'tcx>) -> bool {
+    let mut has_access = false;
+    let mut has_read_access = false;
+
+    // Inspect all expressions and sub-expressions in the block.
+    for_each_expr_with_closures(cx, block, |expr| {
+        // Ignore expressions that are not simply `id`.
+        if !path_to_local_id(expr, id) {
+            return ControlFlow::Continue(());
+        }
+
+        // `id` is being accessed. Investigate if it's a read access.
+        has_access = true;
+
+        // `id` appearing in the left-hand side of an assignment is not a read access:
+        //
+        // id = ...; // Not reading `id`.
+        if let Some(Node::Expr(parent)) = get_parent_node(cx.tcx, expr.hir_id)
+            && let ExprKind::Assign(lhs, ..) = parent.kind
+            && path_to_local_id(lhs, id)
+        {
+            return ControlFlow::Continue(());
+        }
+
+        // Method call on `id` in a statement ignores any return value, so it's not a read access:
+        //
+        // id.foo(...); // Not reading `id`.
+        //
+        // Only assuming this for "official" methods defined on the type. For methods defined in extension
+        // traits (identified as local, based on the orphan rule), pessimistically assume that they might
+        // have side effects, so consider them a read.
+        if let Some(Node::Expr(parent)) = get_parent_node(cx.tcx, expr.hir_id)
+            && let ExprKind::MethodCall(_, receiver, _, _) = parent.kind
+            && path_to_local_id(receiver, id)
+            && let Some(Node::Stmt(..)) = get_parent_node(cx.tcx, parent.hir_id)
+            && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id)
+            && !method_def_id.is_local()
+        {
+            return ControlFlow::Continue(());
+        }
+
+        // Any other access to `id` is a read access. Stop searching.
+        has_read_access = true;
+        ControlFlow::Break(())
+    });
+
+    // Ignore collections that have no access at all. Other lints should catch them.
+    has_access && !has_read_access
+}
diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs
index cd5dd7a5706..cc6024b87cd 100644
--- a/src/tools/clippy/clippy_lints/src/declared_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs
@@ -92,6 +92,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO,
     crate::collapsible_if::COLLAPSIBLE_ELSE_IF_INFO,
     crate::collapsible_if::COLLAPSIBLE_IF_INFO,
+    crate::collection_is_never_read::COLLECTION_IS_NEVER_READ_INFO,
     crate::comparison_chain::COMPARISON_CHAIN_INFO,
     crate::copies::BRANCHES_SHARING_CODE_INFO,
     crate::copies::IFS_SAME_COND_INFO,
@@ -226,6 +227,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::let_underscore::LET_UNDERSCORE_LOCK_INFO,
     crate::let_underscore::LET_UNDERSCORE_MUST_USE_INFO,
     crate::let_underscore::LET_UNDERSCORE_UNTYPED_INFO,
+    crate::let_with_type_underscore::LET_WITH_TYPE_UNDERSCORE_INFO,
     crate::lifetimes::EXTRA_UNUSED_LIFETIMES_INFO,
     crate::lifetimes::NEEDLESS_LIFETIMES_INFO,
     crate::literal_representation::DECIMAL_LITERAL_REPRESENTATION_INFO,
@@ -416,6 +418,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::misc_early::UNSEPARATED_LITERAL_SUFFIX_INFO,
     crate::misc_early::ZERO_PREFIXED_LITERAL_INFO,
     crate::mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER_INFO,
+    crate::missing_assert_message::MISSING_ASSERT_MESSAGE_INFO,
     crate::missing_const_for_fn::MISSING_CONST_FOR_FN_INFO,
     crate::missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS_INFO,
     crate::missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES_INFO,
@@ -517,6 +520,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::ranges::REVERSED_EMPTY_RANGES_INFO,
     crate::rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT_INFO,
     crate::read_zero_byte_vec::READ_ZERO_BYTE_VEC_INFO,
+    crate::redundant_async_block::REDUNDANT_ASYNC_BLOCK_INFO,
     crate::redundant_clone::REDUNDANT_CLONE_INFO,
     crate::redundant_closure_call::REDUNDANT_CLOSURE_CALL_INFO,
     crate::redundant_else::REDUNDANT_ELSE_INFO,
diff --git a/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs b/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs
index 1ad929864b2..f296b80d283 100644
--- a/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs
+++ b/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs
@@ -1,11 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::last_path_segment;
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
 use clippy_utils::{match_def_path, paths};
 use rustc_errors::Applicability;
 use rustc_hir::{def, Expr, ExprKind, GenericArg, QPath, TyKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::SyntaxContext;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -38,9 +39,11 @@ impl<'tcx> LateLintPass<'tcx> for DefaultIterEmpty {
             && let QPath::Resolved(None, path) = ty_path
             && let def::Res::Def(_, def_id) = &path.res
             && match_def_path(cx, *def_id, &paths::ITER_EMPTY)
+            && let ctxt = expr.span.ctxt()
+            && ty.span.ctxt() == ctxt
         {
             let mut applicability = Applicability::MachineApplicable;
-            let sugg = make_sugg(cx, ty_path, &mut applicability);
+            let sugg = make_sugg(cx, ty_path, ctxt, &mut applicability);
             span_lint_and_sugg(
                 cx,
                 DEFAULT_INSTEAD_OF_ITER_EMPTY,
@@ -54,14 +57,19 @@ impl<'tcx> LateLintPass<'tcx> for DefaultIterEmpty {
     }
 }
 
-fn make_sugg(cx: &LateContext<'_>, ty_path: &rustc_hir::QPath<'_>, applicability: &mut Applicability) -> String {
+fn make_sugg(
+    cx: &LateContext<'_>,
+    ty_path: &rustc_hir::QPath<'_>,
+    ctxt: SyntaxContext,
+    applicability: &mut Applicability,
+) -> String {
     if let Some(last) = last_path_segment(ty_path).args
         && let Some(iter_ty) = last.args.iter().find_map(|arg| match arg {
             GenericArg::Type(ty) => Some(ty),
             _ => None,
         })
     {
-        format!("std::iter::empty::<{}>()", snippet_with_applicability(cx, iter_ty.span, "..", applicability))
+        format!("std::iter::empty::<{}>()", snippet_with_context(cx, iter_ty.span, ctxt, "..", applicability).0)
     } else {
         "std::iter::empty()".to_owned()
     }
diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs
index 47501980e66..7f3f26bed7c 100644
--- a/src/tools/clippy/clippy_lints/src/dereference.rs
+++ b/src/tools/clippy/clippy_lints/src/dereference.rs
@@ -1357,10 +1357,10 @@ fn replace_types<'tcx>(
                     && let Some(term_ty) = projection_predicate.term.ty()
                     && let ty::Param(term_param_ty) = term_ty.kind()
                 {
-                    let item_def_id = projection_predicate.projection_ty.def_id;
-                    let assoc_item = cx.tcx.associated_item(item_def_id);
-                    let projection = cx.tcx
-                        .mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(new_ty, []));
+                    let projection = cx.tcx.mk_ty_from_kind(ty::Alias(
+                        ty::Projection,
+                        projection_predicate.projection_ty.with_self_ty(cx.tcx, new_ty),
+                    ));
 
                     if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection)
                         && substs[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty)
diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
index c5f4e943f4f..8a5a28c6b3d 100644
--- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
@@ -8,7 +8,7 @@ use rustc_hir::{
     Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, Ty, TyKind,
 };
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::AdtDef;
+use rustc_middle::ty::{Adt, AdtDef, SubstsRef};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::sym;
 
@@ -81,13 +81,18 @@ fn check_struct<'tcx>(
     self_ty: &Ty<'_>,
     func_expr: &Expr<'_>,
     adt_def: AdtDef<'_>,
+    substs: SubstsRef<'_>,
 ) {
     if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind {
-        if let Some(PathSegment { args: Some(a), .. }) = p.segments.last() {
-            for arg in a.args {
-                if !matches!(arg, GenericArg::Lifetime(_)) {
-                    return;
-                }
+        if let Some(PathSegment { args, .. }) = p.segments.last() {
+            let args = args.map(|a| a.args).unwrap_or(&[]);
+
+            // substs contains the generic parameters of the type declaration, while args contains the arguments
+            // used at instantiation time. If both len are not equal, it means that some parameters were not
+            // provided (which means that the default values were used); in this case we will not risk
+            // suggesting too broad a rewrite. We won't either if any argument is a type or a const.
+            if substs.len() != args.len() || args.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))) {
+                return;
             }
         }
     }
@@ -184,7 +189,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
             if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir);
             if let ImplItemKind::Fn(_, b) = &impl_item.kind;
             if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b);
-            if let Some(adt_def) = cx.tcx.type_of(item.owner_id).subst_identity().ty_adt_def();
+            if let &Adt(adt_def, substs) = cx.tcx.type_of(item.owner_id).subst_identity().kind();
             if let attrs = cx.tcx.hir().attrs(item.hir_id());
             if !attrs.iter().any(|attr| attr.doc_str().is_some());
             if let child_attrs = cx.tcx.hir().attrs(impl_item_hir);
@@ -192,7 +197,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
 
             then {
                 if adt_def.is_struct() {
-                    check_struct(cx, item, self_ty, func_expr, adt_def);
+                    check_struct(cx, item, self_ty, func_expr, adt_def, substs);
                 } else if adt_def.is_enum() && self.msrv.meets(msrvs::DEFAULT_ENUM_ATTRIBUTE) {
                     check_enum(cx, item, func_expr, adt_def);
                 }
diff --git a/src/tools/clippy/clippy_lints/src/exit.rs b/src/tools/clippy/clippy_lints/src/exit.rs
index 9c8b0d076df..8ba6a9e4876 100644
--- a/src/tools/clippy/clippy_lints/src/exit.rs
+++ b/src/tools/clippy/clippy_lints/src/exit.rs
@@ -11,7 +11,7 @@ declare_clippy_lint! {
     ///
     /// ### Why is this bad?
     /// Exit terminates the program at the location it is called. For unrecoverable
-    /// errors `panics` should be used to provide a stacktrace and potentualy other
+    /// errors `panics` should be used to provide a stacktrace and potentially other
     /// information. A normal termination or one with an error code should happen in
     /// the main function.
     ///
diff --git a/src/tools/clippy/clippy_lints/src/fn_null_check.rs b/src/tools/clippy/clippy_lints/src/fn_null_check.rs
index 91c8c340ce2..d8f4a5fe221 100644
--- a/src/tools/clippy/clippy_lints/src/fn_null_check.rs
+++ b/src/tools/clippy/clippy_lints/src/fn_null_check.rs
@@ -25,7 +25,7 @@ declare_clippy_lint! {
     ///
     /// if fn_ptr.is_none() { ... }
     /// ```
-    #[clippy::version = "1.67.0"]
+    #[clippy::version = "1.68.0"]
     pub FN_NULL_CHECK,
     correctness,
     "`fn()` type assumed to be nullable"
diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs
index d0fab694960..8040938c626 100644
--- a/src/tools/clippy/clippy_lints/src/format.rs
+++ b/src/tools/clippy/clippy_lints/src/format.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn};
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
 use clippy_utils::sugg::Sugg;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
@@ -84,9 +84,9 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
                         _ => false,
                     };
                     let sugg = if is_new_string {
-                        snippet_with_applicability(cx, value.span, "..", &mut applicability).into_owned()
+                        snippet_with_context(cx, value.span, call_site.ctxt(), "..", &mut applicability).0.into_owned()
                     } else {
-                        let sugg = Sugg::hir_with_applicability(cx, value, "<arg>", &mut applicability);
+                        let sugg = Sugg::hir_with_context(cx, value, call_site.ctxt(), "<arg>", &mut applicability);
                         format!("{}.to_string()", sugg.maybe_par())
                     };
                     span_useless_format(cx, call_site, sugg, applicability);
diff --git a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
index 2811a73f6c1..d3d0d91c1be 100644
--- a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
@@ -22,7 +22,7 @@ pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body:
                             if let Some(gen_span) = generics.span_for_param_suggestion() {
                                 diag.span_suggestion_with_style(
                                     gen_span,
-                                    "add a type paremeter",
+                                    "add a type parameter",
                                     format!(", {{ /* Generic name */ }}: {}", &param.name.ident().as_str()[5..]),
                                     rustc_errors::Applicability::HasPlaceholders,
                                     rustc_errors::SuggestionStyle::ShowAlways,
@@ -35,7 +35,7 @@ pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body:
                                         ident.span.ctxt(),
                                         ident.span.parent(),
                                     ),
-                                    "add a type paremeter",
+                                    "add a type parameter",
                                     format!("<{{ /* Generic name */ }}: {}>", &param.name.ident().as_str()[5..]),
                                     rustc_errors::Applicability::HasPlaceholders,
                                     rustc_errors::SuggestionStyle::ShowAlways,
diff --git a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs
index 8b53ee68ebd..e5945939e60 100644
--- a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs
@@ -97,7 +97,7 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body:
 
     let Some(correct_field) = correct_field else {
         // There is no field corresponding to the getter name.
-        // FIXME: This can be a false positive if the correct field is reachable trought deeper autodereferences than used_field is
+        // FIXME: This can be a false positive if the correct field is reachable through deeper autodereferences than used_field is
         return;
     };
 
diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs
index d2852b4acad..7c5e44bb7dc 100644
--- a/src/tools/clippy/clippy_lints/src/functions/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs
@@ -185,7 +185,7 @@ declare_clippy_lint! {
     /// ### Examples
     /// ```rust
     /// // this could be annotated with `#[must_use]`.
-    /// fn id<T>(t: T) -> T { t }
+    /// pub fn id<T>(t: T) -> T { t }
     /// ```
     #[clippy::version = "1.40.0"]
     pub MUST_USE_CANDIDATE,
diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs
index 6e19343931e..57e6caa8711 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs
@@ -1,7 +1,7 @@
 use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::get_parent_expr;
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
 use if_chain::if_chain;
 use rustc_ast::ast::{LitIntType, LitKind};
 use rustc_errors::Applicability;
@@ -55,6 +55,9 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd {
             if let ExprKind::AssignOp(op1, target, value) = ex.kind;
             let ty = cx.typeck_results().expr_ty(target);
             if Some(c) == get_int_max(ty);
+            let ctxt = expr.span.ctxt();
+            if ex.span.ctxt() == ctxt;
+            if expr1.span.ctxt() == ctxt;
             if clippy_utils::SpanlessEq::new(cx).eq_expr(l, target);
             if BinOpKind::Add == op1.node;
             if let ExprKind::Lit(ref lit) = value.kind;
@@ -62,8 +65,15 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd {
             if block.expr.is_none();
             then {
                 let mut app = Applicability::MachineApplicable;
-                let code = snippet_with_applicability(cx, target.span, "_", &mut app);
-                let sugg = if let Some(parent) = get_parent_expr(cx, expr) && let ExprKind::If(_cond, _then, Some(else_)) = parent.kind && else_.hir_id == expr.hir_id {format!("{{{code} = {code}.saturating_add(1); }}")} else {format!("{code} = {code}.saturating_add(1);")};
+                let code = snippet_with_context(cx, target.span, ctxt, "_", &mut app).0;
+                let sugg = if let Some(parent) = get_parent_expr(cx, expr)
+                    && let ExprKind::If(_cond, _then, Some(else_)) = parent.kind
+                    && else_.hir_id == expr.hir_id
+                {
+                    format!("{{{code} = {code}.saturating_add(1); }}")
+                } else {
+                    format!("{code} = {code}.saturating_add(1);")
+                };
                 span_lint_and_sugg(cx, IMPLICIT_SATURATING_ADD, expr.span, "manual saturating add detected", "use instead", sugg, app);
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
index 668110c7cc0..34e9991582c 100644
--- a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
+++ b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::{self, span_lint_and_sugg};
 use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::source;
+use clippy_utils::source::snippet_with_context;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty;
 use rustc_errors::Applicability;
@@ -161,14 +161,9 @@ fn print_unchecked_duration_subtraction_sugg(
 ) {
     let mut applicability = Applicability::MachineApplicable;
 
-    let left_expr =
-        source::snippet_with_applicability(cx, left_expr.span, "std::time::Instant::now()", &mut applicability);
-    let right_expr = source::snippet_with_applicability(
-        cx,
-        right_expr.span,
-        "std::time::Duration::from_secs(1)",
-        &mut applicability,
-    );
+    let ctxt = expr.span.ctxt();
+    let left_expr = snippet_with_context(cx, left_expr.span, ctxt, "<instant>", &mut applicability).0;
+    let right_expr = snippet_with_context(cx, right_expr.span, ctxt, "<duration>", &mut applicability).0;
 
     diagnostics::span_lint_and_sugg(
         cx,
diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs
index e13bc47973b..0805b4b1979 100644
--- a/src/tools/clippy/clippy_lints/src/len_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/len_zero.rs
@@ -1,13 +1,14 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators};
+use clippy_utils::source::snippet_with_context;
+use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators, sugg::Sugg};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::def_id::DefIdSet;
 use rustc_hir::{
-    def_id::DefId, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, ImplItem, ImplItemKind, ImplicitSelfKind, Item,
-    ItemKind, Mutability, Node, TraitItemRef, TyKind, UnOp,
+    def::Res, def_id::DefId, lang_items::LangItem, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, GenericArg,
+    GenericBound, ImplItem, ImplItemKind, ImplicitSelfKind, Item, ItemKind, Mutability, Node, PathSegment, PrimTy,
+    QPath, TraitItemRef, TyKind, TypeBindingKind,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{self, AssocKind, FnSig, Ty};
@@ -16,7 +17,6 @@ use rustc_span::{
     source_map::{Span, Spanned, Symbol},
     symbol::sym,
 };
-use std::borrow::Cow;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -251,33 +251,98 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items
 }
 
 #[derive(Debug, Clone, Copy)]
-enum LenOutput<'tcx> {
+enum LenOutput {
     Integral,
     Option(DefId),
-    Result(DefId, Ty<'tcx>),
+    Result(DefId),
 }
-fn parse_len_output<'tcx>(cx: &LateContext<'_>, sig: FnSig<'tcx>) -> Option<LenOutput<'tcx>> {
+
+fn extract_future_output<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
+    if let ty::Alias(_, alias_ty) = ty.kind() &&
+        let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(alias_ty.def_id) &&
+        let Item { kind: ItemKind::OpaqueTy(opaque), .. } = item &&
+        opaque.bounds.len() == 1 &&
+        let GenericBound::LangItemTrait(LangItem::Future, _, _, generic_args) = &opaque.bounds[0] &&
+        generic_args.bindings.len() == 1 &&
+        let TypeBindingKind::Equality {
+            term: rustc_hir::Term::Ty(rustc_hir::Ty {kind: TyKind::Path(QPath::Resolved(_, path)), .. }),
+        } = &generic_args.bindings[0].kind &&
+        path.segments.len() == 1 {
+            return Some(&path.segments[0]);
+        }
+
+    None
+}
+
+fn is_first_generic_integral<'tcx>(segment: &'tcx PathSegment<'tcx>) -> bool {
+    if let Some(generic_args) = segment.args {
+        if generic_args.args.is_empty() {
+            return false;
+        }
+        let arg = &generic_args.args[0];
+        if let GenericArg::Type(rustc_hir::Ty {
+            kind: TyKind::Path(QPath::Resolved(_, path)),
+            ..
+        }) = arg
+        {
+            let segments = &path.segments;
+            let segment = &segments[0];
+            let res = &segment.res;
+            if matches!(res, Res::PrimTy(PrimTy::Uint(_))) || matches!(res, Res::PrimTy(PrimTy::Int(_))) {
+                return true;
+            }
+        }
+    }
+
+    false
+}
+
+fn parse_len_output<'tcx>(cx: &LateContext<'tcx>, sig: FnSig<'tcx>) -> Option<LenOutput> {
+    if let Some(segment) = extract_future_output(cx, sig.output()) {
+        let res = segment.res;
+
+        if matches!(res, Res::PrimTy(PrimTy::Uint(_))) || matches!(res, Res::PrimTy(PrimTy::Int(_))) {
+            return Some(LenOutput::Integral);
+        }
+
+        if let Res::Def(_, def_id) = res {
+            if cx.tcx.is_diagnostic_item(sym::Option, def_id) && is_first_generic_integral(segment) {
+                return Some(LenOutput::Option(def_id));
+            } else if cx.tcx.is_diagnostic_item(sym::Result, def_id) && is_first_generic_integral(segment) {
+                return Some(LenOutput::Result(def_id));
+            }
+        }
+
+        return None;
+    }
+
     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()) => {
             subs.type_at(0).is_integral().then(|| LenOutput::Option(adt.did()))
         },
-        ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => subs
-            .type_at(0)
-            .is_integral()
-            .then(|| LenOutput::Result(adt.did(), subs.type_at(1))),
+        ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => {
+            subs.type_at(0).is_integral().then(|| LenOutput::Result(adt.did()))
+        },
         _ => None,
     }
 }
 
-impl<'tcx> LenOutput<'tcx> {
-    fn matches_is_empty_output(self, ty: Ty<'tcx>) -> bool {
+impl LenOutput {
+    fn matches_is_empty_output<'tcx>(self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+        if let Some(segment) = extract_future_output(cx, ty) {
+            return match (self, segment.res) {
+                (_, Res::PrimTy(PrimTy::Bool)) => true,
+                (Self::Option(_), Res::Def(_, def_id)) if cx.tcx.is_diagnostic_item(sym::Option, def_id) => true,
+                (Self::Result(_), Res::Def(_, def_id)) if cx.tcx.is_diagnostic_item(sym::Result, def_id) => true,
+                _ => false,
+            };
+        }
+
         match (self, ty.kind()) {
             (_, &ty::Bool) => true,
             (Self::Option(id), &ty::Adt(adt, subs)) if id == adt.did() => subs.type_at(0).is_bool(),
-            (Self::Result(id, err_ty), &ty::Adt(adt, subs)) if id == adt.did() => {
-                subs.type_at(0).is_bool() && subs.type_at(1) == err_ty
-            },
+            (Self::Result(id), &ty::Adt(adt, subs)) if id == adt.did() => subs.type_at(0).is_bool(),
             _ => false,
         }
     }
@@ -301,9 +366,14 @@ impl<'tcx> LenOutput<'tcx> {
 }
 
 /// Checks if the given signature matches the expectations for `is_empty`
-fn check_is_empty_sig<'tcx>(sig: FnSig<'tcx>, self_kind: ImplicitSelfKind, len_output: LenOutput<'tcx>) -> bool {
+fn check_is_empty_sig<'tcx>(
+    cx: &LateContext<'tcx>,
+    sig: FnSig<'tcx>,
+    self_kind: ImplicitSelfKind,
+    len_output: LenOutput,
+) -> bool {
     match &**sig.inputs_and_output {
-        [arg, res] if len_output.matches_is_empty_output(*res) => {
+        [arg, res] if len_output.matches_is_empty_output(cx, *res) => {
             matches!(
                 (arg.kind(), self_kind),
                 (ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::ImmRef)
@@ -315,11 +385,11 @@ fn check_is_empty_sig<'tcx>(sig: FnSig<'tcx>, self_kind: ImplicitSelfKind, len_o
 }
 
 /// Checks if the given type has an `is_empty` method with the appropriate signature.
-fn check_for_is_empty<'tcx>(
-    cx: &LateContext<'tcx>,
+fn check_for_is_empty(
+    cx: &LateContext<'_>,
     span: Span,
     self_kind: ImplicitSelfKind,
-    output: LenOutput<'tcx>,
+    output: LenOutput,
     impl_ty: DefId,
     item_name: Symbol,
     item_kind: &str,
@@ -352,6 +422,7 @@ fn check_for_is_empty<'tcx>(
         Some(is_empty)
             if !(is_empty.fn_has_self_parameter
                 && check_is_empty_sig(
+                    cx,
                     cx.tcx.fn_sig(is_empty.def_id).subst_identity().skip_binder(),
                     self_kind,
                     output,
@@ -431,7 +502,7 @@ fn check_len(
                 &format!("using `{op}is_empty` is clearer and more explicit"),
                 format!(
                     "{op}{}.is_empty()",
-                    snippet_with_applicability(cx, receiver.span, "_", &mut applicability)
+                    snippet_with_context(cx, receiver.span, span.ctxt(), "_", &mut applicability).0,
                 ),
                 applicability,
             );
@@ -444,13 +515,7 @@ fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Ex
         let mut applicability = Applicability::MachineApplicable;
 
         let lit1 = peel_ref_operators(cx, lit1);
-        let mut lit_str = snippet_with_applicability(cx, lit1.span, "_", &mut applicability);
-
-        // Wrap the expression in parentheses if it's a deref expression. Otherwise operator precedence will
-        // cause the code to dereference boolean(won't compile).
-        if let ExprKind::Unary(UnOp::Deref, _) = lit1.kind {
-            lit_str = Cow::from(format!("({lit_str})"));
-        }
+        let lit_str = Sugg::hir_with_context(cx, lit1, span.ctxt(), "_", &mut applicability).maybe_par();
 
         span_lint_and_sugg(
             cx,
diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs
index 7600777fab9..51b5de27de8 100644
--- a/src/tools/clippy/clippy_lints/src/let_underscore.rs
+++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs
@@ -124,7 +124,7 @@ declare_clippy_lint! {
     /// ```
     #[clippy::version = "1.69.0"]
     pub LET_UNDERSCORE_UNTYPED,
-    pedantic,
+    restriction,
     "non-binding `let` without a type annotation"
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs
new file mode 100644
index 00000000000..ba51973f2f9
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs
@@ -0,0 +1,45 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use rustc_hir::*;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Detects when a variable is declared with an explicit type of `_`.
+    /// ### Why is this bad?
+    /// It adds noise, `: _` provides zero clarity or utility.
+    /// ### Example
+    /// ```rust,ignore
+    /// let my_number: _ = 1;
+    /// ```
+    /// Use instead:
+    /// ```rust,ignore
+    /// let my_number = 1;
+    /// ```
+    #[clippy::version = "1.69.0"]
+    pub LET_WITH_TYPE_UNDERSCORE,
+    complexity,
+    "unneeded underscore type (`_`) in a variable declaration"
+}
+declare_lint_pass!(UnderscoreTyped => [LET_WITH_TYPE_UNDERSCORE]);
+
+impl LateLintPass<'_> for UnderscoreTyped {
+    fn check_local<'tcx>(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
+        if_chain! {
+            if !in_external_macro(cx.tcx.sess, local.span);
+            if let Some(ty) = local.ty; // Ensure that it has a type defined
+            if let TyKind::Infer = &ty.kind; // that type is '_'
+            if local.span.ctxt() == ty.span.ctxt();
+            then {
+                span_lint_and_help(cx,
+                    LET_WITH_TYPE_UNDERSCORE,
+                    local.span,
+                    "variable declared with type underscore",
+                    Some(ty.span.with_lo(local.pat.span.hi())),
+                    "remove the explicit type `_` declaration"
+                )
+            }
+        };
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index c626e0bd998..491732be208 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -87,6 +87,7 @@ mod casts;
 mod checked_conversions;
 mod cognitive_complexity;
 mod collapsible_if;
+mod collection_is_never_read;
 mod comparison_chain;
 mod copies;
 mod copy_iterator;
@@ -166,6 +167,7 @@ mod large_stack_arrays;
 mod len_zero;
 mod let_if_seq;
 mod let_underscore;
+mod let_with_type_underscore;
 mod lifetimes;
 mod literal_representation;
 mod loops;
@@ -192,6 +194,7 @@ mod minmax;
 mod misc;
 mod misc_early;
 mod mismatching_type_param_order;
+mod missing_assert_message;
 mod missing_const_for_fn;
 mod missing_doc;
 mod missing_enforced_import_rename;
@@ -249,6 +252,7 @@ mod question_mark_used;
 mod ranges;
 mod rc_clone_in_vec_init;
 mod read_zero_byte_vec;
+mod redundant_async_block;
 mod redundant_clone;
 mod redundant_closure_call;
 mod redundant_else;
@@ -533,6 +537,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
                 .collect(),
         ))
     });
+    store.register_early_pass(|| Box::new(utils::format_args_collector::FormatArgsCollector));
     store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir));
     store.register_late_pass(|_| Box::new(utils::author::Author));
     let await_holding_invalid_types = conf.await_holding_invalid_types.clone();
@@ -924,6 +929,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         ))
     });
     store.register_late_pass(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi));
+    store.register_late_pass(|_| Box::new(collection_is_never_read::CollectionIsNeverRead));
+    store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage));
+    store.register_early_pass(|| Box::new(redundant_async_block::RedundantAsyncBlock));
+    store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/manual_bits.rs b/src/tools/clippy/clippy_lints/src/manual_bits.rs
index 462d73cf0b9..bc815dc4a26 100644
--- a/src/tools/clippy/clippy_lints/src/manual_bits.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_bits.rs
@@ -1,11 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::get_parent_expr;
 use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath};
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, Ty};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::sym;
@@ -55,13 +56,17 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits {
         if_chain! {
             if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind;
             if let BinOpKind::Mul = &bin_op.node;
+            if !in_external_macro(cx.sess(), expr.span);
+            let ctxt = expr.span.ctxt();
+            if left_expr.span.ctxt() == ctxt;
+            if right_expr.span.ctxt() == ctxt;
             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 {
                 let mut app = Applicability::MachineApplicable;
-                let ty_snip = snippet_with_applicability(cx, real_ty.span, "..", &mut app);
+                let ty_snip = snippet_with_context(cx, real_ty.span, ctxt, "..", &mut app).0;
                 let sugg = create_sugg(cx, expr, format!("{ty_snip}::BITS"));
 
                 span_lint_and_sugg(
diff --git a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs
index 2fd32c009ea..31264261f5d 100644
--- a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs
@@ -1,5 +1,5 @@
 use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::{diagnostics::span_lint_and_sugg, higher, in_constant, macros::root_macro_call, source::snippet};
+use clippy_utils::{diagnostics::span_lint_and_sugg, higher, in_constant, macros::root_macro_call, sugg::Sugg};
 use rustc_ast::ast::RangeLimits;
 use rustc_ast::LitKind::{Byte, Char};
 use rustc_errors::Applicability;
@@ -115,15 +115,8 @@ fn check_is_ascii(cx: &LateContext<'_>, span: Span, recv: &Expr<'_>, range: &Cha
         CharRange::Otherwise => None,
     } {
         let default_snip = "..";
-        // `snippet_with_applicability` may set applicability to `MaybeIncorrect` for
-        // macro span, so we check applicability manually by comparing `recv` is not default.
-        let recv = snippet(cx, recv.span, default_snip);
-
-        let applicability = if recv == default_snip {
-            Applicability::HasPlaceholders
-        } else {
-            Applicability::MachineApplicable
-        };
+        let mut app = Applicability::MachineApplicable;
+        let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_par();
 
         span_lint_and_sugg(
             cx,
@@ -132,7 +125,7 @@ fn check_is_ascii(cx: &LateContext<'_>, span: Span, recv: &Expr<'_>, range: &Cha
             "manual check for common ascii range",
             "try",
             format!("{recv}.{sugg}()"),
-            applicability,
+            app,
         );
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
index 38f41d077c1..aafee92713f 100644
--- a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
@@ -1,7 +1,7 @@
 use clippy_utils::consts::{constant_full_int, FullInt};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
 use clippy_utils::{in_constant, path_to_local};
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind};
@@ -60,12 +60,16 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
             return;
         }
 
+        // (x % c + c) % c
         if let ExprKind::Binary(op1, expr1, right) = expr.kind
             && op1.node == BinOpKind::Rem
+            && let ctxt = expr.span.ctxt()
+            && expr1.span.ctxt() == ctxt
             && let Some(const1) = check_for_unsigned_int_constant(cx, right)
             && let ExprKind::Binary(op2, left, right) = expr1.kind
             && op2.node == BinOpKind::Add
             && let Some((const2, expr2)) = check_for_either_unsigned_int_constant(cx, left, right)
+            && expr2.span.ctxt() == ctxt
             && let ExprKind::Binary(op3, expr3, right) = expr2.kind
             && op3.node == BinOpKind::Rem
             && let Some(const3) = check_for_unsigned_int_constant(cx, right)
@@ -86,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
                 };
 
                 let mut app = Applicability::MachineApplicable;
-                let rem_of = snippet_with_applicability(cx, expr3.span, "_", &mut app);
+                let rem_of = snippet_with_context(cx, expr3.span, ctxt, "_", &mut app).0;
                 span_lint_and_sugg(
                     cx,
                     MANUAL_REM_EUCLID,
diff --git a/src/tools/clippy/clippy_lints/src/match_result_ok.rs b/src/tools/clippy/clippy_lints/src/match_result_ok.rs
index a020282d234..6ec9784038c 100644
--- a/src/tools/clippy/clippy_lints/src/match_result_ok.rs
+++ b/src/tools/clippy/clippy_lints/src/match_result_ok.rs
@@ -1,11 +1,11 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::higher;
-use clippy_utils::method_chain_args;
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::is_res_lang_ctor;
+use clippy_utils::source::snippet_with_context;
 use clippy_utils::ty::is_type_diagnostic_item;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, PatKind, QPath};
+use rustc_hir::{Expr, ExprKind, LangItem, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::sym;
@@ -58,17 +58,18 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk {
             };
 
         if_chain! {
-            if let ExprKind::MethodCall(ok_path, result_types_0, ..) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
-            if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _)  = let_pat.kind; //get operation
-            if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() method use std::marker::Sized;
-            if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(result_types_0), sym::Result);
-            if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some";
-
+            if let ExprKind::MethodCall(ok_path, recv, [], ..) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
+            if let PatKind::TupleStruct(ref pat_path, [ok_pat], _)  = let_pat.kind; //get operation
+            if ok_path.ident.as_str() == "ok";
+            if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
+            if is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome);
+            let ctxt = expr.span.ctxt();
+            if let_expr.span.ctxt() == ctxt;
+            if let_pat.span.ctxt() == ctxt;
             then {
-
                 let mut applicability = Applicability::MachineApplicable;
-                let some_expr_string = snippet_with_applicability(cx, y[0].span, "", &mut applicability);
-                let trimmed_ok = snippet_with_applicability(cx, let_expr.span.until(ok_path.ident.span), "", &mut applicability);
+                let some_expr_string = snippet_with_context(cx, ok_pat.span, ctxt, "", &mut applicability).0;
+                let trimmed_ok = snippet_with_context(cx, recv.span, ctxt, "", &mut applicability).0;
                 let sugg = format!(
                     "{ifwhile} let Ok({some_expr_string}) = {}",
                     trimmed_ok.trim().trim_end_matches('.'),
diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs
index 7b15a307fec..97ecca450fa 100644
--- a/src/tools/clippy/clippy_lints/src/matches/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs
@@ -925,7 +925,7 @@ declare_clippy_lint! {
     #[clippy::version = "1.66.0"]
     pub MANUAL_FILTER,
     complexity,
-    "reimplentation of `filter`"
+    "reimplementation of `filter`"
 }
 
 #[derive(Default)]
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 702df4b282b..56e3988bf09 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -340,8 +340,9 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for methods with certain name prefixes and which
-    /// doesn't match how self is taken. The actual rules are:
+    /// Checks for methods with certain name prefixes or suffixes, and which
+    /// do not adhere to standard conventions regarding how `self` is taken.
+    /// The actual rules are:
     ///
     /// |Prefix |Postfix     |`self` taken                   | `self` type  |
     /// |-------|------------|-------------------------------|--------------|
diff --git a/src/tools/clippy/clippy_lints/src/missing_assert_message.rs b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs
new file mode 100644
index 00000000000..2214a568d9c
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs
@@ -0,0 +1,82 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::macros::{find_assert_args, find_assert_eq_args, root_macro_call_first_node, PanicExpn};
+use clippy_utils::{is_in_cfg_test, is_in_test_function};
+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
+    /// Checks assertions without a custom panic message.
+    ///
+    /// ### Why is this bad?
+    /// Without a good custom message, it'd be hard to understand what went wrong when the assertion fails.
+    /// A good custom message should be more about why the failure of the assertion is problematic
+    /// and not what is failed because the assertion already conveys that.
+    ///
+    /// ### Known problems
+    /// This lint cannot check the quality of the custom panic messages.
+    /// Hence, you can suppress this lint simply by adding placeholder messages
+    /// like "assertion failed". However, we recommend coming up with good messages
+    /// that provide useful information instead of placeholder messages that
+    /// don't provide any extra information.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # struct Service { ready: bool }
+    /// fn call(service: Service) {
+    ///     assert!(service.ready);
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # struct Service { ready: bool }
+    /// fn call(service: Service) {
+    ///     assert!(service.ready, "`service.poll_ready()` must be called first to ensure that service is ready to receive requests");
+    /// }
+    /// ```
+    #[clippy::version = "1.69.0"]
+    pub MISSING_ASSERT_MESSAGE,
+    restriction,
+    "checks assertions without a custom panic message"
+}
+
+declare_lint_pass!(MissingAssertMessage => [MISSING_ASSERT_MESSAGE]);
+
+impl<'tcx> LateLintPass<'tcx> for MissingAssertMessage {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
+        let single_argument = match cx.tcx.get_diagnostic_name(macro_call.def_id) {
+            Some(sym::assert_macro | sym::debug_assert_macro) => true,
+            Some(
+                sym::assert_eq_macro | sym::assert_ne_macro | sym::debug_assert_eq_macro | sym::debug_assert_ne_macro,
+            ) => false,
+            _ => return,
+        };
+
+        // This lint would be very noisy in tests, so just ignore if we're in test context
+        if is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id) {
+            return;
+        }
+
+        let panic_expn = if single_argument {
+            let Some((_, panic_expn)) = find_assert_args(cx, expr, macro_call.expn) else { return };
+            panic_expn
+        } else {
+            let Some((_, _, panic_expn)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return };
+            panic_expn
+        };
+
+        if let PanicExpn::Empty = panic_expn {
+            span_lint_and_help(
+                cx,
+                MISSING_ASSERT_MESSAGE,
+                macro_call.span,
+                "assert without any message",
+                None,
+                "consider describing why the failing assert is problematic",
+            );
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs
index 5b1f03fc16c..f2773cad400 100644
--- a/src/tools/clippy/clippy_lints/src/missing_doc.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs
@@ -8,10 +8,10 @@
 use clippy_utils::attrs::is_doc_hidden;
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::is_from_proc_macro;
-use hir::def_id::LocalDefId;
 use if_chain::if_chain;
 use rustc_ast::ast::{self, MetaItem, MetaItemKind};
 use rustc_hir as hir;
+use rustc_hir::def_id::LocalDefId;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::ty::Visibility;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -21,8 +21,7 @@ use rustc_span::sym;
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Warns if there is missing doc for any documentable item
-    /// (public or private).
+    /// Warns if there is missing doc for any private documentable item
     ///
     /// ### Why is this bad?
     /// Doc is good. *rustc* has a `MISSING_DOCS`
@@ -32,7 +31,7 @@ declare_clippy_lint! {
     #[clippy::version = "pre 1.29.0"]
     pub MISSING_DOCS_IN_PRIVATE_ITEMS,
     restriction,
-    "detects missing documentation for public and private members"
+    "detects missing documentation for private members"
 }
 
 pub struct MissingDoc {
@@ -107,11 +106,14 @@ impl MissingDoc {
             if vis == Visibility::Public || vis != Visibility::Restricted(CRATE_DEF_ID.into()) {
                 return;
             }
+        } else if def_id != CRATE_DEF_ID && cx.effective_visibilities.is_exported(def_id) {
+            return;
         }
 
         let has_doc = attrs
             .iter()
             .any(|a| a.doc_str().is_some() || Self::has_include(a.meta()));
+
         if !has_doc {
             span_lint(
                 cx,
diff --git a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
index 63c575fca30..5418616ded0 100644
--- a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
+++ b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
@@ -11,6 +11,7 @@ use rustc_ast::Mutability;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::Span;
 
@@ -120,33 +121,15 @@ fn collect_unsafe_exprs<'tcx>(
                 unsafe_ops.push(("raw pointer dereference occurs here", expr.span));
             },
 
-            ExprKind::Call(path_expr, _) => match path_expr.kind {
-                ExprKind::Path(QPath::Resolved(
-                    _,
-                    hir::Path {
-                        res: Res::Def(kind, def_id),
-                        ..
-                    },
-                )) if kind.is_fn_like() => {
-                    let sig = cx.tcx.fn_sig(*def_id);
-                    if sig.0.unsafety() == Unsafety::Unsafe {
-                        unsafe_ops.push(("unsafe function call occurs here", expr.span));
-                    }
-                },
-
-                ExprKind::Path(QPath::TypeRelative(..)) => {
-                    if let Some(sig) = cx
-                        .typeck_results()
-                        .type_dependent_def_id(path_expr.hir_id)
-                        .map(|def_id| cx.tcx.fn_sig(def_id))
-                    {
-                        if sig.0.unsafety() == Unsafety::Unsafe {
-                            unsafe_ops.push(("unsafe function call occurs here", expr.span));
-                        }
-                    }
-                },
-
-                _ => {},
+            ExprKind::Call(path_expr, _) => {
+                let sig = match *cx.typeck_results().expr_ty(path_expr).kind() {
+                    ty::FnDef(id, _) => cx.tcx.fn_sig(id).skip_binder(),
+                    ty::FnPtr(sig) => sig,
+                    _ => return Continue(Descend::Yes),
+                };
+                if sig.unsafety() == Unsafety::Unsafe {
+                    unsafe_ops.push(("unsafe function call occurs here", expr.span));
+                }
             },
 
             ExprKind::MethodCall(..) => {
diff --git a/src/tools/clippy/clippy_lints/src/neg_multiply.rs b/src/tools/clippy/clippy_lints/src/neg_multiply.rs
index fb9a4abd0b4..ed3e2c6e7f4 100644
--- a/src/tools/clippy/clippy_lints/src/neg_multiply.rs
+++ b/src/tools/clippy/clippy_lints/src/neg_multiply.rs
@@ -1,6 +1,6 @@
 use clippy_utils::consts::{self, Constant};
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
 use clippy_utils::sugg::has_enclosing_paren;
 use if_chain::if_chain;
 use rustc_ast::util::parser::PREC_PREFIX;
@@ -60,8 +60,8 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) {
 
         then {
             let mut applicability = Applicability::MachineApplicable;
-            let snip = snippet_with_applicability(cx, exp.span, "..", &mut applicability);
-            let suggestion = if exp.precedence().order() < PREC_PREFIX && !has_enclosing_paren(&snip) {
+            let (snip, from_macro) = snippet_with_context(cx, exp.span, span.ctxt(), "..", &mut applicability);
+            let suggestion = if !from_macro && exp.precedence().order() < PREC_PREFIX && !has_enclosing_paren(&snip) {
                 format!("-({snip})")
             } else {
                 format!("-{snip}")
diff --git a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs
index 2ecb0487484..e1de494eb41 100644
--- a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs
+++ b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs
@@ -53,6 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
                             || is_type_diagnostic_item(cx, obj_ty, sym::DirBuilder)))
                         || (path.ident.name == sym!(set_mode) && match_type(cx, obj_ty, &paths::PERMISSIONS));
                     if let ExprKind::Lit(_) = param.kind;
+                    if param.span.ctxt() == expr.span.ctxt();
 
                     then {
                         let Some(snip) = snippet_opt(cx, param.span) else {
@@ -71,6 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
                     if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id();
                     if match_def_path(cx, def_id, &paths::PERMISSIONS_FROM_MODE);
                     if let ExprKind::Lit(_) = param.kind;
+                    if param.span.ctxt() == expr.span.ctxt();
                     if let Some(snip) = snippet_opt(cx, param.span);
                     if !snip.starts_with("0o");
                     then {
diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
index 87a8a2ed12b..25e8de94863 100644
--- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
@@ -143,6 +143,10 @@ impl ArithmeticSideEffects {
             return;
         }
         let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) {
+            if let hir::BinOpKind::Shl | hir::BinOpKind::Shr = op.node {
+                // At least for integers, shifts are already handled by the CTFE
+                return;
+            }
             let (actual_lhs, lhs_ref_counter) = peel_hir_expr_refs(lhs);
             let (actual_rhs, rhs_ref_counter) = peel_hir_expr_refs(rhs);
             match (
@@ -151,10 +155,13 @@ impl ArithmeticSideEffects {
             ) {
                 (None, None) => false,
                 (None, Some(n)) | (Some(n), None) => match (&op.node, n) {
-                    (hir::BinOpKind::Div | hir::BinOpKind::Rem, 0) => false,
+                    // Division and module are always valid if applied to non-zero integers
+                    (hir::BinOpKind::Div | hir::BinOpKind::Rem, local_n) if local_n != 0 => true,
+                    // Addition or subtracting zeros is always a no-op
                     (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0)
-                    | (hir::BinOpKind::Div | hir::BinOpKind::Rem, _)
-                    | (hir::BinOpKind::Mul, 0 | 1) => true,
+                    // Multiplication by 1 or 0 will never overflow
+                    | (hir::BinOpKind::Mul, 0 | 1)
+                    => true,
                     _ => false,
                 },
                 (Some(_), Some(_)) => {
diff --git a/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs b/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs
index e7095ec191f..664d44d6504 100644
--- a/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs
+++ b/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs
@@ -21,7 +21,7 @@ declare_clippy_lint! {
     /// let mut permissions = metadata.permissions();
     /// permissions.set_readonly(false);
     /// ```
-    #[clippy::version = "1.66.0"]
+    #[clippy::version = "1.68.0"]
     pub PERMISSIONS_SET_READONLY_FALSE,
     suspicious,
     "Checks for calls to `std::fs::Permissions.set_readonly` with argument `false`"
diff --git a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
new file mode 100644
index 00000000000..27ad4308637
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
@@ -0,0 +1,84 @@
+use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet};
+use rustc_ast::ast::*;
+use rustc_ast::visit::Visitor as AstVisitor;
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for `async` block that only returns `await` on a future.
+    ///
+    /// ### Why is this bad?
+    /// It is simpler and more efficient to use the future directly.
+    ///
+    /// ### Example
+    /// ```rust
+    /// async fn f() -> i32 {
+    ///     1 + 2
+    /// }
+    ///
+    /// let fut = async {
+    ///     f().await
+    /// };
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// async fn f() -> i32 {
+    ///     1 + 2
+    /// }
+    ///
+    /// let fut = f();
+    /// ```
+    #[clippy::version = "1.69.0"]
+    pub REDUNDANT_ASYNC_BLOCK,
+    complexity,
+    "`async { future.await }` can be replaced by `future`"
+}
+declare_lint_pass!(RedundantAsyncBlock => [REDUNDANT_ASYNC_BLOCK]);
+
+impl EarlyLintPass for RedundantAsyncBlock {
+    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
+        if expr.span.from_expansion() {
+            return;
+        }
+        if let ExprKind::Async(_, _, block) = &expr.kind && block.stmts.len() == 1 &&
+            let Some(Stmt { kind: StmtKind::Expr(last), .. }) = block.stmts.last() &&
+            let ExprKind::Await(future) = &last.kind &&
+            !future.span.from_expansion() &&
+            !await_in_expr(future)
+        {
+            span_lint_and_sugg(
+                cx,
+                REDUNDANT_ASYNC_BLOCK,
+                expr.span,
+                "this async expression only awaits a single future",
+                "you can reduce it to",
+                snippet(cx, future.span, "..").into_owned(),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+}
+
+/// Check whether an expression contains `.await`
+fn await_in_expr(expr: &Expr) -> bool {
+    let mut detector = AwaitDetector::default();
+    detector.visit_expr(expr);
+    detector.await_found
+}
+
+#[derive(Default)]
+struct AwaitDetector {
+    await_found: bool,
+}
+
+impl<'ast> AstVisitor<'ast> for AwaitDetector {
+    fn visit_expr(&mut self, ex: &'ast Expr) {
+        match (&ex.kind, self.await_found) {
+            (ExprKind::Await(_), _) => self.await_found = true,
+            (_, false) => rustc_ast::visit::walk_expr(self, ex),
+            _ => (),
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/size_of_ref.rs b/src/tools/clippy/clippy_lints/src/size_of_ref.rs
index 3fcdb4288ce..8abec06c641 100644
--- a/src/tools/clippy/clippy_lints/src/size_of_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/size_of_ref.rs
@@ -45,7 +45,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
-    #[clippy::version = "1.67.0"]
+    #[clippy::version = "1.68.0"]
     pub SIZE_OF_REF,
     suspicious,
     "Argument to `std::mem::size_of_val()` is a double-reference, which is almost certainly unintended"
diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs
index 0f062cecf88..1aeac724ab1 100644
--- a/src/tools/clippy/clippy_lints/src/swap.rs
+++ b/src/tools/clippy/clippy_lints/src/swap.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{can_mut_borrow_both, eq_expr_value, in_constant, std_or_core};
@@ -10,6 +10,7 @@ use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Spanned;
+use rustc_span::SyntaxContext;
 use rustc_span::{sym, symbol::Ident, Span};
 
 declare_clippy_lint! {
@@ -80,43 +81,45 @@ impl<'tcx> LateLintPass<'tcx> for Swap {
 }
 
 fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, span: Span, is_xor_based: bool) {
+    let ctxt = span.ctxt();
     let mut applicability = Applicability::MachineApplicable;
 
     if !can_mut_borrow_both(cx, e1, e2) {
-        if let ExprKind::Index(lhs1, idx1) = e1.kind {
-            if let ExprKind::Index(lhs2, idx2) = e2.kind {
-                if eq_expr_value(cx, lhs1, lhs2) {
-                    let ty = cx.typeck_results().expr_ty(lhs1).peel_refs();
+        if let ExprKind::Index(lhs1, idx1) = e1.kind
+            && let ExprKind::Index(lhs2, idx2) = e2.kind
+            && eq_expr_value(cx, lhs1, lhs2)
+            && e1.span.ctxt() == ctxt
+            && e2.span.ctxt() == ctxt
+        {
+            let ty = cx.typeck_results().expr_ty(lhs1).peel_refs();
 
-                    if matches!(ty.kind(), ty::Slice(_))
-                        || matches!(ty.kind(), ty::Array(_, _))
-                        || is_type_diagnostic_item(cx, ty, sym::Vec)
-                        || is_type_diagnostic_item(cx, ty, sym::VecDeque)
-                    {
-                        let slice = Sugg::hir_with_applicability(cx, lhs1, "<slice>", &mut applicability);
-                        span_lint_and_sugg(
-                            cx,
-                            MANUAL_SWAP,
-                            span,
-                            &format!("this looks like you are swapping elements of `{slice}` manually"),
-                            "try",
-                            format!(
-                                "{}.swap({}, {})",
-                                slice.maybe_par(),
-                                snippet_with_applicability(cx, idx1.span, "..", &mut applicability),
-                                snippet_with_applicability(cx, idx2.span, "..", &mut applicability),
-                            ),
-                            applicability,
-                        );
-                    }
-                }
+            if matches!(ty.kind(), ty::Slice(_))
+                || matches!(ty.kind(), ty::Array(_, _))
+                || is_type_diagnostic_item(cx, ty, sym::Vec)
+                || is_type_diagnostic_item(cx, ty, sym::VecDeque)
+            {
+                let slice = Sugg::hir_with_applicability(cx, lhs1, "<slice>", &mut applicability);
+                span_lint_and_sugg(
+                    cx,
+                    MANUAL_SWAP,
+                    span,
+                    &format!("this looks like you are swapping elements of `{slice}` manually"),
+                    "try",
+                    format!(
+                        "{}.swap({}, {});",
+                        slice.maybe_par(),
+                        snippet_with_context(cx, idx1.span, ctxt, "..", &mut applicability).0,
+                        snippet_with_context(cx, idx2.span, ctxt, "..", &mut applicability).0,
+                    ),
+                    applicability,
+                );
             }
         }
         return;
     }
 
-    let first = Sugg::hir_with_applicability(cx, e1, "..", &mut applicability);
-    let second = Sugg::hir_with_applicability(cx, e2, "..", &mut applicability);
+    let first = Sugg::hir_with_context(cx, e1, ctxt, "..", &mut applicability);
+    let second = Sugg::hir_with_context(cx, e2, ctxt, "..", &mut applicability);
     let Some(sugg) = std_or_core(cx) else { return };
 
     span_lint_and_then(
@@ -128,7 +131,7 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa
             diag.span_suggestion(
                 span,
                 "try",
-                format!("{sugg}::mem::swap({}, {})", first.mut_addr(), second.mut_addr()),
+                format!("{sugg}::mem::swap({}, {});", first.mut_addr(), second.mut_addr()),
                 applicability,
             );
             if !is_xor_based {
@@ -144,19 +147,19 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) {
         return;
     }
 
-    for w in block.stmts.windows(3) {
+    for [s1, s2, s3] in block.stmts.array_windows::<3>() {
         if_chain! {
             // let t = foo();
-            if let StmtKind::Local(tmp) = w[0].kind;
+            if let StmtKind::Local(tmp) = s1.kind;
             if let Some(tmp_init) = tmp.init;
             if let PatKind::Binding(.., ident, None) = tmp.pat.kind;
 
             // foo() = bar();
-            if let StmtKind::Semi(first) = w[1].kind;
+            if let StmtKind::Semi(first) = s2.kind;
             if let ExprKind::Assign(lhs1, rhs1, _) = first.kind;
 
             // bar() = t;
-            if let StmtKind::Semi(second) = w[2].kind;
+            if let StmtKind::Semi(second) = s3.kind;
             if let ExprKind::Assign(lhs2, rhs2, _) = second.kind;
             if let ExprKind::Path(QPath::Resolved(None, rhs2)) = rhs2.kind;
             if rhs2.segments.len() == 1;
@@ -164,8 +167,15 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) {
             if ident.name == rhs2.segments[0].ident.name;
             if eq_expr_value(cx, tmp_init, lhs1);
             if eq_expr_value(cx, rhs1, lhs2);
+
+            let ctxt = s1.span.ctxt();
+            if s2.span.ctxt() == ctxt;
+            if s3.span.ctxt() == ctxt;
+            if first.span.ctxt() == ctxt;
+            if second.span.ctxt() == ctxt;
+
             then {
-                let span = w[0].span.to(second.span);
+                let span = s1.span.to(s3.span);
                 generate_swap_warning(cx, lhs1, lhs2, span, false);
             }
         }
@@ -246,17 +256,20 @@ fn parse<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(ExprOrIdent<'hir>, &'a Expr<
 
 /// Implementation of the xor case for `MANUAL_SWAP` lint.
 fn check_xor_swap(cx: &LateContext<'_>, block: &Block<'_>) {
-    for window in block.stmts.windows(3) {
+    for [s1, s2, s3] in block.stmts.array_windows::<3>() {
         if_chain! {
-            if let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(&window[0]);
-            if let Some((lhs1, rhs1)) = extract_sides_of_xor_assign(&window[1]);
-            if let Some((lhs2, rhs2)) = extract_sides_of_xor_assign(&window[2]);
+            let ctxt = s1.span.ctxt();
+            if let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(s1, ctxt);
+            if let Some((lhs1, rhs1)) = extract_sides_of_xor_assign(s2, ctxt);
+            if let Some((lhs2, rhs2)) = extract_sides_of_xor_assign(s3, ctxt);
             if eq_expr_value(cx, lhs0, rhs1);
             if eq_expr_value(cx, lhs2, rhs1);
             if eq_expr_value(cx, lhs1, rhs0);
             if eq_expr_value(cx, lhs1, rhs2);
+            if s2.span.ctxt() == ctxt;
+            if s3.span.ctxt() == ctxt;
             then {
-                let span = window[0].span.to(window[2].span);
+                let span = s1.span.to(s3.span);
                 generate_swap_warning(cx, lhs0, rhs0, span, true);
             }
         };
@@ -264,9 +277,12 @@ fn check_xor_swap(cx: &LateContext<'_>, block: &Block<'_>) {
 }
 
 /// Returns the lhs and rhs of an xor assignment statement.
-fn extract_sides_of_xor_assign<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(&'a Expr<'hir>, &'a Expr<'hir>)> {
-    if let StmtKind::Semi(expr) = stmt.kind {
-        if let ExprKind::AssignOp(
+fn extract_sides_of_xor_assign<'a, 'hir>(
+    stmt: &'a Stmt<'hir>,
+    ctxt: SyntaxContext,
+) -> Option<(&'a Expr<'hir>, &'a Expr<'hir>)> {
+    if let StmtKind::Semi(expr) = stmt.kind
+        && let ExprKind::AssignOp(
             Spanned {
                 node: BinOpKind::BitXor,
                 ..
@@ -274,9 +290,10 @@ fn extract_sides_of_xor_assign<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(&'a Ex
             lhs,
             rhs,
         ) = expr.kind
-        {
-            return Some((lhs, rhs));
-        }
+        && expr.span.ctxt() == ctxt
+    {
+        Some((lhs, rhs))
+    } else {
+        None
     }
-    None
 }
diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs
index c01cbe5090f..0dc30f7a935 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs
@@ -458,7 +458,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let null_fn: Option<fn()> = None;
     /// ```
-    #[clippy::version = "1.67.0"]
+    #[clippy::version = "1.68.0"]
     pub TRANSMUTE_NULL_TO_FN,
     correctness,
     "transmute results in a null function pointer, which is undefined behavior"
diff --git a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
index d6167a62169..3430b6e3734 100644
--- a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
+++ b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
@@ -5,7 +5,7 @@ use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source};
 use core::ops::ControlFlow;
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, Local, Node, PatKind, QPath, TyKind};
+use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, Local, MatchSource, Node, PatKind, QPath, TyKind};
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty;
@@ -41,6 +41,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
                 );
             }
         } else {
+            if let ExprKind::Match(_, _, MatchSource::AwaitDesugar) = init.kind {
+                return
+            }
+
             span_lint_and_then(
                 cx,
                 LET_UNIT_VALUE,
diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs
index e7c54000684..8ea5475fb25 100644
--- a/src/tools/clippy/clippy_lints/src/use_self.rs
+++ b/src/tools/clippy/clippy_lints/src/use_self.rs
@@ -10,8 +10,8 @@ use rustc_hir::{
     def::{CtorOf, DefKind, Res},
     def_id::LocalDefId,
     intravisit::{walk_inf, walk_ty, Visitor},
-    Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath,
-    TyKind,
+    Expr, ExprKind, FnRetTy, FnSig, GenericArg, GenericParam, GenericParamKind, HirId, Impl, ImplItemKind, Item,
+    ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind,
 };
 use rustc_hir_analysis::hir_ty_to_ty;
 use rustc_lint::{LateContext, LateLintPass};
@@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
         // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that
         // we're in an `impl` or nested item, that we don't want to lint
         let stack_item = if_chain! {
-            if let ItemKind::Impl(Impl { self_ty, .. }) = item.kind;
+            if let ItemKind::Impl(Impl { self_ty, generics,.. }) = item.kind;
             if let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind;
             let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args;
             if parameters.as_ref().map_or(true, |params| {
@@ -105,10 +105,17 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
             if !item.span.from_expansion();
             if !is_from_proc_macro(cx, item); // expensive, should be last check
             then {
+                // Self cannot be used inside const generic parameters
+                let types_to_skip = generics.params.iter().filter_map(|param| {
+                    match param {
+                        GenericParam { kind: GenericParamKind::Const { ty: Ty { hir_id, ..}, ..}, ..} => Some(*hir_id),
+                        _ => None,
+                    }
+                }).chain(std::iter::once(self_ty.hir_id)).collect();
                 StackItem::Check {
                     impl_id: item.owner_id.def_id,
                     in_body: 0,
-                    types_to_skip: std::iter::once(self_ty.hir_id).collect(),
+                    types_to_skip,
                 }
             } else {
                 StackItem::NoCheck
diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs
index c37e5bb6716..f31c3fdb095 100644
--- a/src/tools/clippy/clippy_lints/src/utils/author.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/author.rs
@@ -588,7 +588,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
                     },
                 }
             },
-            ExprKind::Err(_) => kind!("Err"),
+            ExprKind::Err(_) => kind!("Err(_)"),
             ExprKind::DropTemps(expr) => {
                 bind!(self, expr);
                 kind!("DropTemps({expr})");
diff --git a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
new file mode 100644
index 00000000000..be56b842b98
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
@@ -0,0 +1,23 @@
+use clippy_utils::macros::collect_ast_format_args;
+use rustc_ast::{Expr, ExprKind};
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Collects [`rustc_ast::FormatArgs`] so that future late passes can call
+    /// [`clippy_utils::macros::find_format_args`]
+    pub FORMAT_ARGS_COLLECTOR,
+    internal_warn,
+    "collects `format_args` AST nodes for use in later lints"
+}
+
+declare_lint_pass!(FormatArgsCollector => [FORMAT_ARGS_COLLECTOR]);
+
+impl EarlyLintPass for FormatArgsCollector {
+    fn check_expr(&mut self, _: &EarlyContext<'_>, expr: &Expr) {
+        if let ExprKind::FormatArgs(args) = &expr.kind {
+            collect_ast_format_args(expr.span, args);
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
index b1b5164ffb3..3d0d4a52511 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
@@ -26,7 +26,7 @@ use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::symbol::Ident;
 use rustc_span::{sym, Loc, Span, Symbol};
 use serde::{ser::SerializeStruct, Serialize, Serializer};
-use std::collections::BinaryHeap;
+use std::collections::{BTreeSet, BinaryHeap};
 use std::fmt;
 use std::fmt::Write as _;
 use std::fs::{self, OpenOptions};
@@ -264,6 +264,9 @@ struct LintMetadata {
     /// This field is only used in the output and will only be
     /// mapped shortly before the actual output.
     applicability: Option<ApplicabilityInfo>,
+    /// All the past names of lints which have been renamed.
+    #[serde(skip_serializing_if = "BTreeSet::is_empty")]
+    former_ids: BTreeSet<String>,
 }
 
 impl LintMetadata {
@@ -283,6 +286,7 @@ impl LintMetadata {
             version,
             docs,
             applicability: None,
+            former_ids: BTreeSet::new(),
         }
     }
 }
@@ -901,6 +905,7 @@ fn collect_renames(lints: &mut Vec<LintMetadata>) {
                         if name == lint_name;
                         if let Some(past_name) = k.strip_prefix(CLIPPY_LINT_GROUP_PREFIX);
                         then {
+                            lint.former_ids.insert(past_name.to_owned());
                             writeln!(collected, "* `{past_name}`").unwrap();
                             names.push(past_name.to_string());
                         }
diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs
index 787e9fd982c..dc647af264c 100644
--- a/src/tools/clippy/clippy_lints/src/utils/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs
@@ -1,5 +1,6 @@
 pub mod author;
 pub mod conf;
 pub mod dump_hir;
+pub mod format_args_collector;
 #[cfg(feature = "internal")]
 pub mod internal_lints;
diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs
index df335038881..8114a8463fa 100644
--- a/src/tools/clippy/clippy_lints/src/write.rs
+++ b/src/tools/clippy/clippy_lints/src/write.rs
@@ -1,10 +1,11 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
-use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn, MacroCall};
+use clippy_utils::macros::{find_format_args, format_arg_removal_span, root_macro_call_first_node, MacroCall};
 use clippy_utils::source::{expand_past_previous_comma, snippet_opt};
 use clippy_utils::{is_in_cfg_test, is_in_test_function};
-use rustc_ast::LitKind;
+use rustc_ast::token::LitKind;
+use rustc_ast::{FormatArgPosition, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder, FormatTrait};
 use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, HirIdMap, Impl, Item, ItemKind};
+use rustc_hir::{Expr, Impl, Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{sym, BytePos};
@@ -297,34 +298,40 @@ impl<'tcx> LateLintPass<'tcx> for Write {
             _ => return,
         }
 
-        let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, macro_call.expn) else { return };
-
-        // ignore `writeln!(w)` and `write!(v, some_macro!())`
-        if format_args.format_string.span.from_expansion() {
-            return;
-        }
+        find_format_args(cx, expr, macro_call.expn, |format_args| {
+            // ignore `writeln!(w)` and `write!(v, some_macro!())`
+            if format_args.span.from_expansion() {
+                return;
+            }
 
-        match diag_name {
-            sym::print_macro | sym::eprint_macro | sym::write_macro => {
-                check_newline(cx, &format_args, &macro_call, name);
-            },
-            sym::println_macro | sym::eprintln_macro | sym::writeln_macro => {
-                check_empty_string(cx, &format_args, &macro_call, name);
-            },
-            _ => {},
-        }
+            match diag_name {
+                sym::print_macro | sym::eprint_macro | sym::write_macro => {
+                    check_newline(cx, format_args, &macro_call, name);
+                },
+                sym::println_macro | sym::eprintln_macro | sym::writeln_macro => {
+                    check_empty_string(cx, format_args, &macro_call, name);
+                },
+                _ => {},
+            }
 
-        check_literal(cx, &format_args, name);
+            check_literal(cx, format_args, name);
 
-        if !self.in_debug_impl {
-            for arg in &format_args.args {
-                if arg.format.r#trait == sym::Debug {
-                    span_lint(cx, USE_DEBUG, arg.span, "use of `Debug`-based formatting");
+            if !self.in_debug_impl {
+                for piece in &format_args.template {
+                    if let &FormatArgsPiece::Placeholder(FormatPlaceholder {
+                        span: Some(span),
+                        format_trait: FormatTrait::Debug,
+                        ..
+                    }) = piece
+                    {
+                        span_lint(cx, USE_DEBUG, span, "use of `Debug`-based formatting");
+                    }
                 }
             }
-        }
+        });
     }
 }
+
 fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
     if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), .. }) = &item.kind
         && let Some(trait_id) = trait_ref.trait_def_id()
@@ -335,16 +342,18 @@ fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
     }
 }
 
-fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_call: &MacroCall, name: &str) {
-    let format_string_parts = &format_args.format_string.parts;
-    let mut format_string_span = format_args.format_string.span;
-
-    let Some(last) = format_string_parts.last() else { return };
+fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) {
+    let Some(FormatArgsPiece::Literal(last)) = format_args.template.last() else { return };
 
     let count_vertical_whitespace = || {
-        format_string_parts
+        format_args
+            .template
             .iter()
-            .flat_map(|part| part.as_str().chars())
+            .filter_map(|piece| match piece {
+                FormatArgsPiece::Literal(literal) => Some(literal),
+                FormatArgsPiece::Placeholder(_) => None,
+            })
+            .flat_map(|literal| literal.as_str().chars())
             .filter(|ch| matches!(ch, '\r' | '\n'))
             .count()
     };
@@ -352,10 +361,9 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c
     if last.as_str().ends_with('\n')
         // ignore format strings with other internal vertical whitespace
         && count_vertical_whitespace() == 1
-
-        // ignore trailing arguments: `print!("Issue\n{}", 1265);`
-        && format_string_parts.len() > format_args.args.len()
     {
+        let mut format_string_span = format_args.span;
+
         let lint = if name == "write" {
             format_string_span = expand_past_previous_comma(cx, format_string_span);
 
@@ -373,7 +381,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c
                 let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!');
                 let Some(format_snippet) = snippet_opt(cx, format_string_span) else { return };
 
-                if format_string_parts.len() == 1 && last.as_str() == "\n" {
+                if format_args.template.len() == 1 && last.as_str() == "\n" {
                     // print!("\n"), write!(f, "\n")
 
                     diag.multipart_suggestion(
@@ -398,11 +406,12 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c
     }
 }
 
-fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_call: &MacroCall, name: &str) {
-    if let [part] = &format_args.format_string.parts[..]
-        && let mut span = format_args.format_string.span
-        && part.as_str() == "\n"
+fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) {
+    if let [FormatArgsPiece::Literal(literal)] = &format_args.template[..]
+        && literal.as_str() == "\n"
     {
+        let mut span = format_args.span;
+
         let lint = if name == "writeln" {
             span = expand_past_previous_comma(cx, span);
 
@@ -428,33 +437,43 @@ fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, ma
     }
 }
 
-fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: &str) {
-    let mut counts = HirIdMap::<usize>::default();
-    for param in format_args.params() {
-        *counts.entry(param.value.hir_id).or_default() += 1;
+fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
+    let arg_index = |argument: &FormatArgPosition| argument.index.unwrap_or_else(|pos| pos);
+
+    let mut counts = vec![0u32; format_args.arguments.all_args().len()];
+    for piece in &format_args.template {
+        if let FormatArgsPiece::Placeholder(placeholder) = piece {
+            counts[arg_index(&placeholder.argument)] += 1;
+        }
     }
 
-    for arg in &format_args.args {
-        let value = arg.param.value;
-
-        if counts[&value.hir_id] == 1
-            && arg.format.is_default()
-            && let ExprKind::Lit(lit) = &value.kind
-            && !value.span.from_expansion()
-            && let Some(value_string) = snippet_opt(cx, value.span)
-        {
-            let (replacement, replace_raw) = match lit.node {
-                LitKind::Str(..) => extract_str_literal(&value_string),
-                LitKind::Char(ch) => (
-                    match ch {
-                        '"' => "\\\"",
-                        '\'' => "'",
+    for piece in &format_args.template {
+        if let FormatArgsPiece::Placeholder(FormatPlaceholder {
+            argument,
+            span: Some(placeholder_span),
+            format_trait: FormatTrait::Display,
+            format_options,
+        }) = piece
+            && *format_options == FormatOptions::default()
+            && let index = arg_index(argument)
+            && counts[index] == 1
+            && let Some(arg) = format_args.arguments.by_index(index)
+            && let rustc_ast::ExprKind::Lit(lit) = &arg.expr.kind
+            && !arg.expr.span.from_expansion()
+            && let Some(value_string) = snippet_opt(cx, arg.expr.span)
+    {
+            let (replacement, replace_raw) = match lit.kind {
+                LitKind::Str | LitKind::StrRaw(_)  => extract_str_literal(&value_string),
+                LitKind::Char => (
+                    match lit.symbol.as_str() {
+                        "\"" => "\\\"",
+                        "\\'" => "'",
                         _ => &value_string[1..value_string.len() - 1],
                     }
                     .to_string(),
                     false,
                 ),
-                LitKind::Bool(b) => (b.to_string(), false),
+                LitKind::Bool => (lit.symbol.to_string(), false),
                 _ => continue,
             };
 
@@ -464,7 +483,9 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: &
                 PRINT_LITERAL
             };
 
-            let format_string_is_raw = format_args.format_string.style.is_some();
+            let Some(format_string_snippet) = snippet_opt(cx, format_args.span) else { continue };
+            let format_string_is_raw = format_string_snippet.starts_with('r');
+
             let replacement = match (format_string_is_raw, replace_raw) {
                 (false, false) => Some(replacement),
                 (false, true) => Some(replacement.replace('"', "\\\"").replace('\\', "\\\\")),
@@ -485,23 +506,24 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: &
             span_lint_and_then(
                 cx,
                 lint,
-                value.span,
+                arg.expr.span,
                 "literal with an empty format string",
                 |diag| {
                     if let Some(replacement) = replacement
                         // `format!("{}", "a")`, `format!("{named}", named = "b")
                         //              ~~~~~                      ~~~~~~~~~~~~~
-                        && let Some(value_span) = format_args.value_with_prev_comma_span(value.hir_id)
+                        && let Some(removal_span) = format_arg_removal_span(format_args, index)
                     {
                         let replacement = replacement.replace('{', "{{").replace('}', "}}");
                         diag.multipart_suggestion(
                             "try this",
-                            vec![(arg.span, replacement), (value_span, String::new())],
+                            vec![(*placeholder_span, replacement), (removal_span, String::new())],
                             Applicability::MachineApplicable,
                         );
                     }
                 },
             );
+
         }
     }
 }
diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml
index 173469f6cdc..124ebd164e6 100644
--- a/src/tools/clippy/clippy_utils/Cargo.toml
+++ b/src/tools/clippy/clippy_utils/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_utils"
-version = "0.1.69"
+version = "0.1.70"
 edition = "2021"
 publish = false
 
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index bcfedd07ed1..44b6b9f7b0b 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -617,7 +617,7 @@ fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Re
 /// Can return multiple resolutions when there are multiple versions of the same crate, e.g.
 /// `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0.
 ///
-/// Also returns multiple results when there are mulitple paths under the same name e.g. `std::vec`
+/// Also returns multiple results when there are multiple paths under the same name e.g. `std::vec`
 /// would have both a [`DefKind::Mod`] and [`DefKind::Macro`].
 ///
 /// This function is expensive and should be used sparingly.
diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs
index be6133d3202..e135bd9feee 100644
--- a/src/tools/clippy/clippy_utils/src/macros.rs
+++ b/src/tools/clippy/clippy_utils/src/macros.rs
@@ -6,6 +6,8 @@ use crate::visitors::{for_each_expr, Descend};
 use arrayvec::ArrayVec;
 use itertools::{izip, Either, Itertools};
 use rustc_ast::ast::LitKind;
+use rustc_ast::FormatArgs;
+use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::intravisit::{walk_expr, Visitor};
 use rustc_hir::{self as hir, Expr, ExprField, ExprKind, HirId, LangItem, Node, QPath, TyKind};
 use rustc_lexer::unescape::unescape_literal;
@@ -15,8 +17,10 @@ use rustc_parse_format::{self as rpf, Alignment};
 use rustc_span::def_id::DefId;
 use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
 use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Pos, Span, SpanData, Symbol};
+use std::cell::RefCell;
 use std::iter::{once, zip};
 use std::ops::ControlFlow;
+use std::sync::atomic::{AtomicBool, Ordering};
 
 const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[
     sym::assert_eq_macro,
@@ -213,6 +217,7 @@ pub fn is_assert_macro(cx: &LateContext<'_>, def_id: DefId) -> bool {
     matches!(name, sym::assert_macro | sym::debug_assert_macro)
 }
 
+#[derive(Debug)]
 pub enum PanicExpn<'a> {
     /// No arguments - `panic!()`
     Empty,
@@ -226,10 +231,7 @@ pub enum PanicExpn<'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::Call(callee, [arg, rest @ ..]) = &expr.kind else { return None };
         let ExprKind::Path(QPath::Resolved(_, path)) = &callee.kind else { return None };
         let result = match path.segments.last().unwrap().ident.as_str() {
             "panic" if arg.span.ctxt() == expr.span.ctxt() => Self::Empty,
@@ -239,6 +241,21 @@ impl<'a> PanicExpn<'a> {
                 Self::Display(e)
             },
             "panic_fmt" => Self::Format(FormatArgsExpn::parse(cx, arg)?),
+            // Since Rust 1.52, `assert_{eq,ne}` macros expand to use:
+            // `core::panicking::assert_failed(.., left_val, right_val, None | Some(format_args!(..)));`
+            "assert_failed" => {
+                // It should have 4 arguments in total (we already matched with the first argument,
+                // so we're just checking for 3)
+                if rest.len() != 3 {
+                    return None;
+                }
+                // `msg_arg` is either `None` (no custom message) or `Some(format_args!(..))` (custom message)
+                let msg_arg = &rest[2];
+                match msg_arg.kind {
+                    ExprKind::Call(_, [fmt_arg]) => Self::Format(FormatArgsExpn::parse(cx, fmt_arg)?),
+                    _ => Self::Empty,
+                }
+            },
             _ => return None,
         };
         Some(result)
@@ -251,7 +268,17 @@ pub fn find_assert_args<'a>(
     expr: &'a Expr<'a>,
     expn: ExpnId,
 ) -> Option<(&'a Expr<'a>, PanicExpn<'a>)> {
-    find_assert_args_inner(cx, expr, expn).map(|([e], p)| (e, p))
+    find_assert_args_inner(cx, expr, expn).map(|([e], mut p)| {
+        // `assert!(..)` expands to `core::panicking::panic("assertion failed: ...")` (which we map to
+        // `PanicExpn::Str(..)`) and `assert!(.., "..")` expands to
+        // `core::panicking::panic_fmt(format_args!(".."))` (which we map to `PanicExpn::Format(..)`).
+        // So even we got `PanicExpn::Str(..)` that means there is no custom message provided
+        if let PanicExpn::Str(_) = p {
+            p = PanicExpn::Empty;
+        }
+
+        (e, p)
+    })
 }
 
 /// Finds the arguments of an `assert_eq!` or `debug_assert_eq!` macro call within the macro
@@ -275,13 +302,12 @@ fn find_assert_args_inner<'a, const N: usize>(
         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;
-    let _: Option<!> = for_each_expr(expr, |e| {
+    let panic_expn = for_each_expr(expr, |e| {
         if args.is_full() {
-            if panic_expn.is_none() && e.span.ctxt() != expr.span.ctxt() {
-                panic_expn = PanicExpn::parse(cx, e);
+            match PanicExpn::parse(cx, e) {
+                Some(expn) => ControlFlow::Break(expn),
+                None => ControlFlow::Continue(Descend::Yes),
             }
-            ControlFlow::Continue(Descend::from(panic_expn.is_none()))
         } else if is_assert_arg(cx, e, expn) {
             args.push(e);
             ControlFlow::Continue(Descend::No)
@@ -339,6 +365,77 @@ fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) ->
     }
 }
 
+thread_local! {
+    /// We preserve the [`FormatArgs`] structs from the early pass for use in the late pass to be
+    /// able to access the many features of a [`LateContext`].
+    ///
+    /// A thread local is used because [`FormatArgs`] is `!Send` and `!Sync`, we are making an
+    /// assumption that the early pass the populates the map and the later late passes will all be
+    /// running on the same thread.
+    static AST_FORMAT_ARGS: RefCell<FxHashMap<Span, FormatArgs>> = {
+        static CALLED: AtomicBool = AtomicBool::new(false);
+        debug_assert!(
+            !CALLED.swap(true, Ordering::SeqCst),
+            "incorrect assumption: `AST_FORMAT_ARGS` should only be accessed by a single thread",
+        );
+
+        RefCell::default()
+    };
+}
+
+/// Record [`rustc_ast::FormatArgs`] for use in late lint passes, this should only be called by
+/// `FormatArgsCollector`
+pub fn collect_ast_format_args(span: Span, format_args: &FormatArgs) {
+    AST_FORMAT_ARGS.with(|ast_format_args| {
+        ast_format_args.borrow_mut().insert(span, format_args.clone());
+    });
+}
+
+/// Calls `callback` with an AST [`FormatArgs`] node if one is found
+pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId, callback: impl FnOnce(&FormatArgs)) {
+    let format_args_expr = for_each_expr(start, |expr| {
+        let ctxt = expr.span.ctxt();
+        if ctxt == start.span.ctxt() {
+            ControlFlow::Continue(Descend::Yes)
+        } else if ctxt.outer_expn().is_descendant_of(expn_id)
+            && macro_backtrace(expr.span)
+                .map(|macro_call| cx.tcx.item_name(macro_call.def_id))
+                .any(|name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl))
+        {
+            ControlFlow::Break(expr)
+        } else {
+            ControlFlow::Continue(Descend::No)
+        }
+    });
+
+    if let Some(format_args_expr) = format_args_expr {
+        AST_FORMAT_ARGS.with(|ast_format_args| {
+            ast_format_args.borrow().get(&format_args_expr.span).map(callback);
+        });
+    }
+}
+
+/// Returns the [`Span`] of the value at `index` extended to the previous comma, e.g. for the value
+/// `10`
+///
+/// ```ignore
+/// format("{}.{}", 10, 11)
+/// //            ^^^^
+/// ```
+pub fn format_arg_removal_span(format_args: &FormatArgs, index: usize) -> Option<Span> {
+    let ctxt = format_args.span.ctxt();
+
+    let current = hygiene::walk_chain(format_args.arguments.by_index(index)?.expr.span, ctxt);
+
+    let prev = if index == 0 {
+        format_args.span
+    } else {
+        hygiene::walk_chain(format_args.arguments.by_index(index - 1)?.expr.span, ctxt)
+    };
+
+    Some(current.with_lo(prev.hi()))
+}
+
 /// The format string doesn't exist in the HIR, so we reassemble it from source code
 #[derive(Debug)]
 pub struct FormatString {
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index 41e34eba0ad..e0ea3952785 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -16,9 +16,9 @@ use rustc_infer::infer::{
 use rustc_lint::LateContext;
 use rustc_middle::mir::interpret::{ConstValue, Scalar};
 use rustc_middle::ty::{
-    self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, IntTy, List, ParamEnv, Predicate,
-    PredicateKind, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
-    TypeVisitor, UintTy, VariantDef, VariantDiscr,
+    self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, IntTy, List, ParamEnv, Predicate, PredicateKind,
+    Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
+    UintTy, VariantDef, VariantDiscr,
 };
 use rustc_middle::ty::{GenericArg, GenericArgKind};
 use rustc_span::symbol::Ident;
diff --git a/src/tools/clippy/declare_clippy_lint/Cargo.toml b/src/tools/clippy/declare_clippy_lint/Cargo.toml
index 80eee368178..5c9f76dbbc6 100644
--- a/src/tools/clippy/declare_clippy_lint/Cargo.toml
+++ b/src/tools/clippy/declare_clippy_lint/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "declare_clippy_lint"
-version = "0.1.69"
+version = "0.1.70"
 edition = "2021"
 publish = false
 
diff --git a/src/tools/clippy/lintcheck/Cargo.toml b/src/tools/clippy/lintcheck/Cargo.toml
index 653121af54d..27d32f39003 100644
--- a/src/tools/clippy/lintcheck/Cargo.toml
+++ b/src/tools/clippy/lintcheck/Cargo.toml
@@ -8,12 +8,16 @@ repository = "https://github.com/rust-lang/rust-clippy"
 categories = ["development-tools"]
 edition = "2021"
 publish = false
+default-run = "lintcheck"
 
 [dependencies]
+anyhow = "1.0.69"
 cargo_metadata = "0.15.3"
-clap = "4.1.4"
+clap = { version = "4.1.8", features = ["derive", "env"] }
+crates_io_api = "0.8.1"
 crossbeam-channel = "0.5.6"
 flate2 = "1.0"
+indicatif = "0.17.3"
 rayon = "1.5.1"
 serde = { version = "1.0", features = ["derive"] }
 serde_json = "1.0.85"
@@ -24,3 +28,11 @@ walkdir = "2.3"
 
 [features]
 deny-warnings = []
+
+[[bin]]
+name = "lintcheck"
+path = "src/main.rs"
+
+[[bin]]
+name = "popular-crates"
+path = "src/popular-crates.rs"
diff --git a/src/tools/clippy/lintcheck/README.md b/src/tools/clippy/lintcheck/README.md
index 6142de5e313..e997eb47e32 100644
--- a/src/tools/clippy/lintcheck/README.md
+++ b/src/tools/clippy/lintcheck/README.md
@@ -25,6 +25,15 @@ the repo root.
 
 The results will then be saved to `lintcheck-logs/custom_logs.toml`.
 
+The `custom.toml` file may be built using <https://crates.io> recently most
+downloaded crates by using the `popular-crates` binary from the `lintcheck`
+directory. For example, to retrieve the 100 recently most downloaded crates:
+
+```
+cargo run --release --bin popular-crates -- -n 100 custom.toml
+```
+
+
 ### Configuring the Crate Sources
 
 The sources to check are saved in a `toml` file. There are three types of
diff --git a/src/tools/clippy/lintcheck/src/config.rs b/src/tools/clippy/lintcheck/src/config.rs
index e0244ddcecb..3f01e9bb0a7 100644
--- a/src/tools/clippy/lintcheck/src/config.rs
+++ b/src/tools/clippy/lintcheck/src/config.rs
@@ -1,131 +1,79 @@
-use clap::{Arg, ArgAction, ArgMatches, Command};
-use std::env;
-use std::path::PathBuf;
+use clap::Parser;
+use std::{num::NonZeroUsize, path::PathBuf};
 
-fn get_clap_config() -> ArgMatches {
-    Command::new("lintcheck")
-        .about("run clippy on a set of crates and check output")
-        .args([
-            Arg::new("only")
-                .action(ArgAction::Set)
-                .value_name("CRATE")
-                .long("only")
-                .help("Only process a single crate of the list"),
-            Arg::new("crates-toml")
-                .action(ArgAction::Set)
-                .value_name("CRATES-SOURCES-TOML-PATH")
-                .long("crates-toml")
-                .help("Set the path for a crates.toml where lintcheck should read the sources from"),
-            Arg::new("threads")
-                .action(ArgAction::Set)
-                .value_name("N")
-                .value_parser(clap::value_parser!(usize))
-                .short('j')
-                .long("jobs")
-                .help("Number of threads to use, 0 automatic choice"),
-            Arg::new("fix")
-                .long("fix")
-                .help("Runs cargo clippy --fix and checks if all suggestions apply"),
-            Arg::new("filter")
-                .long("filter")
-                .action(ArgAction::Append)
-                .value_name("clippy_lint_name")
-                .help("Apply a filter to only collect specified lints, this also overrides `allow` attributes"),
-            Arg::new("markdown")
-                .long("markdown")
-                .help("Change the reports table to use markdown links"),
-            Arg::new("recursive")
-                .long("recursive")
-                .help("Run clippy on the dependencies of crates specified in crates-toml")
-                .conflicts_with("threads")
-                .conflicts_with("fix"),
-        ])
-        .get_matches()
-}
-
-#[derive(Debug, Clone)]
+#[derive(Clone, Debug, Parser)]
 pub(crate) struct LintcheckConfig {
-    /// max number of jobs to spawn (default 1)
+    /// Number of threads to use (default: all unless --fix or --recursive)
+    #[clap(
+        long = "jobs",
+        short = 'j',
+        value_name = "N",
+        default_value_t = 0,
+        hide_default_value = true
+    )]
     pub max_jobs: usize,
-    /// we read the sources to check from here
+    /// Set the path for a crates.toml where lintcheck should read the sources from
+    #[clap(
+        long = "crates-toml",
+        value_name = "CRATES-SOURCES-TOML-PATH",
+        default_value = "lintcheck/lintcheck_crates.toml",
+        hide_default_value = true,
+        env = "LINTCHECK_TOML",
+        hide_env = true
+    )]
     pub sources_toml_path: PathBuf,
-    /// we save the clippy lint results here
-    pub lintcheck_results_path: PathBuf,
-    /// Check only a specified package
+    /// File to save the clippy lint results here
+    #[clap(skip = "")]
+    pub lintcheck_results_path: PathBuf, // Overridden in new()
+    /// Only process a single crate on the list
+    #[clap(long, value_name = "CRATE")]
     pub only: Option<String>,
-    /// whether to just run --fix and not collect all the warnings
+    /// Runs cargo clippy --fix and checks if all suggestions apply
+    #[clap(long, conflicts_with("max_jobs"))]
     pub fix: bool,
-    /// A list of lints that this lintcheck run should focus on
+    /// Apply a filter to only collect specified lints, this also overrides `allow` attributes
+    #[clap(long = "filter", value_name = "clippy_lint_name", use_value_delimiter = true)]
     pub lint_filter: Vec<String>,
-    /// Indicate if the output should support markdown syntax
+    /// Change the reports table to use markdown links
+    #[clap(long)]
     pub markdown: bool,
-    /// Run clippy on the dependencies of crates
+    /// Run clippy on the dependencies of crates specified in crates-toml
+    #[clap(long, conflicts_with("max_jobs"))]
     pub recursive: bool,
 }
 
 impl LintcheckConfig {
     pub fn new() -> Self {
-        let clap_config = get_clap_config();
-
-        // first, check if we got anything passed via the LINTCHECK_TOML env var,
-        // if not, ask clap if we got any value for --crates-toml  <foo>
-        // if not, use the default "lintcheck/lintcheck_crates.toml"
-        let sources_toml = env::var("LINTCHECK_TOML").unwrap_or_else(|_| {
-            clap_config
-                .get_one::<String>("crates-toml")
-                .map_or("lintcheck/lintcheck_crates.toml", |s| &**s)
-                .into()
-        });
-
-        let markdown = clap_config.contains_id("markdown");
-        let sources_toml_path = PathBuf::from(sources_toml);
+        let mut config = LintcheckConfig::parse();
 
         // for the path where we save the lint results, get the filename without extension (so for
         // wasd.toml, use "wasd"...)
-        let filename: PathBuf = sources_toml_path.file_stem().unwrap().into();
-        let lintcheck_results_path = PathBuf::from(format!(
+        let filename: PathBuf = config.sources_toml_path.file_stem().unwrap().into();
+        config.lintcheck_results_path = PathBuf::from(format!(
             "lintcheck-logs/{}_logs.{}",
             filename.display(),
-            if markdown { "md" } else { "txt" }
+            if config.markdown { "md" } else { "txt" }
         ));
 
-        // look at the --threads arg, if 0 is passed, ask rayon rayon how many threads it would spawn and
-        // use half of that for the physical core count
-        // by default use a single thread
-        let max_jobs = match clap_config.get_one::<usize>("threads") {
-            Some(&0) => {
-                // automatic choice
-                // Rayon seems to return thread count so half that for core count
-                rayon::current_num_threads() / 2
-            },
-            Some(&threads) => threads,
-            // no -j passed, use a single thread
-            None => 1,
+        // look at the --threads arg, if 0 is passed, use the threads count
+        if config.max_jobs == 0 {
+            config.max_jobs = if config.fix || config.recursive {
+                1
+            } else {
+                std::thread::available_parallelism().map_or(1, NonZeroUsize::get)
+            };
         };
 
-        let lint_filter: Vec<String> = clap_config
-            .get_many::<String>("filter")
-            .map(|iter| {
-                iter.map(|lint_name| {
-                    let mut filter = lint_name.replace('_', "-");
-                    if !filter.starts_with("clippy::") {
-                        filter.insert_str(0, "clippy::");
-                    }
-                    filter
-                })
-                .collect()
-            })
-            .unwrap_or_default();
-
-        LintcheckConfig {
-            max_jobs,
-            sources_toml_path,
-            lintcheck_results_path,
-            only: clap_config.get_one::<String>("only").map(String::from),
-            fix: clap_config.contains_id("fix"),
-            lint_filter,
-            markdown,
-            recursive: clap_config.contains_id("recursive"),
+        for lint_name in &mut config.lint_filter {
+            *lint_name = format!(
+                "clippy::{}",
+                lint_name
+                    .strip_prefix("clippy::")
+                    .unwrap_or(lint_name)
+                    .replace('_', "-")
+            );
         }
+
+        config
     }
 }
diff --git a/src/tools/clippy/lintcheck/src/popular-crates.rs b/src/tools/clippy/lintcheck/src/popular-crates.rs
new file mode 100644
index 00000000000..fdab984ad86
--- /dev/null
+++ b/src/tools/clippy/lintcheck/src/popular-crates.rs
@@ -0,0 +1,65 @@
+#![deny(clippy::pedantic)]
+
+use clap::Parser;
+use crates_io_api::{CratesQueryBuilder, Sort, SyncClient};
+use indicatif::ProgressBar;
+use std::collections::HashSet;
+use std::fs::File;
+use std::io::{BufWriter, Write};
+use std::path::PathBuf;
+use std::time::Duration;
+
+#[derive(Parser)]
+struct Opts {
+    /// Output TOML file name
+    output: PathBuf,
+    /// Number of crate names to download
+    #[clap(short, long, default_value_t = 100)]
+    number: usize,
+    /// Do not output progress
+    #[clap(short, long)]
+    quiet: bool,
+}
+
+fn main() -> anyhow::Result<()> {
+    let opts = Opts::parse();
+    let mut output = BufWriter::new(File::create(opts.output)?);
+    output.write_all(b"[crates]\n")?;
+    let client = SyncClient::new(
+        "clippy/lintcheck (github.com/rust-lang/rust-clippy/)",
+        Duration::from_secs(1),
+    )?;
+    let mut seen_crates = HashSet::new();
+    let pb = if opts.quiet {
+        None
+    } else {
+        Some(ProgressBar::new(opts.number as u64))
+    };
+    let mut query = CratesQueryBuilder::new()
+        .sort(Sort::RecentDownloads)
+        .page_size(100)
+        .build();
+    while seen_crates.len() < opts.number {
+        let retrieved = client.crates(query.clone())?.crates;
+        if retrieved.is_empty() {
+            eprintln!("No more than {} crates available from API", seen_crates.len());
+            break;
+        }
+        for c in retrieved {
+            if seen_crates.insert(c.name.clone()) {
+                output.write_all(
+                    format!(
+                        "{} = {{ name = '{}', versions = ['{}'] }}\n",
+                        c.name, c.name, c.max_version
+                    )
+                    .as_bytes(),
+                )?;
+                if let Some(pb) = &pb {
+                    pb.inc(1);
+                }
+            }
+        }
+        query.set_page(query.page() + 1);
+    }
+    Ok(())
+}
diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain
index cfe845ec78f..d788c6359d7 100644
--- a/src/tools/clippy/rust-toolchain
+++ b/src/tools/clippy/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2023-02-25"
+channel = "nightly-2023-03-10"
 components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs
index dd183362f27..f08393c303e 100644
--- a/src/tools/clippy/src/driver.rs
+++ b/src/tools/clippy/src/driver.rs
@@ -176,7 +176,7 @@ Common options:
         --rustc              Pass all args to rustc
     -V, --version            Print version info and exit
 
-Other options are the same as `cargo check`.
+For the other options see `cargo check --help`.
 
 To allow or deny a lint from the command line you can use `cargo clippy --`
 with:
diff --git a/src/tools/clippy/src/main.rs b/src/tools/clippy/src/main.rs
index 82147eba33f..c5e9b96cf3f 100644
--- a/src/tools/clippy/src/main.rs
+++ b/src/tools/clippy/src/main.rs
@@ -18,7 +18,7 @@ Common options:
     -V, --version            Print version info and exit
     --explain LINT           Print the documentation for a given lint
 
-Other options are the same as `cargo check`.
+For the other options see `cargo check --help`.
 
 To allow or deny a lint from the command line you can use `cargo clippy --`
 with:
diff --git a/src/tools/clippy/tests/dogfood.rs b/src/tools/clippy/tests/dogfood.rs
index 6d0022f7a5c..9643c2c9707 100644
--- a/src/tools/clippy/tests/dogfood.rs
+++ b/src/tools/clippy/tests/dogfood.rs
@@ -7,6 +7,7 @@
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
 #![warn(rust_2018_idioms, unused_lifetimes)]
 
+use itertools::Itertools;
 use std::path::PathBuf;
 use std::process::Command;
 use test_utils::IS_RUSTC_TEST_SUITE;
@@ -19,8 +20,10 @@ fn dogfood_clippy() {
         return;
     }
 
+    let mut failed_packages = Vec::new();
+
     // "" is the root package
-    for package in &[
+    for package in [
         "",
         "clippy_dev",
         "clippy_lints",
@@ -28,8 +31,16 @@ fn dogfood_clippy() {
         "lintcheck",
         "rustc_tools_util",
     ] {
-        run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]);
+        if !run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]) {
+            failed_packages.push(if package.is_empty() { "root" } else { package });
+        }
     }
+
+    assert!(
+        !failed_packages.is_empty(),
+        "Dogfood failed for packages `{}`",
+        failed_packages.iter().format(", "),
+    )
 }
 
 #[test]
@@ -71,7 +82,7 @@ fn run_metadata_collection_lint() {
     run_clippy_for_package("clippy_lints", &["-A", "unfulfilled_lint_expectations"]);
 }
 
-fn run_clippy_for_package(project: &str, args: &[&str]) {
+fn run_clippy_for_package(project: &str, args: &[&str]) -> bool {
     let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
 
     let mut command = Command::new(&*test_utils::CARGO_CLIPPY_PATH);
@@ -107,5 +118,5 @@ fn run_clippy_for_package(project: &str, args: &[&str]) {
     println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
     println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
 
-    assert!(output.status.success());
+    output.status.success()
 }
diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs
index 2611e3a785f..ee7d2ba444b 100644
--- a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs
+++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs
@@ -45,24 +45,32 @@ impl_arith!(
     Div, Custom, Custom, div;
     Mul, Custom, Custom, mul;
     Rem, Custom, Custom, rem;
+    Shl, Custom, Custom, shl;
+    Shr, Custom, Custom, shr;
     Sub, Custom, Custom, sub;
 
     Add, Custom, &Custom, add;
     Div, Custom, &Custom, div;
     Mul, Custom, &Custom, mul;
     Rem, Custom, &Custom, rem;
+    Shl, Custom, &Custom, shl;
+    Shr, Custom, &Custom, shr;
     Sub, Custom, &Custom, sub;
 
     Add, &Custom, Custom, add;
     Div, &Custom, Custom, div;
     Mul, &Custom, Custom, mul;
     Rem, &Custom, Custom, rem;
+    Shl, &Custom, Custom, shl;
+    Shr, &Custom, Custom, shr;
     Sub, &Custom, Custom, sub;
 
     Add, &Custom, &Custom, add;
     Div, &Custom, &Custom, div;
     Mul, &Custom, &Custom, mul;
     Rem, &Custom, &Custom, rem;
+    Shl, &Custom, &Custom, shl;
+    Shr, &Custom, &Custom, shr;
     Sub, &Custom, &Custom, sub;
 );
 
@@ -71,24 +79,32 @@ impl_assign_arith!(
     DivAssign, Custom, Custom, div_assign;
     MulAssign, Custom, Custom, mul_assign;
     RemAssign, Custom, Custom, rem_assign;
+    ShlAssign, Custom, Custom, shl_assign;
+    ShrAssign, Custom, Custom, shr_assign;
     SubAssign, Custom, Custom, sub_assign;
 
     AddAssign, Custom, &Custom, add_assign;
     DivAssign, Custom, &Custom, div_assign;
     MulAssign, Custom, &Custom, mul_assign;
     RemAssign, Custom, &Custom, rem_assign;
+    ShlAssign, Custom, &Custom, shl_assign;
+    ShrAssign, Custom, &Custom, shr_assign;
     SubAssign, Custom, &Custom, sub_assign;
 
     AddAssign, &Custom, Custom, add_assign;
     DivAssign, &Custom, Custom, div_assign;
     MulAssign, &Custom, Custom, mul_assign;
     RemAssign, &Custom, Custom, rem_assign;
+    ShlAssign, &Custom, Custom, shl_assign;
+    ShrAssign, &Custom, Custom, shr_assign;
     SubAssign, &Custom, Custom, sub_assign;
 
     AddAssign, &Custom, &Custom, add_assign;
     DivAssign, &Custom, &Custom, div_assign;
     MulAssign, &Custom, &Custom, mul_assign;
     RemAssign, &Custom, &Custom, rem_assign;
+    ShlAssign, &Custom, &Custom, shl_assign;
+    ShrAssign, &Custom, &Custom, shr_assign;
     SubAssign, &Custom, &Custom, sub_assign;
 );
 
@@ -297,6 +313,10 @@ pub fn unknown_ops_or_runtime_ops_that_can_overflow() {
     _custom %= &Custom;
     _custom *= Custom;
     _custom *= &Custom;
+    _custom >>= Custom;
+    _custom >>= &Custom;
+    _custom <<= Custom;
+    _custom <<= &Custom;
     _custom += -Custom;
     _custom += &-Custom;
     _custom -= -Custom;
@@ -307,6 +327,10 @@ pub fn unknown_ops_or_runtime_ops_that_can_overflow() {
     _custom %= &-Custom;
     _custom *= -Custom;
     _custom *= &-Custom;
+    _custom >>= -Custom;
+    _custom >>= &-Custom;
+    _custom <<= -Custom;
+    _custom <<= &-Custom;
 
     // Binary
     _n = _n + 1;
@@ -347,6 +371,10 @@ pub fn unknown_ops_or_runtime_ops_that_can_overflow() {
     _custom = Custom + &Custom;
     _custom = &Custom + Custom;
     _custom = &Custom + &Custom;
+    _custom = _custom >> _custom;
+    _custom = _custom >> &_custom;
+    _custom = Custom << _custom;
+    _custom = &Custom << _custom;
 
     // Unary
     _n = -_n;
diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr
index 17a2448fbfc..3895f08964c 100644
--- a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr
+++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr
@@ -1,5 +1,5 @@
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:270:5
+  --> $DIR/arithmetic_side_effects.rs:286:5
    |
 LL |     _n += 1;
    |     ^^^^^^^
@@ -7,592 +7,640 @@ LL |     _n += 1;
    = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings`
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:271:5
+  --> $DIR/arithmetic_side_effects.rs:287:5
    |
 LL |     _n += &1;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:272:5
+  --> $DIR/arithmetic_side_effects.rs:288:5
    |
 LL |     _n -= 1;
    |     ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:273:5
+  --> $DIR/arithmetic_side_effects.rs:289:5
    |
 LL |     _n -= &1;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:274:5
+  --> $DIR/arithmetic_side_effects.rs:290:5
    |
 LL |     _n /= 0;
    |     ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:275:5
+  --> $DIR/arithmetic_side_effects.rs:291:5
    |
 LL |     _n /= &0;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:276:5
+  --> $DIR/arithmetic_side_effects.rs:292:5
    |
 LL |     _n %= 0;
    |     ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:277:5
+  --> $DIR/arithmetic_side_effects.rs:293:5
    |
 LL |     _n %= &0;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:278:5
+  --> $DIR/arithmetic_side_effects.rs:294:5
    |
 LL |     _n *= 2;
    |     ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:279:5
+  --> $DIR/arithmetic_side_effects.rs:295:5
    |
 LL |     _n *= &2;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:280:5
+  --> $DIR/arithmetic_side_effects.rs:296:5
    |
 LL |     _n += -1;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:281:5
+  --> $DIR/arithmetic_side_effects.rs:297:5
    |
 LL |     _n += &-1;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:282:5
+  --> $DIR/arithmetic_side_effects.rs:298:5
    |
 LL |     _n -= -1;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:283:5
+  --> $DIR/arithmetic_side_effects.rs:299:5
    |
 LL |     _n -= &-1;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:284:5
+  --> $DIR/arithmetic_side_effects.rs:300:5
    |
 LL |     _n /= -0;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:285:5
+  --> $DIR/arithmetic_side_effects.rs:301:5
    |
 LL |     _n /= &-0;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:286:5
+  --> $DIR/arithmetic_side_effects.rs:302:5
    |
 LL |     _n %= -0;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:287:5
+  --> $DIR/arithmetic_side_effects.rs:303:5
    |
 LL |     _n %= &-0;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:288:5
+  --> $DIR/arithmetic_side_effects.rs:304:5
    |
 LL |     _n *= -2;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:289:5
+  --> $DIR/arithmetic_side_effects.rs:305:5
    |
 LL |     _n *= &-2;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:290:5
+  --> $DIR/arithmetic_side_effects.rs:306:5
    |
 LL |     _custom += Custom;
    |     ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:291:5
+  --> $DIR/arithmetic_side_effects.rs:307:5
    |
 LL |     _custom += &Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:292:5
+  --> $DIR/arithmetic_side_effects.rs:308:5
    |
 LL |     _custom -= Custom;
    |     ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:293:5
+  --> $DIR/arithmetic_side_effects.rs:309:5
    |
 LL |     _custom -= &Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:294:5
+  --> $DIR/arithmetic_side_effects.rs:310:5
    |
 LL |     _custom /= Custom;
    |     ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:295:5
+  --> $DIR/arithmetic_side_effects.rs:311:5
    |
 LL |     _custom /= &Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:296:5
+  --> $DIR/arithmetic_side_effects.rs:312:5
    |
 LL |     _custom %= Custom;
    |     ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:297:5
+  --> $DIR/arithmetic_side_effects.rs:313:5
    |
 LL |     _custom %= &Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:298:5
+  --> $DIR/arithmetic_side_effects.rs:314:5
    |
 LL |     _custom *= Custom;
    |     ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:299:5
+  --> $DIR/arithmetic_side_effects.rs:315:5
    |
 LL |     _custom *= &Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:300:5
+  --> $DIR/arithmetic_side_effects.rs:316:5
+   |
+LL |     _custom >>= Custom;
+   |     ^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:317:5
+   |
+LL |     _custom >>= &Custom;
+   |     ^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:318:5
+   |
+LL |     _custom <<= Custom;
+   |     ^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:319:5
+   |
+LL |     _custom <<= &Custom;
+   |     ^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:320:5
    |
 LL |     _custom += -Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:301:5
+  --> $DIR/arithmetic_side_effects.rs:321:5
    |
 LL |     _custom += &-Custom;
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:302:5
+  --> $DIR/arithmetic_side_effects.rs:322:5
    |
 LL |     _custom -= -Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:303:5
+  --> $DIR/arithmetic_side_effects.rs:323:5
    |
 LL |     _custom -= &-Custom;
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:304:5
+  --> $DIR/arithmetic_side_effects.rs:324:5
    |
 LL |     _custom /= -Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:305:5
+  --> $DIR/arithmetic_side_effects.rs:325:5
    |
 LL |     _custom /= &-Custom;
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:306:5
+  --> $DIR/arithmetic_side_effects.rs:326:5
    |
 LL |     _custom %= -Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:307:5
+  --> $DIR/arithmetic_side_effects.rs:327:5
    |
 LL |     _custom %= &-Custom;
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:308:5
+  --> $DIR/arithmetic_side_effects.rs:328:5
    |
 LL |     _custom *= -Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:309:5
+  --> $DIR/arithmetic_side_effects.rs:329:5
    |
 LL |     _custom *= &-Custom;
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:312:10
+  --> $DIR/arithmetic_side_effects.rs:330:5
+   |
+LL |     _custom >>= -Custom;
+   |     ^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:331:5
+   |
+LL |     _custom >>= &-Custom;
+   |     ^^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:332:5
+   |
+LL |     _custom <<= -Custom;
+   |     ^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:333:5
+   |
+LL |     _custom <<= &-Custom;
+   |     ^^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:336:10
    |
 LL |     _n = _n + 1;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:313:10
+  --> $DIR/arithmetic_side_effects.rs:337:10
    |
 LL |     _n = _n + &1;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:314:10
+  --> $DIR/arithmetic_side_effects.rs:338:10
    |
 LL |     _n = 1 + _n;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:315:10
+  --> $DIR/arithmetic_side_effects.rs:339:10
    |
 LL |     _n = &1 + _n;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:316:10
+  --> $DIR/arithmetic_side_effects.rs:340:10
    |
 LL |     _n = _n - 1;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:317:10
+  --> $DIR/arithmetic_side_effects.rs:341:10
    |
 LL |     _n = _n - &1;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:318:10
+  --> $DIR/arithmetic_side_effects.rs:342:10
    |
 LL |     _n = 1 - _n;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:319:10
+  --> $DIR/arithmetic_side_effects.rs:343:10
    |
 LL |     _n = &1 - _n;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:320:10
+  --> $DIR/arithmetic_side_effects.rs:344:10
    |
 LL |     _n = _n / 0;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:321:10
+  --> $DIR/arithmetic_side_effects.rs:345:10
    |
 LL |     _n = _n / &0;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:322:10
+  --> $DIR/arithmetic_side_effects.rs:346:10
    |
 LL |     _n = _n % 0;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:323:10
+  --> $DIR/arithmetic_side_effects.rs:347:10
    |
 LL |     _n = _n % &0;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:324:10
+  --> $DIR/arithmetic_side_effects.rs:348:10
    |
 LL |     _n = _n * 2;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:325:10
+  --> $DIR/arithmetic_side_effects.rs:349:10
    |
 LL |     _n = _n * &2;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:326:10
+  --> $DIR/arithmetic_side_effects.rs:350:10
    |
 LL |     _n = 2 * _n;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:327:10
+  --> $DIR/arithmetic_side_effects.rs:351:10
    |
 LL |     _n = &2 * _n;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:328:10
+  --> $DIR/arithmetic_side_effects.rs:352:10
    |
 LL |     _n = 23 + &85;
    |          ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:329:10
+  --> $DIR/arithmetic_side_effects.rs:353:10
    |
 LL |     _n = &23 + 85;
    |          ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:330:10
+  --> $DIR/arithmetic_side_effects.rs:354:10
    |
 LL |     _n = &23 + &85;
    |          ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:331:15
+  --> $DIR/arithmetic_side_effects.rs:355:15
    |
 LL |     _custom = _custom + _custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:332:15
+  --> $DIR/arithmetic_side_effects.rs:356:15
    |
 LL |     _custom = _custom + &_custom;
    |               ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:333:15
+  --> $DIR/arithmetic_side_effects.rs:357:15
    |
 LL |     _custom = Custom + _custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:334:15
+  --> $DIR/arithmetic_side_effects.rs:358:15
    |
 LL |     _custom = &Custom + _custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:335:15
+  --> $DIR/arithmetic_side_effects.rs:359:15
    |
 LL |     _custom = _custom - Custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:336:15
+  --> $DIR/arithmetic_side_effects.rs:360:15
    |
 LL |     _custom = _custom - &Custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:337:15
+  --> $DIR/arithmetic_side_effects.rs:361:15
    |
 LL |     _custom = Custom - _custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:338:15
+  --> $DIR/arithmetic_side_effects.rs:362:15
    |
 LL |     _custom = &Custom - _custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:339:15
+  --> $DIR/arithmetic_side_effects.rs:363:15
    |
 LL |     _custom = _custom / Custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:340:15
+  --> $DIR/arithmetic_side_effects.rs:364:15
    |
 LL |     _custom = _custom / &Custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:341:15
+  --> $DIR/arithmetic_side_effects.rs:365:15
    |
 LL |     _custom = _custom % Custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:342:15
+  --> $DIR/arithmetic_side_effects.rs:366:15
    |
 LL |     _custom = _custom % &Custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:343:15
+  --> $DIR/arithmetic_side_effects.rs:367:15
    |
 LL |     _custom = _custom * Custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:344:15
+  --> $DIR/arithmetic_side_effects.rs:368:15
    |
 LL |     _custom = _custom * &Custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:345:15
+  --> $DIR/arithmetic_side_effects.rs:369:15
    |
 LL |     _custom = Custom * _custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:346:15
+  --> $DIR/arithmetic_side_effects.rs:370:15
    |
 LL |     _custom = &Custom * _custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:347:15
+  --> $DIR/arithmetic_side_effects.rs:371:15
    |
 LL |     _custom = Custom + &Custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:348:15
+  --> $DIR/arithmetic_side_effects.rs:372:15
    |
 LL |     _custom = &Custom + Custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:349:15
+  --> $DIR/arithmetic_side_effects.rs:373:15
    |
 LL |     _custom = &Custom + &Custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:352:10
+  --> $DIR/arithmetic_side_effects.rs:374:15
+   |
+LL |     _custom = _custom >> _custom;
+   |               ^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:375:15
+   |
+LL |     _custom = _custom >> &_custom;
+   |               ^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:376:15
+   |
+LL |     _custom = Custom << _custom;
+   |               ^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:377:15
+   |
+LL |     _custom = &Custom << _custom;
+   |               ^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+  --> $DIR/arithmetic_side_effects.rs:380:10
    |
 LL |     _n = -_n;
    |          ^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:353:10
+  --> $DIR/arithmetic_side_effects.rs:381:10
    |
 LL |     _n = -&_n;
    |          ^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:354:15
+  --> $DIR/arithmetic_side_effects.rs:382:15
    |
 LL |     _custom = -_custom;
    |               ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:355:15
+  --> $DIR/arithmetic_side_effects.rs:383:15
    |
 LL |     _custom = -&_custom;
    |               ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:364:5
+  --> $DIR/arithmetic_side_effects.rs:392:5
    |
 LL |     1 + i;
    |     ^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:365:5
+  --> $DIR/arithmetic_side_effects.rs:393:5
    |
 LL |     i * 2;
    |     ^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:367:5
+  --> $DIR/arithmetic_side_effects.rs:395:5
    |
 LL |     i - 2 + 2 - i;
    |     ^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:368:5
+  --> $DIR/arithmetic_side_effects.rs:396:5
    |
 LL |     -i;
    |     ^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:369:5
-   |
-LL |     i >> 1;
-   |     ^^^^^^
-
-error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:370:5
-   |
-LL |     i << 1;
-   |     ^^^^^^
-
-error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:379:5
+  --> $DIR/arithmetic_side_effects.rs:407:5
    |
 LL |     i += 1;
    |     ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:380:5
+  --> $DIR/arithmetic_side_effects.rs:408:5
    |
 LL |     i -= 1;
    |     ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:381:5
+  --> $DIR/arithmetic_side_effects.rs:409:5
    |
 LL |     i *= 2;
    |     ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:383:5
+  --> $DIR/arithmetic_side_effects.rs:411:5
    |
 LL |     i /= 0;
    |     ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:385:5
+  --> $DIR/arithmetic_side_effects.rs:413:5
    |
 LL |     i /= var1;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:386:5
+  --> $DIR/arithmetic_side_effects.rs:414:5
    |
 LL |     i /= var2;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:388:5
+  --> $DIR/arithmetic_side_effects.rs:416:5
    |
 LL |     i %= 0;
    |     ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:390:5
+  --> $DIR/arithmetic_side_effects.rs:418:5
    |
 LL |     i %= var1;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:391:5
+  --> $DIR/arithmetic_side_effects.rs:419:5
    |
 LL |     i %= var2;
    |     ^^^^^^^^^
 
-error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:392:5
-   |
-LL |     i <<= 3;
-   |     ^^^^^^^
-
-error: arithmetic operation that can potentially result in unexpected side-effects
-  --> $DIR/arithmetic_side_effects.rs:393:5
-   |
-LL |     i >>= 2;
-   |     ^^^^^^^
-
-error: aborting due to 99 previous errors
+error: aborting due to 107 previous errors
 
diff --git a/src/tools/clippy/tests/ui/async_yields_async.fixed b/src/tools/clippy/tests/ui/async_yields_async.fixed
index 3cf380d2b95..579a63ea477 100644
--- a/src/tools/clippy/tests/ui/async_yields_async.fixed
+++ b/src/tools/clippy/tests/ui/async_yields_async.fixed
@@ -2,6 +2,7 @@
 #![feature(lint_reasons)]
 #![feature(async_closure)]
 #![warn(clippy::async_yields_async)]
+#![allow(clippy::redundant_async_block)]
 
 use core::future::Future;
 use core::pin::Pin;
diff --git a/src/tools/clippy/tests/ui/async_yields_async.rs b/src/tools/clippy/tests/ui/async_yields_async.rs
index dd4131b60ab..5aec2fb50f6 100644
--- a/src/tools/clippy/tests/ui/async_yields_async.rs
+++ b/src/tools/clippy/tests/ui/async_yields_async.rs
@@ -2,6 +2,7 @@
 #![feature(lint_reasons)]
 #![feature(async_closure)]
 #![warn(clippy::async_yields_async)]
+#![allow(clippy::redundant_async_block)]
 
 use core::future::Future;
 use core::pin::Pin;
diff --git a/src/tools/clippy/tests/ui/async_yields_async.stderr b/src/tools/clippy/tests/ui/async_yields_async.stderr
index 22ce1c6f647..7f72534832b 100644
--- a/src/tools/clippy/tests/ui/async_yields_async.stderr
+++ b/src/tools/clippy/tests/ui/async_yields_async.stderr
@@ -1,5 +1,5 @@
 error: an async construct yields a type which is itself awaitable
-  --> $DIR/async_yields_async.rs:39:9
+  --> $DIR/async_yields_async.rs:40:9
    |
 LL |        let _h = async {
    |  _____________________-
@@ -19,7 +19,7 @@ LL +         }.await
    |
 
 error: an async construct yields a type which is itself awaitable
-  --> $DIR/async_yields_async.rs:44:9
+  --> $DIR/async_yields_async.rs:45:9
    |
 LL |       let _i = async {
    |  ____________________-
@@ -32,7 +32,7 @@ LL | |     };
    | |_____- outer async construct
 
 error: an async construct yields a type which is itself awaitable
-  --> $DIR/async_yields_async.rs:50:9
+  --> $DIR/async_yields_async.rs:51:9
    |
 LL |        let _j = async || {
    |  ________________________-
@@ -51,7 +51,7 @@ LL +         }.await
    |
 
 error: an async construct yields a type which is itself awaitable
-  --> $DIR/async_yields_async.rs:55:9
+  --> $DIR/async_yields_async.rs:56:9
    |
 LL |       let _k = async || {
    |  _______________________-
@@ -64,7 +64,7 @@ LL | |     };
    | |_____- outer async construct
 
 error: an async construct yields a type which is itself awaitable
-  --> $DIR/async_yields_async.rs:57:23
+  --> $DIR/async_yields_async.rs:58:23
    |
 LL |     let _l = async || CustomFutureType;
    |                       ^^^^^^^^^^^^^^^^
@@ -74,7 +74,7 @@ LL |     let _l = async || CustomFutureType;
    |                       help: consider awaiting this value: `CustomFutureType.await`
 
 error: an async construct yields a type which is itself awaitable
-  --> $DIR/async_yields_async.rs:63:9
+  --> $DIR/async_yields_async.rs:64:9
    |
 LL |       let _m = async || {
    |  _______________________-
diff --git a/src/tools/clippy/tests/ui/collection_is_never_read.rs b/src/tools/clippy/tests/ui/collection_is_never_read.rs
new file mode 100644
index 00000000000..068a49486cf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/collection_is_never_read.rs
@@ -0,0 +1,165 @@
+#![allow(unused)]
+#![warn(clippy::collection_is_never_read)]
+
+use std::collections::{HashMap, HashSet};
+
+fn main() {}
+
+fn not_a_collection() {
+    // TODO: Expand `collection_is_never_read` beyond collections?
+    let mut x = 10; // Ok
+    x += 1;
+}
+
+fn no_access_at_all() {
+    // Other lints should catch this.
+    let x = vec![1, 2, 3]; // Ok
+}
+
+fn write_without_read() {
+    // The main use case for `collection_is_never_read`.
+    let mut x = HashMap::new(); // WARNING
+    x.insert(1, 2);
+}
+
+fn read_without_write() {
+    let mut x = vec![1, 2, 3]; // Ok
+    let _ = x.len();
+}
+
+fn write_and_read() {
+    let mut x = vec![1, 2, 3]; // Ok
+    x.push(4);
+    let _ = x.len();
+}
+
+fn write_after_read() {
+    // TODO: Warn here, but this requires more extensive data flow analysis.
+    let mut x = vec![1, 2, 3]; // Ok
+    let _ = x.len();
+    x.push(4); // Pointless
+}
+
+fn write_before_reassign() {
+    // TODO: Warn here, but this requires more extensive data flow analysis.
+    let mut x = HashMap::new(); // Ok
+    x.insert(1, 2); // Pointless
+    x = HashMap::new();
+    let _ = x.len();
+}
+
+fn read_in_closure() {
+    let mut x = HashMap::new(); // Ok
+    x.insert(1, 2);
+    let _ = || {
+        let _ = x.len();
+    };
+}
+
+fn write_in_closure() {
+    let mut x = vec![1, 2, 3]; // WARNING
+    let _ = || {
+        x.push(4);
+    };
+}
+
+fn read_in_format() {
+    let mut x = HashMap::new(); // Ok
+    x.insert(1, 2);
+    format!("{x:?}");
+}
+
+fn shadowing_1() {
+    let x = HashMap::<usize, usize>::new(); // Ok
+    let _ = x.len();
+    let mut x = HashMap::new(); // WARNING
+    x.insert(1, 2);
+}
+
+fn shadowing_2() {
+    let mut x = HashMap::new(); // WARNING
+    x.insert(1, 2);
+    let x = HashMap::<usize, usize>::new(); // Ok
+    let _ = x.len();
+}
+
+#[allow(clippy::let_unit_value)]
+fn fake_read() {
+    let mut x = vec![1, 2, 3]; // Ok
+    x.reverse();
+    // `collection_is_never_read` gets fooled, but other lints should catch this.
+    let _: () = x.clear();
+}
+
+fn assignment() {
+    let mut x = vec![1, 2, 3]; // WARNING
+    let y = vec![4, 5, 6]; // Ok
+    x = y;
+}
+
+#[allow(clippy::self_assignment)]
+fn self_assignment() {
+    let mut x = vec![1, 2, 3]; // WARNING
+    x = x;
+}
+
+fn method_argument_but_not_target() {
+    struct MyStruct;
+    impl MyStruct {
+        fn my_method(&self, _argument: &[usize]) {}
+    }
+    let my_struct = MyStruct;
+
+    let mut x = vec![1, 2, 3]; // Ok
+    x.reverse();
+    my_struct.my_method(&x);
+}
+
+fn insert_is_not_a_read() {
+    let mut x = HashSet::new(); // WARNING
+    x.insert(5);
+}
+
+fn insert_is_a_read() {
+    let mut x = HashSet::new(); // Ok
+    if x.insert(5) {
+        println!("5 was inserted");
+    }
+}
+
+fn not_read_if_return_value_not_used() {
+    // `is_empty` does not modify the set, so it's a query. But since the return value is not used, the
+    // lint does not consider it a read here.
+    let x = vec![1, 2, 3]; // WARNING
+    x.is_empty();
+}
+
+fn extension_traits() {
+    trait VecExt<T> {
+        fn method_with_side_effect(&self);
+        fn method_without_side_effect(&self);
+    }
+
+    impl<T> VecExt<T> for Vec<T> {
+        fn method_with_side_effect(&self) {
+            println!("my length: {}", self.len());
+        }
+        fn method_without_side_effect(&self) {}
+    }
+
+    let x = vec![1, 2, 3]; // Ok
+    x.method_with_side_effect();
+
+    let y = vec![1, 2, 3]; // Ok (false negative)
+    y.method_without_side_effect();
+}
+
+fn function_argument() {
+    #[allow(clippy::ptr_arg)]
+    fn foo<T>(v: &Vec<T>) -> usize {
+        v.len()
+    }
+
+    let x = vec![1, 2, 3]; // Ok
+    foo(&x);
+}
diff --git a/src/tools/clippy/tests/ui/collection_is_never_read.stderr b/src/tools/clippy/tests/ui/collection_is_never_read.stderr
new file mode 100644
index 00000000000..7654b74be3d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/collection_is_never_read.stderr
@@ -0,0 +1,52 @@
+error: collection is never read
+  --> $DIR/collection_is_never_read.rs:21:5
+   |
+LL |     let mut x = HashMap::new(); // WARNING
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::collection-is-never-read` implied by `-D warnings`
+
+error: collection is never read
+  --> $DIR/collection_is_never_read.rs:60:5
+   |
+LL |     let mut x = vec![1, 2, 3]; // WARNING
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+  --> $DIR/collection_is_never_read.rs:75:5
+   |
+LL |     let mut x = HashMap::new(); // WARNING
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+  --> $DIR/collection_is_never_read.rs:80:5
+   |
+LL |     let mut x = HashMap::new(); // WARNING
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+  --> $DIR/collection_is_never_read.rs:95:5
+   |
+LL |     let mut x = vec![1, 2, 3]; // WARNING
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+  --> $DIR/collection_is_never_read.rs:102:5
+   |
+LL |     let mut x = vec![1, 2, 3]; // WARNING
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+  --> $DIR/collection_is_never_read.rs:119:5
+   |
+LL |     let mut x = HashSet::new(); // WARNING
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+  --> $DIR/collection_is_never_read.rs:133:5
+   |
+LL |     let x = vec![1, 2, 3]; // WARNING
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-10148.rs b/src/tools/clippy/tests/ui/crashes/ice-10148.rs
new file mode 100644
index 00000000000..af33b10c693
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-10148.rs
@@ -0,0 +1,9 @@
+// aux-build:../../auxiliary/proc_macro_with_span.rs
+
+extern crate proc_macro_with_span;
+
+use proc_macro_with_span::with_span;
+
+fn main() {
+    println!(with_span!(""something ""));
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-10148.stderr b/src/tools/clippy/tests/ui/crashes/ice-10148.stderr
new file mode 100644
index 00000000000..f23e4433f95
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-10148.stderr
@@ -0,0 +1,12 @@
+error: empty string literal in `println!`
+  --> $DIR/ice-10148.rs:8:5
+   |
+LL |     println!(with_span!(""something ""));
+   |     ^^^^^^^^^^^^^^^^^^^^-----------^^^^^
+   |                         |
+   |                         help: remove the empty string
+   |
+   = note: `-D clippy::println-empty-string` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6179.rs b/src/tools/clippy/tests/ui/crashes/ice-6179.rs
index 4fe92d356c4..ce1895851e2 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-6179.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-6179.rs
@@ -2,7 +2,7 @@
 //! The ICE is mainly caused by using `hir_ty_to_ty`. See the discussion in the PR for details.
 
 #![warn(clippy::use_self)]
-#![allow(dead_code)]
+#![allow(dead_code, clippy::let_with_type_underscore)]
 
 struct Foo;
 
diff --git a/src/tools/clippy/tests/ui/crashes/ice-rust-107877.rs b/src/tools/clippy/tests/ui/crashes/ice-rust-107877.rs
new file mode 100644
index 00000000000..7f5bae60d55
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-rust-107877.rs
@@ -0,0 +1,17 @@
+#![allow(dead_code)]
+
+struct Foo;
+
+impl<'a> std::convert::TryFrom<&'a String> for Foo {
+    type Error = std::convert::Infallible;
+
+    fn try_from(_: &'a String) -> Result<Self, Self::Error> {
+        Ok(Foo)
+    }
+}
+
+fn find<E>(_: impl std::convert::TryInto<Foo, Error = E>) {}
+
+fn main() {
+    find(&String::new());
+}
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed
index a370ccc7696..a9e5fd159af 100644
--- a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed
@@ -9,7 +9,8 @@
     clippy::unnecessary_operation,
     clippy::branches_sharing_code,
     clippy::match_single_binding,
-    clippy::let_unit_value
+    clippy::let_unit_value,
+    clippy::let_with_type_underscore
 )]
 
 #[macro_use]
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs
index 2476fe95141..085f8f452b2 100644
--- a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs
@@ -9,7 +9,8 @@
     clippy::unnecessary_operation,
     clippy::branches_sharing_code,
     clippy::match_single_binding,
-    clippy::let_unit_value
+    clippy::let_unit_value,
+    clippy::let_with_type_underscore
 )]
 
 #[macro_use]
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr
index 5df2f642388..44c6f1a9bea 100644
--- a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr
@@ -1,5 +1,5 @@
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:21:17
+  --> $DIR/default_numeric_fallback_f64.rs:22:17
    |
 LL |         let x = 0.12;
    |                 ^^^^ help: consider adding suffix: `0.12_f64`
@@ -7,139 +7,139 @@ LL |         let x = 0.12;
    = note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:22:18
+  --> $DIR/default_numeric_fallback_f64.rs:23:18
    |
 LL |         let x = [1., 2., 3.];
    |                  ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:22:22
+  --> $DIR/default_numeric_fallback_f64.rs:23:22
    |
 LL |         let x = [1., 2., 3.];
    |                      ^^ help: consider adding suffix: `2.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:22:26
+  --> $DIR/default_numeric_fallback_f64.rs:23:26
    |
 LL |         let x = [1., 2., 3.];
    |                          ^^ help: consider adding suffix: `3.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:23:28
+  --> $DIR/default_numeric_fallback_f64.rs:24:28
    |
 LL |         let x = if true { (1., 2.) } else { (3., 4.) };
    |                            ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:23:32
+  --> $DIR/default_numeric_fallback_f64.rs:24:32
    |
 LL |         let x = if true { (1., 2.) } else { (3., 4.) };
    |                                ^^ help: consider adding suffix: `2.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:23:46
+  --> $DIR/default_numeric_fallback_f64.rs:24:46
    |
 LL |         let x = if true { (1., 2.) } else { (3., 4.) };
    |                                              ^^ help: consider adding suffix: `3.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:23:50
+  --> $DIR/default_numeric_fallback_f64.rs:24:50
    |
 LL |         let x = if true { (1., 2.) } else { (3., 4.) };
    |                                                  ^^ help: consider adding suffix: `4.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:24:23
+  --> $DIR/default_numeric_fallback_f64.rs:25:23
    |
 LL |         let x = match 1. {
    |                       ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:25:18
+  --> $DIR/default_numeric_fallback_f64.rs:26:18
    |
 LL |             _ => 1.,
    |                  ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:44:21
+  --> $DIR/default_numeric_fallback_f64.rs:45:21
    |
 LL |             let y = 1.;
    |                     ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:52:21
+  --> $DIR/default_numeric_fallback_f64.rs:53:21
    |
 LL |             let y = 1.;
    |                     ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:58:21
+  --> $DIR/default_numeric_fallback_f64.rs:59:21
    |
 LL |             let y = 1.;
    |                     ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:66:21
+  --> $DIR/default_numeric_fallback_f64.rs:67:21
    |
 LL |             let y = 1.;
    |                     ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:78:9
+  --> $DIR/default_numeric_fallback_f64.rs:79:9
    |
 LL |         1.
    |         ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:84:27
+  --> $DIR/default_numeric_fallback_f64.rs:85:27
    |
 LL |         let f = || -> _ { 1. };
    |                           ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:88:29
+  --> $DIR/default_numeric_fallback_f64.rs:89:29
    |
 LL |         let f = || -> f64 { 1. };
    |                             ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:102:21
+  --> $DIR/default_numeric_fallback_f64.rs:103:21
    |
 LL |         generic_arg(1.);
    |                     ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:105:32
+  --> $DIR/default_numeric_fallback_f64.rs:106:32
    |
 LL |         let x: _ = generic_arg(1.);
    |                                ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:123:28
+  --> $DIR/default_numeric_fallback_f64.rs:124:28
    |
 LL |         GenericStruct { x: 1. };
    |                            ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:126:36
+  --> $DIR/default_numeric_fallback_f64.rs:127:36
    |
 LL |         let _ = GenericStruct { x: 1. };
    |                                    ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:144:24
+  --> $DIR/default_numeric_fallback_f64.rs:145:24
    |
 LL |         GenericEnum::X(1.);
    |                        ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:164:23
+  --> $DIR/default_numeric_fallback_f64.rs:165:23
    |
 LL |         s.generic_arg(1.);
    |                       ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:171:21
+  --> $DIR/default_numeric_fallback_f64.rs:172:21
    |
 LL |             let x = 22.;
    |                     ^^^ help: consider adding suffix: `22.0_f64`
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed
index 3f4994f0453..63ac4d5aeb6 100644
--- a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed
@@ -9,7 +9,8 @@
     clippy::no_effect,
     clippy::unnecessary_operation,
     clippy::branches_sharing_code,
-    clippy::let_unit_value
+    clippy::let_unit_value,
+    clippy::let_with_type_underscore
 )]
 
 #[macro_use]
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs
index 2df0e09787f..28e6eceb80e 100644
--- a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs
@@ -9,7 +9,8 @@
     clippy::no_effect,
     clippy::unnecessary_operation,
     clippy::branches_sharing_code,
-    clippy::let_unit_value
+    clippy::let_unit_value,
+    clippy::let_with_type_underscore
 )]
 
 #[macro_use]
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr
index 6f219c3fc2b..dd91574d5b3 100644
--- a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr
@@ -1,5 +1,5 @@
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:21:17
+  --> $DIR/default_numeric_fallback_i32.rs:22:17
    |
 LL |         let x = 22;
    |                 ^^ help: consider adding suffix: `22_i32`
@@ -7,151 +7,151 @@ LL |         let x = 22;
    = note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:22:18
+  --> $DIR/default_numeric_fallback_i32.rs:23:18
    |
 LL |         let x = [1, 2, 3];
    |                  ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:22:21
+  --> $DIR/default_numeric_fallback_i32.rs:23:21
    |
 LL |         let x = [1, 2, 3];
    |                     ^ help: consider adding suffix: `2_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:22:24
+  --> $DIR/default_numeric_fallback_i32.rs:23:24
    |
 LL |         let x = [1, 2, 3];
    |                        ^ help: consider adding suffix: `3_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:23:28
+  --> $DIR/default_numeric_fallback_i32.rs:24:28
    |
 LL |         let x = if true { (1, 2) } else { (3, 4) };
    |                            ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:23:31
+  --> $DIR/default_numeric_fallback_i32.rs:24:31
    |
 LL |         let x = if true { (1, 2) } else { (3, 4) };
    |                               ^ help: consider adding suffix: `2_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:23:44
+  --> $DIR/default_numeric_fallback_i32.rs:24:44
    |
 LL |         let x = if true { (1, 2) } else { (3, 4) };
    |                                            ^ help: consider adding suffix: `3_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:23:47
+  --> $DIR/default_numeric_fallback_i32.rs:24:47
    |
 LL |         let x = if true { (1, 2) } else { (3, 4) };
    |                                               ^ help: consider adding suffix: `4_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:24:23
+  --> $DIR/default_numeric_fallback_i32.rs:25:23
    |
 LL |         let x = match 1 {
    |                       ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:25:13
+  --> $DIR/default_numeric_fallback_i32.rs:26:13
    |
 LL |             1 => 1,
    |             ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:25:18
+  --> $DIR/default_numeric_fallback_i32.rs:26:18
    |
 LL |             1 => 1,
    |                  ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:26:18
+  --> $DIR/default_numeric_fallback_i32.rs:27:18
    |
 LL |             _ => 2,
    |                  ^ help: consider adding suffix: `2_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:45:21
+  --> $DIR/default_numeric_fallback_i32.rs:46:21
    |
 LL |             let y = 1;
    |                     ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:53:21
+  --> $DIR/default_numeric_fallback_i32.rs:54:21
    |
 LL |             let y = 1;
    |                     ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:59:21
+  --> $DIR/default_numeric_fallback_i32.rs:60:21
    |
 LL |             let y = 1;
    |                     ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:67:21
+  --> $DIR/default_numeric_fallback_i32.rs:68:21
    |
 LL |             let y = 1;
    |                     ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:79:9
+  --> $DIR/default_numeric_fallback_i32.rs:80:9
    |
 LL |         1
    |         ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:85:27
+  --> $DIR/default_numeric_fallback_i32.rs:86:27
    |
 LL |         let f = || -> _ { 1 };
    |                           ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:89:29
+  --> $DIR/default_numeric_fallback_i32.rs:90:29
    |
 LL |         let f = || -> i32 { 1 };
    |                             ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:103:21
+  --> $DIR/default_numeric_fallback_i32.rs:104:21
    |
 LL |         generic_arg(1);
    |                     ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:106:32
+  --> $DIR/default_numeric_fallback_i32.rs:107:32
    |
 LL |         let x: _ = generic_arg(1);
    |                                ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:124:28
+  --> $DIR/default_numeric_fallback_i32.rs:125:28
    |
 LL |         GenericStruct { x: 1 };
    |                            ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:127:36
+  --> $DIR/default_numeric_fallback_i32.rs:128:36
    |
 LL |         let _ = GenericStruct { x: 1 };
    |                                    ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:145:24
+  --> $DIR/default_numeric_fallback_i32.rs:146:24
    |
 LL |         GenericEnum::X(1);
    |                        ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:165:23
+  --> $DIR/default_numeric_fallback_i32.rs:166:23
    |
 LL |         s.generic_arg(1);
    |                       ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:172:21
+  --> $DIR/default_numeric_fallback_i32.rs:173:21
    |
 LL |             let x = 22;
    |                     ^^ help: consider adding suffix: `22_i32`
diff --git a/src/tools/clippy/tests/ui/derivable_impls.fixed b/src/tools/clippy/tests/ui/derivable_impls.fixed
index ee8456f5deb..89ec33a0d8f 100644
--- a/src/tools/clippy/tests/ui/derivable_impls.fixed
+++ b/src/tools/clippy/tests/ui/derivable_impls.fixed
@@ -231,4 +231,41 @@ impl Default for NonExhaustiveEnum {
     }
 }
 
+// https://github.com/rust-lang/rust-clippy/issues/10396
+
+#[derive(Default)]
+struct DefaultType;
+
+struct GenericType<T = DefaultType> {
+    t: T,
+}
+
+impl Default for GenericType {
+    fn default() -> Self {
+        Self { t: Default::default() }
+    }
+}
+
+struct InnerGenericType<T> {
+    t: T,
+}
+
+impl Default for InnerGenericType<DefaultType> {
+    fn default() -> Self {
+        Self { t: Default::default() }
+    }
+}
+
+struct OtherGenericType<T = DefaultType> {
+    inner: InnerGenericType<T>,
+}
+
+impl Default for OtherGenericType {
+    fn default() -> Self {
+        Self {
+            inner: Default::default(),
+        }
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/derivable_impls.rs b/src/tools/clippy/tests/ui/derivable_impls.rs
index 14af419bcad..def6e41162f 100644
--- a/src/tools/clippy/tests/ui/derivable_impls.rs
+++ b/src/tools/clippy/tests/ui/derivable_impls.rs
@@ -267,4 +267,41 @@ impl Default for NonExhaustiveEnum {
     }
 }
 
+// https://github.com/rust-lang/rust-clippy/issues/10396
+
+#[derive(Default)]
+struct DefaultType;
+
+struct GenericType<T = DefaultType> {
+    t: T,
+}
+
+impl Default for GenericType {
+    fn default() -> Self {
+        Self { t: Default::default() }
+    }
+}
+
+struct InnerGenericType<T> {
+    t: T,
+}
+
+impl Default for InnerGenericType<DefaultType> {
+    fn default() -> Self {
+        Self { t: Default::default() }
+    }
+}
+
+struct OtherGenericType<T = DefaultType> {
+    inner: InnerGenericType<T>,
+}
+
+impl Default for OtherGenericType {
+    fn default() -> Self {
+        Self {
+            inner: Default::default(),
+        }
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/format.fixed b/src/tools/clippy/tests/ui/format.fixed
index cd2f70ee8b0..beedf2c1db2 100644
--- a/src/tools/clippy/tests/ui/format.fixed
+++ b/src/tools/clippy/tests/ui/format.fixed
@@ -1,5 +1,4 @@
 // run-rustfix
-// aux-build: proc_macro_with_span.rs
 #![warn(clippy::useless_format)]
 #![allow(
     unused_tuple_struct_fields,
@@ -10,8 +9,6 @@
     clippy::uninlined_format_args
 )]
 
-extern crate proc_macro_with_span;
-
 struct Foo(pub String);
 
 macro_rules! foo {
@@ -90,7 +87,4 @@ fn main() {
     let _ = abc.to_string();
     let xx = "xx";
     let _ = xx.to_string();
-
-    // Issue #10148
-    println!(proc_macro_with_span::with_span!(""something ""));
 }
diff --git a/src/tools/clippy/tests/ui/format.rs b/src/tools/clippy/tests/ui/format.rs
index c22345a79d4..e805f181889 100644
--- a/src/tools/clippy/tests/ui/format.rs
+++ b/src/tools/clippy/tests/ui/format.rs
@@ -1,5 +1,4 @@
 // run-rustfix
-// aux-build: proc_macro_with_span.rs
 #![warn(clippy::useless_format)]
 #![allow(
     unused_tuple_struct_fields,
@@ -10,8 +9,6 @@
     clippy::uninlined_format_args
 )]
 
-extern crate proc_macro_with_span;
-
 struct Foo(pub String);
 
 macro_rules! foo {
@@ -92,7 +89,4 @@ fn main() {
     let _ = format!("{abc}");
     let xx = "xx";
     let _ = format!("{xx}");
-
-    // Issue #10148
-    println!(proc_macro_with_span::with_span!(""something ""));
 }
diff --git a/src/tools/clippy/tests/ui/format.stderr b/src/tools/clippy/tests/ui/format.stderr
index a0e5d5c8ad2..0ef0ac655d3 100644
--- a/src/tools/clippy/tests/ui/format.stderr
+++ b/src/tools/clippy/tests/ui/format.stderr
@@ -1,5 +1,5 @@
 error: useless use of `format!`
-  --> $DIR/format.rs:22:5
+  --> $DIR/format.rs:19:5
    |
 LL |     format!("foo");
    |     ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
@@ -7,19 +7,19 @@ LL |     format!("foo");
    = note: `-D clippy::useless-format` implied by `-D warnings`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:23:5
+  --> $DIR/format.rs:20:5
    |
 LL |     format!("{{}}");
    |     ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{}".to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:24:5
+  --> $DIR/format.rs:21:5
    |
 LL |     format!("{{}} abc {{}}");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{} abc {}".to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:25:5
+  --> $DIR/format.rs:22:5
    |
 LL | /     format!(
 LL | |         r##"foo {{}}
@@ -34,67 +34,67 @@ LL ~ " bar"##.to_string();
    |
 
 error: useless use of `format!`
-  --> $DIR/format.rs:30:13
+  --> $DIR/format.rs:27:13
    |
 LL |     let _ = format!("");
    |             ^^^^^^^^^^^ help: consider using `String::new()`: `String::new()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:32:5
+  --> $DIR/format.rs:29:5
    |
 LL |     format!("{}", "foo");
    |     ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:40:5
+  --> $DIR/format.rs:37:5
    |
 LL |     format!("{}", arg);
    |     ^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:70:5
+  --> $DIR/format.rs:67:5
    |
 LL |     format!("{}", 42.to_string());
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:72:5
+  --> $DIR/format.rs:69:5
    |
 LL |     format!("{}", x.display().to_string());
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:76:18
+  --> $DIR/format.rs:73:18
    |
 LL |     let _ = Some(format!("{}", a + "bar"));
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:80:22
+  --> $DIR/format.rs:77:22
    |
 LL |     let _s: String = format!("{}", &*v.join("/n"));
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("/n")).to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:86:13
+  --> $DIR/format.rs:83:13
    |
 LL |     let _ = format!("{x}");
    |             ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:88:13
+  --> $DIR/format.rs:85:13
    |
 LL |     let _ = format!("{y}", y = x);
    |             ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:92:13
+  --> $DIR/format.rs:89:13
    |
 LL |     let _ = format!("{abc}");
    |             ^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `abc.to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:94:13
+  --> $DIR/format.rs:91:13
    |
 LL |     let _ = format!("{xx}");
    |             ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `xx.to_string()`
diff --git a/src/tools/clippy/tests/ui/impl_trait_in_params.stderr b/src/tools/clippy/tests/ui/impl_trait_in_params.stderr
index acfcc21445e..80383743525 100644
--- a/src/tools/clippy/tests/ui/impl_trait_in_params.stderr
+++ b/src/tools/clippy/tests/ui/impl_trait_in_params.stderr
@@ -5,7 +5,7 @@ LL | pub fn a(_: impl Trait) {}
    |             ^^^^^^^^^^
    |
    = note: `-D clippy::impl-trait-in-params` implied by `-D warnings`
-help: add a type paremeter
+help: add a type parameter
    |
 LL | pub fn a<{ /* Generic name */ }: Trait>(_: impl Trait) {}
    |         +++++++++++++++++++++++++++++++
@@ -16,7 +16,7 @@ error: '`impl Trait` used as a function parameter'
 LL | pub fn c<C: Trait>(_: C, _: impl Trait) {}
    |                             ^^^^^^^^^^
    |
-help: add a type paremeter
+help: add a type parameter
    |
 LL | pub fn c<C: Trait, { /* Generic name */ }: Trait>(_: C, _: impl Trait) {}
    |                  +++++++++++++++++++++++++++++++
diff --git a/src/tools/clippy/tests/ui/implicit_clone.fixed b/src/tools/clippy/tests/ui/implicit_clone.fixed
index 51b1afbe5ac..8ccc3da7b47 100644
--- a/src/tools/clippy/tests/ui/implicit_clone.fixed
+++ b/src/tools/clippy/tests/ui/implicit_clone.fixed
@@ -87,7 +87,7 @@ fn main() {
     let kitten = Kitten {};
     let _ = kitten.clone();
     let _ = own_same_from_ref(&kitten);
-    // this shouln't lint
+    // this shouldn't lint
     let _ = kitten.to_vec();
 
     // we expect no lints for this
diff --git a/src/tools/clippy/tests/ui/implicit_clone.rs b/src/tools/clippy/tests/ui/implicit_clone.rs
index 8a9027433d9..59333312607 100644
--- a/src/tools/clippy/tests/ui/implicit_clone.rs
+++ b/src/tools/clippy/tests/ui/implicit_clone.rs
@@ -87,7 +87,7 @@ fn main() {
     let kitten = Kitten {};
     let _ = kitten.to_owned();
     let _ = own_same_from_ref(&kitten);
-    // this shouln't lint
+    // this shouldn't lint
     let _ = kitten.to_vec();
 
     // we expect no lints for this
diff --git a/src/tools/clippy/tests/ui/len_without_is_empty.rs b/src/tools/clippy/tests/ui/len_without_is_empty.rs
index b5dec6c46bd..52aabefaed2 100644
--- a/src/tools/clippy/tests/ui/len_without_is_empty.rs
+++ b/src/tools/clippy/tests/ui/len_without_is_empty.rs
@@ -282,6 +282,87 @@ impl AsyncLen {
     }
 }
 
+// issue #7232
+pub struct AsyncLenWithoutIsEmpty;
+impl AsyncLenWithoutIsEmpty {
+    pub async fn async_task(&self) -> bool {
+        true
+    }
+
+    pub async fn len(&self) -> usize {
+        usize::from(!self.async_task().await)
+    }
+}
+
+// issue #7232
+pub struct AsyncOptionLenWithoutIsEmpty;
+impl AsyncOptionLenWithoutIsEmpty {
+    async fn async_task(&self) -> bool {
+        true
+    }
+
+    pub async fn len(&self) -> Option<usize> {
+        None
+    }
+}
+
+// issue #7232
+pub struct AsyncOptionLenNonIntegral;
+impl AsyncOptionLenNonIntegral {
+    // don't lint
+    pub async fn len(&self) -> Option<String> {
+        None
+    }
+}
+
+// issue #7232
+pub struct AsyncResultLenWithoutIsEmpty;
+impl AsyncResultLenWithoutIsEmpty {
+    async fn async_task(&self) -> bool {
+        true
+    }
+
+    pub async fn len(&self) -> Result<usize, ()> {
+        Err(())
+    }
+}
+
+// issue #7232
+pub struct AsyncOptionLen;
+impl AsyncOptionLen {
+    async fn async_task(&self) -> bool {
+        true
+    }
+
+    pub async fn len(&self) -> Result<usize, ()> {
+        Err(())
+    }
+
+    pub async fn is_empty(&self) -> bool {
+        true
+    }
+}
+
+pub struct AsyncLenSyncIsEmpty;
+impl AsyncLenSyncIsEmpty {
+    pub async fn len(&self) -> u32 {
+        0
+    }
+
+    pub fn is_empty(&self) -> bool {
+        true
+    }
+}
+
+// issue #9520
+pub struct NonStandardLen;
+impl NonStandardLen {
+    // don't lint
+    pub fn len(&self, something: usize) -> usize {
+        something
+    }
+}
+
 // issue #9520
 pub struct NonStandardLenAndIsEmptySignature;
 impl NonStandardLenAndIsEmptySignature {
@@ -328,4 +409,15 @@ impl NonStandardSignatureWithGenerics {
     }
 }
 
+pub struct DifferingErrors;
+impl DifferingErrors {
+    pub fn len(&self) -> Result<usize, u8> {
+        Ok(0)
+    }
+
+    pub fn is_empty(&self) -> Result<bool, u16> {
+        Ok(true)
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/len_without_is_empty.stderr b/src/tools/clippy/tests/ui/len_without_is_empty.stderr
index 8e890e2e259..1bce1734b81 100644
--- a/src/tools/clippy/tests/ui/len_without_is_empty.stderr
+++ b/src/tools/clippy/tests/ui/len_without_is_empty.stderr
@@ -119,5 +119,23 @@ LL |     pub fn len(&self) -> Result<usize, ()> {
    |
    = help: use a custom `Error` type instead
 
-error: aborting due to 12 previous errors
+error: struct `AsyncLenWithoutIsEmpty` has a public `len` method, but no `is_empty` method
+  --> $DIR/len_without_is_empty.rs:292:5
+   |
+LL |     pub async fn len(&self) -> usize {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: struct `AsyncOptionLenWithoutIsEmpty` has a public `len` method, but no `is_empty` method
+  --> $DIR/len_without_is_empty.rs:304:5
+   |
+LL |     pub async fn len(&self) -> Option<usize> {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: struct `AsyncResultLenWithoutIsEmpty` has a public `len` method, but no `is_empty` method
+  --> $DIR/len_without_is_empty.rs:325:5
+   |
+LL |     pub async fn len(&self) -> Result<usize, ()> {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 15 previous errors
 
diff --git a/src/tools/clippy/tests/ui/let_unit.fixed b/src/tools/clippy/tests/ui/let_unit.fixed
index 6343cff0f7f..76ff0645f41 100644
--- a/src/tools/clippy/tests/ui/let_unit.fixed
+++ b/src/tools/clippy/tests/ui/let_unit.fixed
@@ -175,3 +175,7 @@ fn attributes() {
     #[expect(clippy::let_unit_value)]
     let _ = f();
 }
+
+async fn issue10433() {
+    let _pending: () = std::future::pending().await;
+}
diff --git a/src/tools/clippy/tests/ui/let_unit.rs b/src/tools/clippy/tests/ui/let_unit.rs
index c9bb2849f5c..895ccfe366a 100644
--- a/src/tools/clippy/tests/ui/let_unit.rs
+++ b/src/tools/clippy/tests/ui/let_unit.rs
@@ -175,3 +175,7 @@ fn attributes() {
     #[expect(clippy::let_unit_value)]
     let _ = f();
 }
+
+async fn issue10433() {
+    let _pending: () = std::future::pending().await;
+}
diff --git a/src/tools/clippy/tests/ui/let_with_type_underscore.rs b/src/tools/clippy/tests/ui/let_with_type_underscore.rs
new file mode 100644
index 00000000000..175718b94c8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/let_with_type_underscore.rs
@@ -0,0 +1,19 @@
+#![allow(unused)]
+#![warn(clippy::let_with_type_underscore)]
+#![allow(clippy::let_unit_value)]
+
+fn func() -> &'static str {
+    ""
+}
+
+fn main() {
+    // Will lint
+    let x: _ = 1;
+    let _: _ = 2;
+    let x: _ = func();
+
+    let x = 1; // Will not lint, Rust inferres this to an integer before Clippy
+    let x = func();
+    let x: Vec<_> = Vec::<u32>::new();
+    let x: [_; 1] = [1];
+}
diff --git a/src/tools/clippy/tests/ui/let_with_type_underscore.stderr b/src/tools/clippy/tests/ui/let_with_type_underscore.stderr
new file mode 100644
index 00000000000..16bf83c708f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/let_with_type_underscore.stderr
@@ -0,0 +1,39 @@
+error: variable declared with type underscore
+  --> $DIR/let_with_type_underscore.rs:11:5
+   |
+LL |     let x: _ = 1;
+   |     ^^^^^^^^^^^^^
+   |
+help: remove the explicit type `_` declaration
+  --> $DIR/let_with_type_underscore.rs:11:10
+   |
+LL |     let x: _ = 1;
+   |          ^^^
+   = note: `-D clippy::let-with-type-underscore` implied by `-D warnings`
+
+error: variable declared with type underscore
+  --> $DIR/let_with_type_underscore.rs:12:5
+   |
+LL |     let _: _ = 2;
+   |     ^^^^^^^^^^^^^
+   |
+help: remove the explicit type `_` declaration
+  --> $DIR/let_with_type_underscore.rs:12:10
+   |
+LL |     let _: _ = 2;
+   |          ^^^
+
+error: variable declared with type underscore
+  --> $DIR/let_with_type_underscore.rs:13:5
+   |
+LL |     let x: _ = func();
+   |     ^^^^^^^^^^^^^^^^^^
+   |
+help: remove the explicit type `_` declaration
+  --> $DIR/let_with_type_underscore.rs:13:10
+   |
+LL |     let x: _ = func();
+   |          ^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_rem_euclid.fixed b/src/tools/clippy/tests/ui/manual_rem_euclid.fixed
index 4cdc0546a74..6916a284a20 100644
--- a/src/tools/clippy/tests/ui/manual_rem_euclid.fixed
+++ b/src/tools/clippy/tests/ui/manual_rem_euclid.fixed
@@ -2,6 +2,7 @@
 // aux-build:macro_rules.rs
 
 #![warn(clippy::manual_rem_euclid)]
+#![allow(clippy::let_with_type_underscore)]
 
 #[macro_use]
 extern crate macro_rules;
diff --git a/src/tools/clippy/tests/ui/manual_rem_euclid.rs b/src/tools/clippy/tests/ui/manual_rem_euclid.rs
index 58a9e20f38b..412dbddb426 100644
--- a/src/tools/clippy/tests/ui/manual_rem_euclid.rs
+++ b/src/tools/clippy/tests/ui/manual_rem_euclid.rs
@@ -2,6 +2,7 @@
 // aux-build:macro_rules.rs
 
 #![warn(clippy::manual_rem_euclid)]
+#![allow(clippy::let_with_type_underscore)]
 
 #[macro_use]
 extern crate macro_rules;
diff --git a/src/tools/clippy/tests/ui/manual_rem_euclid.stderr b/src/tools/clippy/tests/ui/manual_rem_euclid.stderr
index e3122a588b6..6d06654638b 100644
--- a/src/tools/clippy/tests/ui/manual_rem_euclid.stderr
+++ b/src/tools/clippy/tests/ui/manual_rem_euclid.stderr
@@ -1,5 +1,5 @@
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:19:18
+  --> $DIR/manual_rem_euclid.rs:20:18
    |
 LL |     let _: i32 = ((value % 4) + 4) % 4;
    |                  ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
@@ -7,31 +7,31 @@ LL |     let _: i32 = ((value % 4) + 4) % 4;
    = note: `-D clippy::manual-rem-euclid` implied by `-D warnings`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:20:18
+  --> $DIR/manual_rem_euclid.rs:21:18
    |
 LL |     let _: i32 = (4 + (value % 4)) % 4;
    |                  ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:21:18
+  --> $DIR/manual_rem_euclid.rs:22:18
    |
 LL |     let _: i32 = (value % 4 + 4) % 4;
    |                  ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:22:18
+  --> $DIR/manual_rem_euclid.rs:23:18
    |
 LL |     let _: i32 = (4 + value % 4) % 4;
    |                  ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:23:22
+  --> $DIR/manual_rem_euclid.rs:24:22
    |
 LL |     let _: i32 = 1 + (4 + value % 4) % 4;
    |                      ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:12:22
+  --> $DIR/manual_rem_euclid.rs:13:22
    |
 LL |         let _: i32 = ((value % 4) + 4) % 4;
    |                      ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
@@ -42,25 +42,25 @@ LL |     internal_rem_euclid!();
    = note: this error originates in the macro `internal_rem_euclid` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:49:5
+  --> $DIR/manual_rem_euclid.rs:50:5
    |
 LL |     ((num % 4) + 4) % 4
    |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:54:5
+  --> $DIR/manual_rem_euclid.rs:55:5
    |
 LL |     ((num % 4) + 4) % 4
    |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:66:18
+  --> $DIR/manual_rem_euclid.rs:67:18
    |
 LL |     let _: i32 = ((x % 4) + 4) % 4;
    |                  ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:79:18
+  --> $DIR/manual_rem_euclid.rs:80:18
    |
 LL |     let _: i32 = ((x % 4) + 4) % 4;
    |                  ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)`
diff --git a/src/tools/clippy/tests/ui/match_result_ok.fixed b/src/tools/clippy/tests/ui/match_result_ok.fixed
index 8b91b9854a0..10ae1ee5245 100644
--- a/src/tools/clippy/tests/ui/match_result_ok.fixed
+++ b/src/tools/clippy/tests/ui/match_result_ok.fixed
@@ -16,7 +16,7 @@ fn str_to_int_ok(x: &str) -> i32 {
 #[rustfmt::skip]
 fn strange_some_no_else(x: &str) -> i32 {
     {
-        if let Ok(y) = x   .   parse()       {
+        if let Ok(y) = x   .   parse()    {
             return y;
         };
         0
diff --git a/src/tools/clippy/tests/ui/match_result_ok.stderr b/src/tools/clippy/tests/ui/match_result_ok.stderr
index 98a95705ca5..cbdc56aa28c 100644
--- a/src/tools/clippy/tests/ui/match_result_ok.stderr
+++ b/src/tools/clippy/tests/ui/match_result_ok.stderr
@@ -18,7 +18,7 @@ LL |         if let Some(y) = x   .   parse()   .   ok   ()    {
    |
 help: consider matching on `Ok(y)` and removing the call to `ok` instead
    |
-LL |         if let Ok(y) = x   .   parse()       {
+LL |         if let Ok(y) = x   .   parse()    {
    |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: matching on `Some` with `ok()` is redundant
diff --git a/src/tools/clippy/tests/ui/missing_assert_message.rs b/src/tools/clippy/tests/ui/missing_assert_message.rs
new file mode 100644
index 00000000000..89404ca8827
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_assert_message.rs
@@ -0,0 +1,84 @@
+#![allow(unused)]
+#![warn(clippy::missing_assert_message)]
+
+macro_rules! bar {
+    ($( $x:expr ),*) => {
+        foo()
+    };
+}
+
+fn main() {}
+
+// Should trigger warning
+fn asserts_without_message() {
+    assert!(foo());
+    assert_eq!(foo(), foo());
+    assert_ne!(foo(), foo());
+    debug_assert!(foo());
+    debug_assert_eq!(foo(), foo());
+    debug_assert_ne!(foo(), foo());
+}
+
+// Should trigger warning
+fn asserts_without_message_but_with_macro_calls() {
+    assert!(bar!(true));
+    assert!(bar!(true, false));
+    assert_eq!(bar!(true), foo());
+    assert_ne!(bar!(true, true), bar!(true));
+}
+
+// Should trigger warning
+fn asserts_with_trailing_commas() {
+    assert!(foo(),);
+    assert_eq!(foo(), foo(),);
+    assert_ne!(foo(), foo(),);
+    debug_assert!(foo(),);
+    debug_assert_eq!(foo(), foo(),);
+    debug_assert_ne!(foo(), foo(),);
+}
+
+// Should not trigger warning
+fn asserts_with_message_and_with_macro_calls() {
+    assert!(bar!(true), "msg");
+    assert!(bar!(true, false), "msg");
+    assert_eq!(bar!(true), foo(), "msg");
+    assert_ne!(bar!(true, true), bar!(true), "msg");
+}
+
+// Should not trigger warning
+fn asserts_with_message() {
+    assert!(foo(), "msg");
+    assert_eq!(foo(), foo(), "msg");
+    assert_ne!(foo(), foo(), "msg");
+    debug_assert!(foo(), "msg");
+    debug_assert_eq!(foo(), foo(), "msg");
+    debug_assert_ne!(foo(), foo(), "msg");
+}
+
+// Should not trigger warning
+#[test]
+fn asserts_without_message_but_inside_a_test_function() {
+    assert!(foo());
+    assert_eq!(foo(), foo());
+    assert_ne!(foo(), foo());
+    debug_assert!(foo());
+    debug_assert_eq!(foo(), foo());
+    debug_assert_ne!(foo(), foo());
+}
+
+// Should not trigger warning
+#[cfg(test)]
+mod tests {
+    fn asserts_without_message_but_inside_a_test_module() {
+        assert!(foo());
+        assert_eq!(foo(), foo());
+        assert_ne!(foo(), foo());
+        debug_assert!(foo());
+        debug_assert_eq!(foo(), foo());
+        debug_assert_ne!(foo(), foo());
+    }
+}
+
+fn foo() -> bool {
+    true
+}
diff --git a/src/tools/clippy/tests/ui/missing_assert_message.stderr b/src/tools/clippy/tests/ui/missing_assert_message.stderr
new file mode 100644
index 00000000000..ecd03801277
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_assert_message.stderr
@@ -0,0 +1,131 @@
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:14:5
+   |
+LL |     assert!(foo());
+   |     ^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+   = note: `-D clippy::missing-assert-message` implied by `-D warnings`
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:15:5
+   |
+LL |     assert_eq!(foo(), foo());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:16:5
+   |
+LL |     assert_ne!(foo(), foo());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:17:5
+   |
+LL |     debug_assert!(foo());
+   |     ^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:18:5
+   |
+LL |     debug_assert_eq!(foo(), foo());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:19:5
+   |
+LL |     debug_assert_ne!(foo(), foo());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:24:5
+   |
+LL |     assert!(bar!(true));
+   |     ^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:25:5
+   |
+LL |     assert!(bar!(true, false));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:26:5
+   |
+LL |     assert_eq!(bar!(true), foo());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:27:5
+   |
+LL |     assert_ne!(bar!(true, true), bar!(true));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:32:5
+   |
+LL |     assert!(foo(),);
+   |     ^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:33:5
+   |
+LL |     assert_eq!(foo(), foo(),);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:34:5
+   |
+LL |     assert_ne!(foo(), foo(),);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:35:5
+   |
+LL |     debug_assert!(foo(),);
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:36:5
+   |
+LL |     debug_assert_eq!(foo(), foo(),);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+  --> $DIR/missing_assert_message.rs:37:5
+   |
+LL |     debug_assert_ne!(foo(), foo(),);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider describing why the failing assert is problematic
+
+error: aborting due to 16 previous errors
+
diff --git a/src/tools/clippy/tests/ui/missing_doc.stderr b/src/tools/clippy/tests/ui/missing_doc.stderr
index d3bef28bf64..4e8a49bf1cd 100644
--- a/src/tools/clippy/tests/ui/missing_doc.stderr
+++ b/src/tools/clippy/tests/ui/missing_doc.stderr
@@ -6,30 +6,12 @@ LL | type Typedef = String;
    |
    = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings`
 
-error: missing documentation for a type alias
-  --> $DIR/missing_doc.rs:17:1
-   |
-LL | pub type PubTypedef = String;
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
 error: missing documentation for a module
   --> $DIR/missing_doc.rs:19:1
    |
 LL | mod module_no_dox {}
    | ^^^^^^^^^^^^^^^^^^^^
 
-error: missing documentation for a module
-  --> $DIR/missing_doc.rs:20:1
-   |
-LL | pub mod pub_module_no_dox {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: missing documentation for a function
-  --> $DIR/missing_doc.rs:24:1
-   |
-LL | pub fn foo2() {}
-   | ^^^^^^^^^^^^^^^^
-
 error: missing documentation for a function
   --> $DIR/missing_doc.rs:25:1
    |
@@ -69,50 +51,18 @@ error: missing documentation for a variant
 LL |     BarB,
    |     ^^^^
 
-error: missing documentation for an enum
-  --> $DIR/missing_doc.rs:44:1
-   |
-LL | / pub enum PubBaz {
-LL | |     PubBazA { a: isize },
-LL | | }
-   | |_^
-
-error: missing documentation for a variant
-  --> $DIR/missing_doc.rs:45:5
-   |
-LL |     PubBazA { a: isize },
-   |     ^^^^^^^^^^^^^^^^^^^^
-
-error: missing documentation for a struct field
-  --> $DIR/missing_doc.rs:45:15
-   |
-LL |     PubBazA { a: isize },
-   |               ^^^^^^^^
-
 error: missing documentation for a constant
   --> $DIR/missing_doc.rs:65:1
    |
 LL | const FOO: u32 = 0;
    | ^^^^^^^^^^^^^^^^^^^
 
-error: missing documentation for a constant
-  --> $DIR/missing_doc.rs:72:1
-   |
-LL | pub const FOO4: u32 = 0;
-   | ^^^^^^^^^^^^^^^^^^^^^^^^
-
 error: missing documentation for a static
   --> $DIR/missing_doc.rs:74:1
    |
 LL | static BAR: u32 = 0;
    | ^^^^^^^^^^^^^^^^^^^^
 
-error: missing documentation for a static
-  --> $DIR/missing_doc.rs:81:1
-   |
-LL | pub static BAR4: u32 = 0;
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
-
 error: missing documentation for a module
   --> $DIR/missing_doc.rs:83:1
    |
@@ -126,34 +76,16 @@ LL | | }
    | |_^
 
 error: missing documentation for a function
-  --> $DIR/missing_doc.rs:86:5
-   |
-LL |     pub fn undocumented1() {}
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: missing documentation for a function
-  --> $DIR/missing_doc.rs:87:5
-   |
-LL |     pub fn undocumented2() {}
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: missing documentation for a function
   --> $DIR/missing_doc.rs:88:5
    |
 LL |     fn undocumented3() {}
    |     ^^^^^^^^^^^^^^^^^^^^^
 
 error: missing documentation for a function
-  --> $DIR/missing_doc.rs:93:9
-   |
-LL |         pub fn also_undocumented1() {}
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: missing documentation for a function
   --> $DIR/missing_doc.rs:94:9
    |
 LL |         fn also_undocumented2() {}
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 24 previous errors
+error: aborting due to 13 previous errors
 
diff --git a/src/tools/clippy/tests/ui/missing_doc_impl.stderr b/src/tools/clippy/tests/ui/missing_doc_impl.stderr
index b410f56e167..111d6546966 100644
--- a/src/tools/clippy/tests/ui/missing_doc_impl.stderr
+++ b/src/tools/clippy/tests/ui/missing_doc_impl.stderr
@@ -21,60 +21,12 @@ error: missing documentation for a struct field
 LL |     b: isize,
    |     ^^^^^^^^
 
-error: missing documentation for a struct
-  --> $DIR/missing_doc_impl.rs:18:1
-   |
-LL | / pub struct PubFoo {
-LL | |     pub a: isize,
-LL | |     b: isize,
-LL | | }
-   | |_^
-
-error: missing documentation for a struct field
-  --> $DIR/missing_doc_impl.rs:19:5
-   |
-LL |     pub a: isize,
-   |     ^^^^^^^^^^^^
-
 error: missing documentation for a struct field
   --> $DIR/missing_doc_impl.rs:20:5
    |
 LL |     b: isize,
    |     ^^^^^^^^
 
-error: missing documentation for a trait
-  --> $DIR/missing_doc_impl.rs:43:1
-   |
-LL | / pub trait C {
-LL | |     fn foo(&self);
-LL | |     fn foo_with_impl(&self) {}
-LL | | }
-   | |_^
-
-error: missing documentation for a method
-  --> $DIR/missing_doc_impl.rs:44:5
-   |
-LL |     fn foo(&self);
-   |     ^^^^^^^^^^^^^^
-
-error: missing documentation for a method
-  --> $DIR/missing_doc_impl.rs:45:5
-   |
-LL |     fn foo_with_impl(&self) {}
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: missing documentation for an associated type
-  --> $DIR/missing_doc_impl.rs:55:5
-   |
-LL |     type AssociatedType;
-   |     ^^^^^^^^^^^^^^^^^^^^
-
-error: missing documentation for an associated type
-  --> $DIR/missing_doc_impl.rs:56:5
-   |
-LL |     type AssociatedTypeDef = Self;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
 error: missing documentation for an associated function
   --> $DIR/missing_doc_impl.rs:67:5
    |
@@ -90,12 +42,6 @@ LL |     fn bar() {}
    |     ^^^^^^^^^^^
 
 error: missing documentation for an associated function
-  --> $DIR/missing_doc_impl.rs:74:5
-   |
-LL |     pub fn foo() {}
-   |     ^^^^^^^^^^^^^^^
-
-error: missing documentation for an associated function
   --> $DIR/missing_doc_impl.rs:78:5
    |
 LL | /     fn foo2() -> u32 {
@@ -103,5 +49,5 @@ LL | |         1
 LL | |     }
    | |_____^
 
-error: aborting due to 15 previous errors
+error: aborting due to 7 previous errors
 
diff --git a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs
index 4511bc99c3c..5073685c9f0 100644
--- a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs
+++ b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs
@@ -116,4 +116,32 @@ fn issue10259() {
     unsafe_macro!();
 }
 
+fn _fn_ptr(x: unsafe fn()) {
+    unsafe {
+        x();
+        x();
+    }
+}
+
+fn _assoc_const() {
+    trait X {
+        const X: unsafe fn();
+    }
+    fn _f<T: X>() {
+        unsafe {
+            T::X();
+            T::X();
+        }
+    }
+}
+
+fn _field_fn_ptr(x: unsafe fn()) {
+    struct X(unsafe fn());
+    let x = X(x);
+    unsafe {
+        x.0();
+        x.0();
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr
index 303aeb7aee0..e0c1d3801f7 100644
--- a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr
+++ b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr
@@ -125,5 +125,65 @@ note: raw pointer dereference occurs here
 LL |     unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
    |                                       ^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 5 previous errors
+error: this `unsafe` block contains 2 unsafe operations, expected only one
+  --> $DIR/multiple_unsafe_ops_per_block.rs:120:5
+   |
+LL | /     unsafe {
+LL | |         x();
+LL | |         x();
+LL | |     }
+   | |_____^
+   |
+note: unsafe function call occurs here
+  --> $DIR/multiple_unsafe_ops_per_block.rs:121:9
+   |
+LL |         x();
+   |         ^^^
+note: unsafe function call occurs here
+  --> $DIR/multiple_unsafe_ops_per_block.rs:122:9
+   |
+LL |         x();
+   |         ^^^
+
+error: this `unsafe` block contains 2 unsafe operations, expected only one
+  --> $DIR/multiple_unsafe_ops_per_block.rs:131:9
+   |
+LL | /         unsafe {
+LL | |             T::X();
+LL | |             T::X();
+LL | |         }
+   | |_________^
+   |
+note: unsafe function call occurs here
+  --> $DIR/multiple_unsafe_ops_per_block.rs:132:13
+   |
+LL |             T::X();
+   |             ^^^^^^
+note: unsafe function call occurs here
+  --> $DIR/multiple_unsafe_ops_per_block.rs:133:13
+   |
+LL |             T::X();
+   |             ^^^^^^
+
+error: this `unsafe` block contains 2 unsafe operations, expected only one
+  --> $DIR/multiple_unsafe_ops_per_block.rs:141:5
+   |
+LL | /     unsafe {
+LL | |         x.0();
+LL | |         x.0();
+LL | |     }
+   | |_____^
+   |
+note: unsafe function call occurs here
+  --> $DIR/multiple_unsafe_ops_per_block.rs:142:9
+   |
+LL |         x.0();
+   |         ^^^^^
+note: unsafe function call occurs here
+  --> $DIR/multiple_unsafe_ops_per_block.rs:143:9
+   |
+LL |         x.0();
+   |         ^^^^^
+
+error: aborting due to 8 previous errors
 
diff --git a/src/tools/clippy/tests/ui/new_ret_no_self.rs b/src/tools/clippy/tests/ui/new_ret_no_self.rs
index beec42f08bb..a2a30c8b931 100644
--- a/src/tools/clippy/tests/ui/new_ret_no_self.rs
+++ b/src/tools/clippy/tests/ui/new_ret_no_self.rs
@@ -406,7 +406,7 @@ mod issue10041 {
     struct Bomb;
 
     impl Bomb {
-        // Hidden <Rhs = Self> default generic paramter.
+        // Hidden <Rhs = Self> default generic parameter.
         pub fn new() -> impl PartialOrd {
             0i32
         }
diff --git a/src/tools/clippy/tests/ui/redundant_async_block.fixed b/src/tools/clippy/tests/ui/redundant_async_block.fixed
new file mode 100644
index 00000000000..5f9931df45e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_async_block.fixed
@@ -0,0 +1,64 @@
+// run-rustfix
+
+#![allow(unused)]
+#![warn(clippy::redundant_async_block)]
+
+async fn func1(n: usize) -> usize {
+    n + 1
+}
+
+async fn func2() -> String {
+    let s = String::from("some string");
+    let f = async { (*s).to_owned() };
+    let x = f;
+    x.await
+}
+
+macro_rules! await_in_macro {
+    ($e:expr) => {
+        std::convert::identity($e).await
+    };
+}
+
+async fn func3(n: usize) -> usize {
+    // Do not lint (suggestion would be `std::convert::identity(func1(n))`
+    // which copies code from inside the macro)
+    async move { await_in_macro!(func1(n)) }.await
+}
+
+// This macro should never be linted as `$e` might contain `.await`
+macro_rules! async_await_parameter_in_macro {
+    ($e:expr) => {
+        async { $e.await }
+    };
+}
+
+// MISSED OPPORTUNITY: this macro could be linted as the `async` block does not
+// contain code coming from the parameters
+macro_rules! async_await_in_macro {
+    ($f:expr) => {
+        ($f)(async { func2().await })
+    };
+}
+
+fn main() {
+    let fut1 = async { 17 };
+    let fut2 = fut1;
+
+    let fut1 = async { 25 };
+    let fut2 = fut1;
+
+    let fut = async { 42 };
+
+    // Do not lint: not a single expression
+    let fut = async {
+        func1(10).await;
+        func2().await
+    };
+
+    // Do not lint: expression contains `.await`
+    let fut = async { func1(func2().await.len()).await };
+
+    let fut = async_await_parameter_in_macro!(func2());
+    let fut = async_await_in_macro!(std::convert::identity);
+}
diff --git a/src/tools/clippy/tests/ui/redundant_async_block.rs b/src/tools/clippy/tests/ui/redundant_async_block.rs
new file mode 100644
index 00000000000..de3c9970c65
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_async_block.rs
@@ -0,0 +1,64 @@
+// run-rustfix
+
+#![allow(unused)]
+#![warn(clippy::redundant_async_block)]
+
+async fn func1(n: usize) -> usize {
+    n + 1
+}
+
+async fn func2() -> String {
+    let s = String::from("some string");
+    let f = async { (*s).to_owned() };
+    let x = async { f.await };
+    x.await
+}
+
+macro_rules! await_in_macro {
+    ($e:expr) => {
+        std::convert::identity($e).await
+    };
+}
+
+async fn func3(n: usize) -> usize {
+    // Do not lint (suggestion would be `std::convert::identity(func1(n))`
+    // which copies code from inside the macro)
+    async move { await_in_macro!(func1(n)) }.await
+}
+
+// This macro should never be linted as `$e` might contain `.await`
+macro_rules! async_await_parameter_in_macro {
+    ($e:expr) => {
+        async { $e.await }
+    };
+}
+
+// MISSED OPPORTUNITY: this macro could be linted as the `async` block does not
+// contain code coming from the parameters
+macro_rules! async_await_in_macro {
+    ($f:expr) => {
+        ($f)(async { func2().await })
+    };
+}
+
+fn main() {
+    let fut1 = async { 17 };
+    let fut2 = async { fut1.await };
+
+    let fut1 = async { 25 };
+    let fut2 = async move { fut1.await };
+
+    let fut = async { async { 42 }.await };
+
+    // Do not lint: not a single expression
+    let fut = async {
+        func1(10).await;
+        func2().await
+    };
+
+    // Do not lint: expression contains `.await`
+    let fut = async { func1(func2().await.len()).await };
+
+    let fut = async_await_parameter_in_macro!(func2());
+    let fut = async_await_in_macro!(std::convert::identity);
+}
diff --git a/src/tools/clippy/tests/ui/redundant_async_block.stderr b/src/tools/clippy/tests/ui/redundant_async_block.stderr
new file mode 100644
index 00000000000..b16d96dce84
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_async_block.stderr
@@ -0,0 +1,28 @@
+error: this async expression only awaits a single future
+  --> $DIR/redundant_async_block.rs:13:13
+   |
+LL |     let x = async { f.await };
+   |             ^^^^^^^^^^^^^^^^^ help: you can reduce it to: `f`
+   |
+   = note: `-D clippy::redundant-async-block` implied by `-D warnings`
+
+error: this async expression only awaits a single future
+  --> $DIR/redundant_async_block.rs:46:16
+   |
+LL |     let fut2 = async { fut1.await };
+   |                ^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `fut1`
+
+error: this async expression only awaits a single future
+  --> $DIR/redundant_async_block.rs:49:16
+   |
+LL |     let fut2 = async move { fut1.await };
+   |                ^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `fut1`
+
+error: this async expression only awaits a single future
+  --> $DIR/redundant_async_block.rs:51:15
+   |
+LL |     let fut = async { async { 42 }.await };
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `async { 42 }`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed
index c0e49ff4caa..b987fd2ce6f 100644
--- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed
@@ -2,6 +2,7 @@
 
 #![feature(async_closure)]
 #![warn(clippy::redundant_closure_call)]
+#![allow(clippy::redundant_async_block)]
 #![allow(unused)]
 
 async fn something() -> u32 {
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs
index 9e6e54348a8..633a2979d5d 100644
--- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs
@@ -2,6 +2,7 @@
 
 #![feature(async_closure)]
 #![warn(clippy::redundant_closure_call)]
+#![allow(clippy::redundant_async_block)]
 #![allow(unused)]
 
 async fn something() -> u32 {
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr
index d71bcba2a82..8a1f0771659 100644
--- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr
@@ -1,5 +1,5 @@
 error: try not to call a closure in the expression where it is declared
-  --> $DIR/redundant_closure_call_fixable.rs:16:13
+  --> $DIR/redundant_closure_call_fixable.rs:17:13
    |
 LL |     let a = (|| 42)();
    |             ^^^^^^^^^ help: try doing something like: `42`
@@ -7,7 +7,7 @@ LL |     let a = (|| 42)();
    = note: `-D clippy::redundant-closure-call` implied by `-D warnings`
 
 error: try not to call a closure in the expression where it is declared
-  --> $DIR/redundant_closure_call_fixable.rs:17:13
+  --> $DIR/redundant_closure_call_fixable.rs:18:13
    |
 LL |       let b = (async || {
    |  _____________^
@@ -27,7 +27,7 @@ LL ~     };
    |
 
 error: try not to call a closure in the expression where it is declared
-  --> $DIR/redundant_closure_call_fixable.rs:22:13
+  --> $DIR/redundant_closure_call_fixable.rs:23:13
    |
 LL |       let c = (|| {
    |  _____________^
@@ -47,13 +47,13 @@ LL ~     };
    |
 
 error: try not to call a closure in the expression where it is declared
-  --> $DIR/redundant_closure_call_fixable.rs:27:13
+  --> $DIR/redundant_closure_call_fixable.rs:28:13
    |
 LL |     let d = (async || something().await)();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async { something().await }`
 
 error: try not to call a closure in the expression where it is declared
-  --> $DIR/redundant_closure_call_fixable.rs:36:13
+  --> $DIR/redundant_closure_call_fixable.rs:37:13
    |
 LL |             (|| m!())()
    |             ^^^^^^^^^^^ help: try doing something like: `m!()`
@@ -64,7 +64,7 @@ LL |     m2!();
    = note: this error originates in the macro `m2` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: try not to call a closure in the expression where it is declared
-  --> $DIR/redundant_closure_call_fixable.rs:31:13
+  --> $DIR/redundant_closure_call_fixable.rs:32:13
    |
 LL |             (|| 0)()
    |             ^^^^^^^^ help: try doing something like: `0`
diff --git a/src/tools/clippy/tests/ui/swap.fixed b/src/tools/clippy/tests/ui/swap.fixed
index fa89706a815..04008c0d9b3 100644
--- a/src/tools/clippy/tests/ui/swap.fixed
+++ b/src/tools/clippy/tests/ui/swap.fixed
@@ -65,19 +65,19 @@ fn xor_swap_locals() {
     // This is an xor-based swap of local variables.
     let mut a = 0;
     let mut b = 1;
-    std::mem::swap(&mut a, &mut b)
+    std::mem::swap(&mut a, &mut b);
 }
 
 fn xor_field_swap() {
     // This is an xor-based swap of fields in a struct.
     let mut bar = Bar { a: 0, b: 1 };
-    std::mem::swap(&mut bar.a, &mut bar.b)
+    std::mem::swap(&mut bar.a, &mut bar.b);
 }
 
 fn xor_slice_swap() {
     // This is an xor-based swap of a slice
     let foo = &mut [1, 2];
-    foo.swap(0, 1)
+    foo.swap(0, 1);
 }
 
 fn xor_no_swap() {
diff --git a/src/tools/clippy/tests/ui/swap.stderr b/src/tools/clippy/tests/ui/swap.stderr
index f0acbfe253f..825c9261e19 100644
--- a/src/tools/clippy/tests/ui/swap.stderr
+++ b/src/tools/clippy/tests/ui/swap.stderr
@@ -4,7 +4,7 @@ error: this looks like you are swapping `bar.a` and `bar.b` manually
 LL | /     let temp = bar.a;
 LL | |     bar.a = bar.b;
 LL | |     bar.b = temp;
-   | |________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b)`
+   | |_________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b);`
    |
    = note: or maybe you should use `std::mem::replace`?
    = note: `-D clippy::manual-swap` implied by `-D warnings`
@@ -15,7 +15,7 @@ error: this looks like you are swapping elements of `foo` manually
 LL | /     let temp = foo[0];
 LL | |     foo[0] = foo[1];
 LL | |     foo[1] = temp;
-   | |_________________^ help: try: `foo.swap(0, 1)`
+   | |__________________^ help: try: `foo.swap(0, 1);`
 
 error: this looks like you are swapping elements of `foo` manually
   --> $DIR/swap.rs:46:5
@@ -23,7 +23,7 @@ error: this looks like you are swapping elements of `foo` manually
 LL | /     let temp = foo[0];
 LL | |     foo[0] = foo[1];
 LL | |     foo[1] = temp;
-   | |_________________^ help: try: `foo.swap(0, 1)`
+   | |__________________^ help: try: `foo.swap(0, 1);`
 
 error: this looks like you are swapping elements of `foo` manually
   --> $DIR/swap.rs:65:5
@@ -31,7 +31,7 @@ error: this looks like you are swapping elements of `foo` manually
 LL | /     let temp = foo[0];
 LL | |     foo[0] = foo[1];
 LL | |     foo[1] = temp;
-   | |_________________^ help: try: `foo.swap(0, 1)`
+   | |__________________^ help: try: `foo.swap(0, 1);`
 
 error: this looks like you are swapping `a` and `b` manually
   --> $DIR/swap.rs:76:5
@@ -39,7 +39,7 @@ error: this looks like you are swapping `a` and `b` manually
 LL | /     a ^= b;
 LL | |     b ^= a;
 LL | |     a ^= b;
-   | |___________^ help: try: `std::mem::swap(&mut a, &mut b)`
+   | |___________^ help: try: `std::mem::swap(&mut a, &mut b);`
 
 error: this looks like you are swapping `bar.a` and `bar.b` manually
   --> $DIR/swap.rs:84:5
@@ -47,7 +47,7 @@ error: this looks like you are swapping `bar.a` and `bar.b` manually
 LL | /     bar.a ^= bar.b;
 LL | |     bar.b ^= bar.a;
 LL | |     bar.a ^= bar.b;
-   | |___________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b)`
+   | |___________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b);`
 
 error: this looks like you are swapping elements of `foo` manually
   --> $DIR/swap.rs:92:5
@@ -55,7 +55,7 @@ error: this looks like you are swapping elements of `foo` manually
 LL | /     foo[0] ^= foo[1];
 LL | |     foo[1] ^= foo[0];
 LL | |     foo[0] ^= foo[1];
-   | |_____________________^ help: try: `foo.swap(0, 1)`
+   | |_____________________^ help: try: `foo.swap(0, 1);`
 
 error: this looks like you are swapping `foo[0][1]` and `bar[1][0]` manually
   --> $DIR/swap.rs:121:5
@@ -63,7 +63,7 @@ error: this looks like you are swapping `foo[0][1]` and `bar[1][0]` manually
 LL | /     let temp = foo[0][1];
 LL | |     foo[0][1] = bar[1][0];
 LL | |     bar[1][0] = temp;
-   | |____________________^ help: try: `std::mem::swap(&mut foo[0][1], &mut bar[1][0])`
+   | |_____________________^ help: try: `std::mem::swap(&mut foo[0][1], &mut bar[1][0]);`
    |
    = note: or maybe you should use `std::mem::replace`?
 
@@ -74,7 +74,7 @@ LL |       ; let t = a;
    |  _______^
 LL | |     a = b;
 LL | |     b = t;
-   | |_________^ help: try: `std::mem::swap(&mut a, &mut b)`
+   | |__________^ help: try: `std::mem::swap(&mut a, &mut b);`
    |
    = note: or maybe you should use `std::mem::replace`?
 
@@ -85,7 +85,7 @@ LL |       ; let t = c.0;
    |  _______^
 LL | |     c.0 = a;
 LL | |     a = t;
-   | |_________^ help: try: `std::mem::swap(&mut c.0, &mut a)`
+   | |__________^ help: try: `std::mem::swap(&mut c.0, &mut a);`
    |
    = note: or maybe you should use `std::mem::replace`?
 
@@ -95,7 +95,7 @@ error: this looks like you are swapping `b` and `a` manually
 LL | /     let t = b;
 LL | |     b = a;
 LL | |     a = t;
-   | |_________^ help: try: `std::mem::swap(&mut b, &mut a)`
+   | |__________^ help: try: `std::mem::swap(&mut b, &mut a);`
    |
    = note: or maybe you should use `std::mem::replace`?
 
@@ -151,7 +151,7 @@ error: this looks like you are swapping `s.0.x` and `s.0.y` manually
 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)`
+   | |______________^ help: try: `std::mem::swap(&mut s.0.x, &mut s.0.y);`
    |
    = note: or maybe you should use `std::mem::replace`?
 
diff --git a/src/tools/clippy/tests/ui/trailing_empty_array.rs b/src/tools/clippy/tests/ui/trailing_empty_array.rs
index c39b0bcaf22..8e3749eef35 100644
--- a/src/tools/clippy/tests/ui/trailing_empty_array.rs
+++ b/src/tools/clippy/tests/ui/trailing_empty_array.rs
@@ -155,7 +155,6 @@ struct TupleStructReprC(i32, [usize; 0]);
 
 type NamedTuple = (i32, [usize; 0]);
 
-#[rustfmt::skip] // [rustfmt#4995](https://github.com/rust-lang/rustfmt/issues/4995)
 struct ConstParamZeroDefault<const N: usize = 0> {
     field: i32,
     last: [usize; N],
@@ -166,7 +165,6 @@ struct ConstParamNoDefault<const N: usize> {
     last: [usize; N],
 }
 
-#[rustfmt::skip] 
 struct ConstParamNonZeroDefault<const N: usize = 1> {
     field: i32,
     last: [usize; N],
diff --git a/src/tools/clippy/tests/ui/use_self.fixed b/src/tools/clippy/tests/ui/use_self.fixed
index 0a6166571eb..3ac6217312a 100644
--- a/src/tools/clippy/tests/ui/use_self.fixed
+++ b/src/tools/clippy/tests/ui/use_self.fixed
@@ -647,3 +647,13 @@ fn msrv_1_37() {
         }
     }
 }
+
+mod issue_10371 {
+    struct Val<const V: i32> {}
+
+    impl<const V: i32> From<Val<V>> for i32 {
+        fn from(_: Val<V>) -> Self {
+            todo!()
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/use_self.rs b/src/tools/clippy/tests/ui/use_self.rs
index 39c2b431f7f..9dc5d1e3f9b 100644
--- a/src/tools/clippy/tests/ui/use_self.rs
+++ b/src/tools/clippy/tests/ui/use_self.rs
@@ -647,3 +647,13 @@ fn msrv_1_37() {
         }
     }
 }
+
+mod issue_10371 {
+    struct Val<const V: i32> {}
+
+    impl<const V: i32> From<Val<V>> for i32 {
+        fn from(_: Val<V>) -> Self {
+            todo!()
+        }
+    }
+}