about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPhilipp Krones <hello@philkrones.com>2022-12-01 12:39:42 +0100
committerPhilipp Krones <hello@philkrones.com>2022-12-01 12:55:15 +0100
commit11434f270fca0e403b695e99d4bf0a7212c46f14 (patch)
treee0750ced79b59251e18f58d38f26b64430c74264
parent58100c014a815f7bc30d58f27e4283166146fbbb (diff)
parent641ced4eb96d07fba2284fa258ccb987f7afc8a1 (diff)
downloadrust-11434f270fca0e403b695e99d4bf0a7212c46f14.tar.gz
rust-11434f270fca0e403b695e99d4bf0a7212c46f14.zip
Merge remote-tracking branch 'upstream/master' into rustup
-rw-r--r--CHANGELOG.md2
-rw-r--r--Cargo.toml1
-rw-r--r--README.md6
-rw-r--r--book/src/SUMMARY.md1
-rw-r--r--book/src/configuration.md6
-rw-r--r--book/src/development/adding_lints.md18
-rw-r--r--book/src/development/proposals/syntax-tree-patterns.md986
-rw-r--r--clippy_dev/src/new_lint.rs16
-rw-r--r--clippy_lints/Cargo.toml2
-rw-r--r--clippy_lints/src/almost_complete_letter_range.rs11
-rw-r--r--clippy_lints/src/approx_const.rs8
-rw-r--r--clippy_lints/src/attrs.rs12
-rw-r--r--clippy_lints/src/blocks_in_if_conditions.rs3
-rw-r--r--clippy_lints/src/casts/cast_abs_to_unsigned.rs7
-rw-r--r--clippy_lints/src/casts/cast_lossless.rs16
-rw-r--r--clippy_lints/src/casts/cast_possible_truncation.rs7
-rw-r--r--clippy_lints/src/casts/cast_slice_different_sizes.rs8
-rw-r--r--clippy_lints/src/casts/cast_slice_from_raw_parts.rs14
-rw-r--r--clippy_lints/src/casts/mod.rs22
-rw-r--r--clippy_lints/src/casts/ptr_as_ptr.rs7
-rw-r--r--clippy_lints/src/casts/unnecessary_cast.rs17
-rw-r--r--clippy_lints/src/checked_conversions.rs10
-rw-r--r--clippy_lints/src/collapsible_if.rs10
-rw-r--r--clippy_lints/src/declared_lints.rs2
-rw-r--r--clippy_lints/src/dereference.rs41
-rw-r--r--clippy_lints/src/derive.rs4
-rw-r--r--clippy_lints/src/doc.rs2
-rw-r--r--clippy_lints/src/eta_reduction.rs22
-rw-r--r--clippy_lints/src/exit.rs23
-rw-r--r--clippy_lints/src/format_args.rs100
-rw-r--r--clippy_lints/src/from_over_into.rs10
-rw-r--r--clippy_lints/src/functions/misnamed_getters.rs125
-rw-r--r--clippy_lints/src/functions/mod.rs45
-rw-r--r--clippy_lints/src/functions/result.rs8
-rw-r--r--clippy_lints/src/future_not_send.rs4
-rw-r--r--clippy_lints/src/if_then_some_else_none.rs14
-rw-r--r--clippy_lints/src/index_refutable_slice.rs13
-rw-r--r--clippy_lints/src/instant_subtraction.rs18
-rw-r--r--clippy_lints/src/lib.rs109
-rw-r--r--clippy_lints/src/lifetimes.rs13
-rw-r--r--clippy_lints/src/macro_use.rs3
-rw-r--r--clippy_lints/src/manual_bits.rs10
-rw-r--r--clippy_lints/src/manual_clamp.rs33
-rw-r--r--clippy_lints/src/manual_is_ascii_check.rs15
-rw-r--r--clippy_lints/src/manual_let_else.rs36
-rw-r--r--clippy_lints/src/manual_non_exhaustive.rs16
-rw-r--r--clippy_lints/src/manual_rem_euclid.rs12
-rw-r--r--clippy_lints/src/manual_retain.rs24
-rw-r--r--clippy_lints/src/manual_strip.rs10
-rw-r--r--clippy_lints/src/matches/mod.rs14
-rw-r--r--clippy_lints/src/matches/try_err.rs6
-rw-r--r--clippy_lints/src/mem_replace.rs10
-rw-r--r--clippy_lints/src/methods/cloned_instead_of_copied.rs10
-rw-r--r--clippy_lints/src/methods/err_expect.rs8
-rw-r--r--clippy_lints/src/methods/filter_map_next.rs8
-rw-r--r--clippy_lints/src/methods/inefficient_to_string.rs2
-rw-r--r--clippy_lints/src/methods/is_digit_ascii_radix.rs9
-rw-r--r--clippy_lints/src/methods/map_clone.rs16
-rw-r--r--clippy_lints/src/methods/map_unwrap_or.rs7
-rw-r--r--clippy_lints/src/methods/mod.rs36
-rw-r--r--clippy_lints/src/methods/option_as_ref_deref.rs8
-rw-r--r--clippy_lints/src/methods/str_splitn.rs8
-rw-r--r--clippy_lints/src/methods/unnecessary_to_owned.rs61
-rw-r--r--clippy_lints/src/missing_const_for_fn.rs14
-rw-r--r--clippy_lints/src/missing_doc.rs1
-rw-r--r--clippy_lints/src/mutable_debug_assertion.rs4
-rw-r--r--clippy_lints/src/needless_pass_by_value.rs8
-rw-r--r--clippy_lints/src/no_effect.rs8
-rw-r--r--clippy_lints/src/ptr.rs9
-rw-r--r--clippy_lints/src/ranges.rs10
-rw-r--r--clippy_lints/src/redundant_closure_call.rs4
-rw-r--r--clippy_lints/src/redundant_field_names.rs9
-rw-r--r--clippy_lints/src/redundant_static_lifetimes.rs9
-rw-r--r--clippy_lints/src/returns.rs29
-rw-r--r--clippy_lints/src/same_name_method.rs10
-rw-r--r--clippy_lints/src/transmute/mod.rs8
-rw-r--r--clippy_lints/src/transmute/transmute_ptr_to_ref.rs10
-rw-r--r--clippy_lints/src/undocumented_unsafe_blocks.rs429
-rw-r--r--clippy_lints/src/unnested_or_patterns.rs17
-rw-r--r--clippy_lints/src/unused_rounding.rs21
-rw-r--r--clippy_lints/src/use_self.rs14
-rw-r--r--clippy_lints/src/utils/conf.rs4
-rw-r--r--clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs2
-rw-r--r--clippy_utils/src/attrs.rs24
-rw-r--r--clippy_utils/src/eager_or_lazy.rs26
-rw-r--r--clippy_utils/src/lib.rs33
-rw-r--r--clippy_utils/src/msrvs.rs101
-rw-r--r--clippy_utils/src/paths.rs5
-rw-r--r--clippy_utils/src/qualify_min_const_fn.rs28
-rw-r--r--clippy_utils/src/source.rs38
-rw-r--r--clippy_utils/src/sugg.rs65
-rw-r--r--clippy_utils/src/ty.rs38
-rw-r--r--clippy_utils/src/visitors.rs12
-rw-r--r--lintcheck/src/main.rs15
-rw-r--r--src/driver.rs5
-rw-r--r--tests/ui-internal/invalid_msrv_attr_impl.fixed4
-rw-r--r--tests/ui-internal/invalid_msrv_attr_impl.rs4
-rw-r--r--tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr18
-rw-r--r--tests/ui-toml/allow_mixed_uninlined_format_args/clippy.toml1
-rw-r--r--tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.fixed14
-rw-r--r--tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs14
-rw-r--r--tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr76
-rw-r--r--tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr1
-rw-r--r--tests/ui/almost_complete_letter_range.fixed5
-rw-r--r--tests/ui/almost_complete_letter_range.rs5
-rw-r--r--tests/ui/almost_complete_letter_range.stderr26
-rw-r--r--tests/ui/cast_abs_to_unsigned.fixed7
-rw-r--r--tests/ui/cast_abs_to_unsigned.rs7
-rw-r--r--tests/ui/cast_abs_to_unsigned.stderr36
-rw-r--r--tests/ui/cast_lossless_bool.fixed7
-rw-r--r--tests/ui/cast_lossless_bool.rs7
-rw-r--r--tests/ui/cast_lossless_bool.stderr28
-rw-r--r--tests/ui/cfg_attr_rustfmt.fixed8
-rw-r--r--tests/ui/cfg_attr_rustfmt.rs8
-rw-r--r--tests/ui/cfg_attr_rustfmt.stderr2
-rw-r--r--tests/ui/checked_conversions.fixed7
-rw-r--r--tests/ui/checked_conversions.rs7
-rw-r--r--tests/ui/checked_conversions.stderr34
-rw-r--r--tests/ui/cloned_instead_of_copied.fixed10
-rw-r--r--tests/ui/cloned_instead_of_copied.rs10
-rw-r--r--tests/ui/cloned_instead_of_copied.stderr16
-rw-r--r--tests/ui/derive.rs1
-rw-r--r--tests/ui/derive.stderr20
-rw-r--r--tests/ui/err_expect.fixed7
-rw-r--r--tests/ui/err_expect.rs7
-rw-r--r--tests/ui/err_expect.stderr4
-rw-r--r--tests/ui/eta.fixed22
-rw-r--r--tests/ui/eta.rs22
-rw-r--r--tests/ui/eta.stderr26
-rw-r--r--tests/ui/expect_tool_lint_rfc_2383.rs12
-rw-r--r--tests/ui/explicit_auto_deref.fixed4
-rw-r--r--tests/ui/explicit_auto_deref.rs4
-rw-r--r--tests/ui/filter_map_next_fixable.fixed7
-rw-r--r--tests/ui/filter_map_next_fixable.rs7
-rw-r--r--tests/ui/filter_map_next_fixable.stderr4
-rw-r--r--tests/ui/from_over_into.fixed7
-rw-r--r--tests/ui/from_over_into.rs7
-rw-r--r--tests/ui/from_over_into.stderr10
-rw-r--r--tests/ui/if_then_some_else_none.rs5
-rw-r--r--tests/ui/if_then_some_else_none.stderr10
-rw-r--r--tests/ui/manual_clamp.rs7
-rw-r--r--tests/ui/manual_clamp.stderr70
-rw-r--r--tests/ui/manual_is_ascii_check.fixed11
-rw-r--r--tests/ui/manual_is_ascii_check.rs11
-rw-r--r--tests/ui/manual_is_ascii_check.stderr22
-rw-r--r--tests/ui/manual_let_else.rs14
-rw-r--r--tests/ui/manual_let_else.stderr11
-rw-r--r--tests/ui/manual_rem_euclid.fixed13
-rw-r--r--tests/ui/manual_rem_euclid.rs13
-rw-r--r--tests/ui/manual_rem_euclid.stderr20
-rw-r--r--tests/ui/manual_retain.fixed7
-rw-r--r--tests/ui/manual_retain.rs7
-rw-r--r--tests/ui/manual_retain.stderr38
-rw-r--r--tests/ui/manual_split_once.fixed5
-rw-r--r--tests/ui/manual_split_once.rs5
-rw-r--r--tests/ui/manual_split_once.stderr38
-rw-r--r--tests/ui/manual_str_repeat.fixed5
-rw-r--r--tests/ui/manual_str_repeat.rs5
-rw-r--r--tests/ui/manual_str_repeat.stderr20
-rw-r--r--tests/ui/manual_strip.rs7
-rw-r--r--tests/ui/manual_strip.stderr32
-rw-r--r--tests/ui/map_unwrap_or.rs7
-rw-r--r--tests/ui/map_unwrap_or.stderr24
-rw-r--r--tests/ui/match_expr_like_matches_macro.fixed7
-rw-r--r--tests/ui/match_expr_like_matches_macro.rs7
-rw-r--r--tests/ui/match_expr_like_matches_macro.stderr28
-rw-r--r--tests/ui/mem_replace.fixed7
-rw-r--r--tests/ui/mem_replace.rs7
-rw-r--r--tests/ui/mem_replace.stderr40
-rw-r--r--tests/ui/min_rust_version_attr.rs41
-rw-r--r--tests/ui/min_rust_version_attr.stderr26
-rw-r--r--tests/ui/min_rust_version_invalid_attr.stderr2
-rw-r--r--tests/ui/misnamed_getters.rs124
-rw-r--r--tests/ui/misnamed_getters.stderr166
-rw-r--r--tests/ui/missing_const_for_fn/cant_be_const.rs4
-rw-r--r--tests/ui/missing_const_for_fn/could_be_const.rs10
-rw-r--r--tests/ui/missing_const_for_fn/could_be_const.stderr24
-rw-r--r--tests/ui/needless_borrow.fixed33
-rw-r--r--tests/ui/needless_borrow.rs33
-rw-r--r--tests/ui/needless_borrow.stderr18
-rw-r--r--tests/ui/needless_question_mark.fixed1
-rw-r--r--tests/ui/needless_question_mark.rs1
-rw-r--r--tests/ui/needless_question_mark.stderr28
-rw-r--r--tests/ui/needless_return.fixed19
-rw-r--r--tests/ui/needless_return.rs13
-rw-r--r--tests/ui/needless_return.stderr85
-rw-r--r--tests/ui/needless_splitn.fixed3
-rw-r--r--tests/ui/needless_splitn.rs3
-rw-r--r--tests/ui/needless_splitn.stderr26
-rw-r--r--tests/ui/option_as_ref_deref.fixed7
-rw-r--r--tests/ui/option_as_ref_deref.rs7
-rw-r--r--tests/ui/option_as_ref_deref.stderr36
-rw-r--r--tests/ui/ptr_as_ptr.fixed5
-rw-r--r--tests/ui/ptr_as_ptr.rs5
-rw-r--r--tests/ui/ptr_as_ptr.stderr16
-rw-r--r--tests/ui/range_contains.fixed7
-rw-r--r--tests/ui/range_contains.rs7
-rw-r--r--tests/ui/range_contains.stderr42
-rw-r--r--tests/ui/redundant_closure_call_fixable.fixed12
-rw-r--r--tests/ui/redundant_closure_call_fixable.rs12
-rw-r--r--tests/ui/redundant_closure_call_fixable.stderr24
-rw-r--r--tests/ui/redundant_field_names.fixed7
-rw-r--r--tests/ui/redundant_field_names.rs7
-rw-r--r--tests/ui/redundant_field_names.stderr16
-rw-r--r--tests/ui/redundant_static_lifetimes.fixed7
-rw-r--r--tests/ui/redundant_static_lifetimes.rs7
-rw-r--r--tests/ui/redundant_static_lifetimes.stderr34
-rw-r--r--tests/ui/result_large_err.rs6
-rw-r--r--tests/ui/seek_from_current.fixed5
-rw-r--r--tests/ui/seek_from_current.rs5
-rw-r--r--tests/ui/seek_from_current.stderr2
-rw-r--r--tests/ui/seek_to_start_instead_of_rewind.fixed7
-rw-r--r--tests/ui/seek_to_start_instead_of_rewind.rs7
-rw-r--r--tests/ui/seek_to_start_instead_of_rewind.stderr6
-rw-r--r--tests/ui/temporary_assignment.rs1
-rw-r--r--tests/ui/temporary_assignment.stderr8
-rw-r--r--tests/ui/transmute_ptr_to_ref.fixed5
-rw-r--r--tests/ui/transmute_ptr_to_ref.rs5
-rw-r--r--tests/ui/transmute_ptr_to_ref.stderr44
-rw-r--r--tests/ui/undocumented_unsafe_blocks.rs2
-rw-r--r--tests/ui/undocumented_unsafe_blocks.stderr33
-rw-r--r--tests/ui/uninlined_format_args.fixed24
-rw-r--r--tests/ui/uninlined_format_args.rs5
-rw-r--r--tests/ui/uninlined_format_args.stderr213
-rw-r--r--tests/ui/unnecessary_cast.fixed14
-rw-r--r--tests/ui/unnecessary_cast.rs14
-rw-r--r--tests/ui/unnecessary_cast.stderr52
-rw-r--r--tests/ui/unnecessary_lazy_eval.fixed23
-rw-r--r--tests/ui/unnecessary_lazy_eval.rs23
-rw-r--r--tests/ui/unnecessary_lazy_eval.stderr84
-rw-r--r--tests/ui/unnecessary_operation.fixed9
-rw-r--r--tests/ui/unnecessary_operation.rs9
-rw-r--r--tests/ui/unnecessary_safety_comment.rs51
-rw-r--r--tests/ui/unnecessary_safety_comment.stderr115
-rw-r--r--tests/ui/unnecessary_to_owned.fixed34
-rw-r--r--tests/ui/unnecessary_to_owned.rs34
-rw-r--r--tests/ui/unnecessary_to_owned.stderr168
-rw-r--r--tests/ui/unnecessary_unsafety_doc.rs (renamed from tests/ui/doc_unnecessary_unsafe.rs)1
-rw-r--r--tests/ui/unnecessary_unsafety_doc.stderr (renamed from tests/ui/doc_unnecessary_unsafe.stderr)14
-rw-r--r--tests/ui/unnested_or_patterns.fixed8
-rw-r--r--tests/ui/unnested_or_patterns.rs8
-rw-r--r--tests/ui/unnested_or_patterns.stderr2
-rw-r--r--tests/ui/unused_rounding.fixed3
-rw-r--r--tests/ui/unused_rounding.rs3
-rw-r--r--tests/ui/unused_rounding.stderr8
-rw-r--r--tests/ui/use_self.fixed7
-rw-r--r--tests/ui/use_self.rs7
-rw-r--r--tests/ui/use_self.stderr84
-rw-r--r--triagebot.toml21
249 files changed, 4286 insertions, 1957 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6f1f73c1fd2..23912bb3ed6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4188,6 +4188,7 @@ Released 2018-09-13
 [`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute
 [`mismatched_target_os`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatched_target_os
 [`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_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
@@ -4450,6 +4451,7 @@ Released 2018-09-13
 [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
 [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
 [`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings
+[`unnecessary_safety_comment`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_comment
 [`unnecessary_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_doc
 [`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports
 [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
diff --git a/Cargo.toml b/Cargo.toml
index fe425a2fb99..6bdac84ada0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -42,6 +42,7 @@ filetime = "0.2"
 rustc-workspace-hack = "1.0"
 
 # UI test dependencies
+clap = { version = "3.1", features = ["derive"] }
 clippy_utils = { path = "clippy_utils" }
 derive-new = "0.5"
 if_chain = "1.0"
diff --git a/README.md b/README.md
index f74de7de42b..81254ba8b8b 100644
--- a/README.md
+++ b/README.md
@@ -197,8 +197,8 @@ disallowed-names = ["toto", "tata", "titi"]
 cognitive-complexity-threshold = 30
 ```
 
-See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which
-lints can be configured and the meaning of the variables.
+See the [list of configurable lints](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration),
+the lint descriptions contain the names and meanings of these configuration variables.
 
 > **Note**
 >
@@ -224,7 +224,7 @@ in the `Cargo.toml` can be used.
 rust-version = "1.30"
 ```
 
-The MSRV can also be specified as an inner attribute, like below.
+The MSRV can also be specified as an attribute, like below.
 
 ```rust
 #![feature(custom_inner_attributes)]
diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md
index 0b945faf9b7..1f0b8db28a1 100644
--- a/book/src/SUMMARY.md
+++ b/book/src/SUMMARY.md
@@ -21,3 +21,4 @@
         - [The Clippy Book](development/infrastructure/book.md)
     - [Proposals](development/proposals/README.md)
         - [Roadmap 2021](development/proposals/roadmap-2021.md)
+        - [Syntax Tree Patterns](development/proposals/syntax-tree-patterns.md)
diff --git a/book/src/configuration.md b/book/src/configuration.md
index 77f1d2e8797..430ff8b739a 100644
--- a/book/src/configuration.md
+++ b/book/src/configuration.md
@@ -11,8 +11,8 @@ disallowed-names = ["toto", "tata", "titi"]
 cognitive-complexity-threshold = 30
 ```
 
-See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which
-lints can be configured and the meaning of the variables.
+See the [list of configurable lints](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration),
+the lint descriptions contain the names and meanings of these configuration variables.
 
 To deactivate the "for further information visit *lint-link*" message you can define the `CLIPPY_DISABLE_DOCS_LINKS`
 environment variable.
@@ -72,7 +72,7 @@ minimum supported Rust version (MSRV) in the clippy configuration file.
 msrv = "1.30.0"
 ```
 
-The MSRV can also be specified as an inner attribute, like below.
+The MSRV can also be specified as an attribute, like below.
 
 ```rust
 #![feature(custom_inner_attributes)]
diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md
index 3c3f368a529..8b4eee8c9d9 100644
--- a/book/src/development/adding_lints.md
+++ b/book/src/development/adding_lints.md
@@ -443,27 +443,27 @@ value is passed to the constructor in `clippy_lints/lib.rs`.
 
 ```rust
 pub struct ManualStrip {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl ManualStrip {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
 ```
 
 The project's MSRV can then be matched against the feature MSRV in the LintPass
-using the `meets_msrv` utility function.
+using the `Msrv::meets` method.
 
 ``` rust
-if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) {
+if !self.msrv.meets(msrvs::STR_STRIP_PREFIX) {
     return;
 }
 ```
 
-The project's MSRV can also be specified as an inner attribute, which overrides
+The project's MSRV can also be specified as an attribute, which overrides
 the value from `clippy.toml`. This can be accounted for using the
 `extract_msrv_attr!(LintContext)` macro and passing
 `LateContext`/`EarlyContext`.
@@ -483,19 +483,15 @@ have a case for the version below the MSRV and one with the same contents but
 for the MSRV version itself.
 
 ```rust
-#![feature(custom_inner_attributes)]
-
 ...
 
+#[clippy::msrv = "1.44"]
 fn msrv_1_44() {
-    #![clippy::msrv = "1.44"]
-
     /* something that would trigger the lint */
 }
 
+#[clippy::msrv = "1.45"]
 fn msrv_1_45() {
-    #![clippy::msrv = "1.45"]
-
     /* something that would trigger the lint */
 }
 ```
diff --git a/book/src/development/proposals/syntax-tree-patterns.md b/book/src/development/proposals/syntax-tree-patterns.md
new file mode 100644
index 00000000000..c5587c4bf90
--- /dev/null
+++ b/book/src/development/proposals/syntax-tree-patterns.md
@@ -0,0 +1,986 @@
+- Feature Name: syntax-tree-patterns
+- Start Date: 2019-03-12
+- RFC PR: [#3875](https://github.com/rust-lang/rust-clippy/pull/3875)
+
+# Summary
+
+Introduce a domain-specific language (similar to regular expressions) that
+allows to describe lints using *syntax tree patterns*.
+
+
+# Motivation
+
+Finding parts of a syntax tree (AST, HIR, ...) that have certain properties
+(e.g. "*an if that has a block as its condition*") is a major task when writing
+lints. For non-trivial lints, it often requires nested pattern matching of AST /
+HIR nodes. For example, testing that an expression is a boolean literal requires
+the following checks:
+
+```rust
+if let ast::ExprKind::Lit(lit) = &expr.node {
+    if let ast::LitKind::Bool(_) = &lit.node {
+        ...
+    }
+}
+```
+
+Writing this kind of matching code quickly becomes a complex task and the
+resulting code is often hard to comprehend. The code below shows a simplified
+version of the pattern matching required by the `collapsible_if` lint:
+
+```rust
+// simplified version of the collapsible_if lint
+if let ast::ExprKind::If(check, then, None) = &expr.node {
+    if then.stmts.len() == 1 {
+        if let ast::StmtKind::Expr(inner) | ast::StmtKind::Semi(inner) = &then.stmts[0].node {
+            if let ast::ExprKind::If(check_inner, content, None) = &inner.node {
+                ...
+            }
+        }
+    }
+}
+```
+
+The `if_chain` macro can improve readability by flattening the nested if
+statements, but the resulting code is still quite hard to read:
+
+```rust
+// simplified version of the collapsible_if lint
+if_chain! {
+    if let ast::ExprKind::If(check, then, None) = &expr.node;
+    if then.stmts.len() == 1;
+    if let ast::StmtKind::Expr(inner) | ast::StmtKind::Semi(inner) = &then.stmts[0].node;
+    if let ast::ExprKind::If(check_inner, content, None) = &inner.node;
+    then {
+        ...
+    }
+}
+```
+
+The code above matches if expressions that contain only another if expression
+(where both ifs don't have an else branch). While it's easy to explain what the
+lint does, it's hard to see that from looking at the code samples above.
+
+Following the motivation above, the first goal this RFC is to **simplify writing
+and reading lints**.
+
+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**.
+
+# 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
+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
+[regular expressions](https://en.wikipedia.org/wiki/Regular_expression). Instead
+of writing code that detects certain character sequences, one can describe a
+search pattern using a domain-specific language and search for matches using
+that pattern. The advantage of using a declarative domain-specific language is
+that its limited domain (e.g. matching character sequences in the case of
+regular expressions) allows to express entities in that domain in a very natural
+and expressive way.
+
+While regular expressions are very useful when searching for patterns in flat
+character sequences, they cannot easily be applied to hierarchical data
+structures like syntax trees. This RFC therefore proposes a pattern matching
+system that is inspired by regular expressions and designed for hierarchical
+syntax trees.
+
+# Guide-level explanation
+
+This proposal adds a `pattern!` macro that can be used to specify a syntax tree
+pattern to search for. A simple pattern is shown below:
+
+```rust
+pattern!{
+    my_pattern: Expr =
+        Lit(Bool(false))
+}
+```
+
+This macro call defines a pattern named `my_pattern` that can be matched against
+an `Expr` syntax tree node. The actual pattern (`Lit(Bool(false))` in this case)
+defines which syntax trees should match the pattern. This pattern matches
+expressions that are boolean literals with value `false`.
+
+The pattern can then be used to implement lints in the following way:
+
+```rust
+...
+
+impl EarlyLintPass for MyAwesomeLint {
+    fn check_expr(&mut self, cx: &EarlyContext, expr: &syntax::ast::Expr) {
+
+        if my_pattern(expr).is_some() {
+            cx.span_lint(
+                MY_AWESOME_LINT,
+                expr.span,
+                "This is a match for a simple pattern. Well done!",
+            );
+        }
+
+    }
+}
+```
+
+The `pattern!` macro call expands to a function `my_pattern` that expects a
+syntax tree expression as its argument and returns an `Option` that indicates
+whether the pattern matched.
+
+> Note: The result type is explained in more detail in [a later
+> section](#the-result-type). For now, it's enough to know that the result is
+> `Some` if the pattern matched and `None` otherwise.
+
+## Pattern syntax
+
+The following examples demonstate the pattern syntax:
+
+
+#### Any (`_`)
+
+The simplest pattern is the any pattern. It matches anything and is therefore
+similar to regex's `*`.
+
+```rust
+pattern!{
+    // matches any expression
+    my_pattern: Expr =
+        _
+}
+```
+
+#### Node (`<node-name>(<args>)`)
+
+Nodes are used to match a specific variant of an AST node. A node has a name and
+a number of arguments that depends on the node type. For example, the `Lit` node
+has a single argument that describes the type of the literal. As another
+example, the `If` node has three arguments describing the if's condition, then
+block and else block.
+
+```rust
+pattern!{
+    // matches any expression that is a literal
+    my_pattern: Expr =
+        Lit(_)
+}
+
+pattern!{
+    // matches any expression that is a boolean literal
+    my_pattern: Expr =
+        Lit(Bool(_))
+}
+
+pattern!{
+    // matches if expressions that have a boolean literal in their condition
+    // Note: The `_?` syntax here means that the else branch is optional and can be anything.
+    //       This is discussed in more detail in the section `Repetition`.
+    my_pattern: Expr =
+        If( Lit(Bool(_)) , _, _?)
+}
+```
+
+
+#### Literal (`<lit>`)
+
+A pattern can also contain Rust literals. These literals match themselves.
+
+```rust
+pattern!{
+    // matches the boolean literal false
+    my_pattern: Expr =
+        Lit(Bool(false))
+}
+
+pattern!{
+    // matches the character literal 'x'
+    my_pattern: Expr =
+        Lit(Char('x'))
+}
+```
+
+#### Alternations (`a | b`)
+
+```rust
+pattern!{
+    // matches if the literal is a boolean or integer literal
+    my_pattern: Lit =
+        Bool(_) | Int(_)
+}
+
+pattern!{
+    // matches if the expression is a char literal with value 'x' or 'y'
+    my_pattern: Expr =
+        Lit( Char('x' | 'y') )
+}
+```
+
+#### Empty (`()`)
+
+The empty pattern represents an empty sequence or the `None` variant of an
+optional.
+
+```rust
+pattern!{
+    // matches if the expression is an empty array
+    my_pattern: Expr =
+        Array( () )
+}
+
+pattern!{
+    // matches if expressions that don't have an else clause
+    my_pattern: Expr =
+        If(_, _, ())
+}
+```
+
+#### Sequence (`<a> <b>`)
+
+```rust
+pattern!{
+    // matches the array [true, false]
+    my_pattern: Expr =
+        Array( Lit(Bool(true)) Lit(Bool(false)) )
+}
+```
+
+#### Repetition (`<a>*`, `<a>+`, `<a>?`, `<a>{n}`, `<a>{n,m}`, `<a>{n,}`)
+
+Elements may be repeated. The syntax for specifying repetitions is identical to
+[regex's syntax](https://docs.rs/regex/1.1.2/regex/#repetitions).
+
+```rust
+pattern!{
+    // matches arrays that contain 2 'x's as their last or second-last elements
+    // Examples:
+    //     ['x', 'x']                         match
+    //     ['x', 'x', 'y']                    match
+    //     ['a', 'b', 'c', 'x', 'x', 'y']     match
+    //     ['x', 'x', 'y', 'z']               no match
+    my_pattern: Expr =
+        Array( _* Lit(Char('x')){2} _? )
+}
+
+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(_, _, _)  |       match        |       no match
+    // If(_, _, _?) |       match        |        match
+    // If(_, _, ()) |      no match      |        match
+    my_pattern: Expr =
+        If(_, _, _?)
+}
+```
+
+#### Named submatch (`<a>#<name>`)
+
+```rust
+pattern!{
+    // matches character literals and gives the literal the name foo
+    my_pattern: Expr =
+        Lit(Char(_)#foo)
+}
+
+pattern!{
+    // matches character literals and gives the char the name bar
+    my_pattern: Expr =
+        Lit(Char(_#bar))
+}
+
+pattern!{
+    // matches character literals and gives the expression the name baz
+    my_pattern: Expr =
+        Lit(Char(_))#baz
+}
+```
+
+The reason for using named submatches is described in the section [The result
+type](#the-result-type).
+
+### Summary
+
+The following table gives an summary of the pattern syntax:
+
+| Syntax                  | Concept          | Examples                                   |
+|-------------------------|------------------|--------------------------------------------|
+|`_`                      | Any              | `_`                                        |
+|`<node-name>(<args>)`    | Node             | `Lit(Bool(true))`, `If(_, _, _)`           |
+|`<lit>`                  | Literal          | `'x'`, `false`, `101`                      |
+|`<a> \| <b>`             | Alternation      | `Char(_) \| Bool(_)`                       |
+|`()`                     | Empty            | `Array( () )`                              |
+|`<a> <b>`                | Sequence         | `Tuple( Lit(Bool(_)) Lit(Int(_)) Lit(_) )` |
+|`<a>*` <br> `<a>+` <br> `<a>?` <br> `<a>{n}` <br> `<a>{n,m}` <br> `<a>{n,}` | Repetition <br> <br> <br> <br> <br><br> | `Array( _* )`, <br> `Block( Semi(_)+ )`, <br> `If(_, _, Block(_)?)`, <br> `Array( Lit(_){10} )`, <br> `Lit(_){5,10}`, <br> `Lit(Bool(_)){10,}` |
+|`<a>#<name>`             | Named submatch   | `Lit(Int(_))#foo` `Lit(Int(_#bar))`        |
+
+
+## The result type
+
+A lot of lints require checks that go beyond what the pattern syntax described
+above can express. For example, a lint might want to check whether a node was
+created as part of a macro expansion or whether there's no comment above a node.
+Another example would be a lint that wants to match two nodes that have the same
+value (as needed by lints like `almost_swapped`). Instead of allowing users to
+write these checks into the pattern directly (which might make patterns hard to
+read), the proposed solution allows users to assign names to parts of a pattern
+expression. When matching a pattern against a syntax tree node, the return value
+will contain references to all nodes that were matched by these named
+subpatterns. This is similar to capture groups in regular expressions.
+
+For example, given the following pattern
+
+```rust
+pattern!{
+    // matches character literals
+    my_pattern: Expr =
+        Lit(Char(_#val_inner)#val)#val_outer
+}
+```
+
+one could get references to the nodes that matched the subpatterns in the
+following way:
+
+```rust
+...
+fn check_expr(expr: &syntax::ast::Expr) {
+    if let Some(result) = my_pattern(expr) {
+        result.val_inner  // type: &char
+        result.val        // type: &syntax::ast::Lit
+        result.val_outer  // type: &syntax::ast::Expr
+    }
+}
+```
+
+The types in the `result` struct depend on the pattern. For example, the
+following pattern
+
+```rust
+pattern!{
+    // matches arrays of character literals
+    my_pattern_seq: Expr =
+        Array( Lit(_)*#foo )
+}
+```
+
+matches arrays that consist of any number of literal expressions. Because those
+expressions are named `foo`, the result struct contains a `foo` attribute which
+is a vector of expressions:
+
+```rust
+...
+if let Some(result) = my_pattern_seq(expr) {
+    result.foo        // type: Vec<&syntax::ast::Expr>
+}
+```
+
+Another result type occurs when a name is only defined in one branch of an
+alternation:
+
+```rust
+pattern!{
+    // matches if expression is a boolean or integer literal
+    my_pattern_alt: Expr =
+        Lit( Bool(_#bar) | Int(_) )
+}
+```
+
+In the pattern above, the `bar` name is only defined if the pattern matches a
+boolean literal. If it matches an integer literal, the name isn't set. To
+account for this, the result struct's `bar` attribute is an option type:
+
+```rust
+...
+if let Some(result) = my_pattern_alt(expr) {
+    result.bar        // type: Option<&bool>
+}
+```
+
+It's also possible to use a name in multiple alternation branches if they have
+compatible types:
+
+```rust
+pattern!{
+    // matches if expression is a boolean or integer literal
+    my_pattern_mult: Expr =
+        Lit(_#baz) | Array( Lit(_#baz) )
+}
+...
+if let Some(result) = my_pattern_mult(expr) {
+    result.baz        // type: &syntax::ast::Lit
+}
+```
+
+Named submatches are a **flat** namespace and this is intended. In the example
+above, two different sub-structures are assigned to a flat name. I expect that
+for most lints, a flat namespace is sufficient and easier to work with than a
+hierarchical one.
+
+#### Two stages
+
+Using named subpatterns, users can write lints in two stages. First, a coarse
+selection of possible matches is produced by the pattern syntax. In the second
+stage, the named subpattern references can be used to do additional tests like
+asserting that a node hasn't been created as part of a macro expansion.
+
+## Implementing clippy lints using patterns
+
+As a "real-world" example, I re-implemented the `collapsible_if` lint using
+patterns. The code can be found
+[here](https://github.com/fkohlgrueber/rust-clippy-pattern/blob/039b07ecccaf96d6aa7504f5126720d2c9cceddd/clippy_lints/src/collapsible_if.rs#L88-L163).
+The pattern-based version passes all test cases that were written for
+`collapsible_if`.
+
+
+# Reference-level explanation
+
+## Overview
+
+The following diagram shows the dependencies between the main parts of the
+proposed solution:
+
+```
+                          Pattern syntax
+                                |
+                                |  parsing / lowering
+                                v
+                           PatternTree
+                                ^
+                                |
+                                |
+                          IsMatch trait
+                                |
+                                |
+             +---------------+-----------+---------+
+             |               |           |         |
+             v               v           v         v
+        syntax::ast     rustc::hir      syn       ...
+```
+
+The pattern syntax described in the previous section is parsed / lowered into
+the so-called *PatternTree* data structure that represents a valid syntax tree
+pattern. Matching a *PatternTree* against an actual syntax tree (e.g. rust ast /
+hir or the syn ast, ...) is done using the *IsMatch* trait.
+
+The *PatternTree* and the *IsMatch* trait are introduced in more detail in the
+following sections.
+
+## PatternTree
+
+The core data structure of this RFC is the **PatternTree**.
+
+It's a data structure similar to rust's AST / HIR, but with the following
+differences:
+
+- The PatternTree doesn't contain parsing information like `Span`s
+- The PatternTree can represent alternatives, sequences and optionals
+
+The code below shows a simplified version of the current PatternTree:
+
+> Note: The current implementation can be found
+> [here](https://github.com/fkohlgrueber/pattern-matching/blob/dfb3bc9fbab69cec7c91e72564a63ebaa2ede638/pattern-match/src/pattern_tree.rs#L50-L96).
+
+
+```rust
+pub enum Expr {
+    Lit(Alt<Lit>),
+    Array(Seq<Expr>),
+    Block_(Alt<BlockType>),
+    If(Alt<Expr>, Alt<BlockType>, Opt<Expr>),
+    IfLet(
+        Alt<BlockType>,
+        Opt<Expr>,
+    ),
+}
+
+pub enum Lit {
+    Char(Alt<char>),
+    Bool(Alt<bool>),
+    Int(Alt<u128>),
+}
+
+pub enum Stmt {
+    Expr(Alt<Expr>),
+    Semi(Alt<Expr>),
+}
+
+pub enum BlockType {
+    Block(Seq<Stmt>),
+}
+```
+
+The `Alt`, `Seq` and `Opt` structs look like these:
+
+> Note: The current implementation can be found
+> [here](https://github.com/fkohlgrueber/pattern-matching/blob/dfb3bc9fbab69cec7c91e72564a63ebaa2ede638/pattern-match/src/matchers.rs#L35-L60).
+
+```rust
+pub enum Alt<T> {
+    Any,
+    Elmt(Box<T>),
+    Alt(Box<Self>, Box<Self>),
+    Named(Box<Self>, ...)
+}
+
+pub enum Opt<T> {
+    Any,  // anything, but not None
+    Elmt(Box<T>),
+    None,
+    Alt(Box<Self>, Box<Self>),
+    Named(Box<Self>, ...)
+}
+
+pub enum Seq<T> {
+    Any,
+    Empty,
+    Elmt(Box<T>),
+    Repeat(Box<Self>, RepeatRange),
+    Seq(Box<Self>, Box<Self>),
+    Alt(Box<Self>, Box<Self>),
+    Named(Box<Self>, ...)
+}
+
+pub struct RepeatRange {
+    pub start: usize,
+    pub end: Option<usize>  // exclusive
+}
+```
+
+## Parsing / Lowering
+
+The input of a `pattern!` macro call is parsed into a `ParseTree` first and then
+lowered to a `PatternTree`.
+
+Valid patterns depend on the *PatternTree* definitions. For example, the pattern
+`Lit(Bool(_)*)` isn't valid because the parameter type of the `Lit` variant of
+the `Expr` enum is `Any<Lit>` and therefore doesn't support repetition (`*`). As
+another example, `Array( Lit(_)* )` is a valid pattern because the parameter of
+`Array` is of type `Seq<Expr>` which allows sequences and repetitions.
+
+> Note: names in the pattern syntax correspond to *PatternTree* enum
+> **variants**. For example, the `Lit` in the pattern above refers to the `Lit`
+> variant of the `Expr` enum (`Expr::Lit`), not the `Lit` enum.
+
+## The IsMatch Trait
+
+The pattern syntax and the *PatternTree* are independant 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.
+In contrast, clippy lints currently match against ast / hir syntax tree nodes
+and therefore directly depend on their implementation.
+
+The connection between the *PatternTree* and specific syntax tree
+implementations is the `IsMatch` trait. It defines how to match *PatternTree*
+nodes against specific syntax tree nodes. A simplified implementation of the
+`IsMatch` trait is shown below:
+
+```rust
+pub trait IsMatch<O> {
+    fn is_match(&self, other: &'o O) -> bool;
+}
+```
+
+This trait needs to be implemented on each enum of the *PatternTree* (for the
+corresponding syntax tree types). For example, the `IsMatch` implementation for
+matching `ast::LitKind` against the *PatternTree's* `Lit` enum might look like
+this:
+
+```rust
+impl IsMatch<ast::LitKind> for Lit {
+    fn is_match(&self, other: &ast::LitKind) -> bool {
+        match (self, other) {
+            (Lit::Char(i), ast::LitKind::Char(j)) => i.is_match(j),
+            (Lit::Bool(i), ast::LitKind::Bool(j)) => i.is_match(j),
+            (Lit::Int(i), ast::LitKind::Int(j, _)) => i.is_match(j),
+            _ => false,
+        }
+    }
+}
+```
+
+All `IsMatch` implementations for matching the current *PatternTree* against
+`syntax::ast` can be found
+[here](https://github.com/fkohlgrueber/pattern-matching/blob/dfb3bc9fbab69cec7c91e72564a63ebaa2ede638/pattern-match/src/ast_match.rs).
+
+
+# Drawbacks
+
+#### Performance
+
+The pattern matching code is currently not optimized for performance, so it
+might be slower than hand-written matching code. Additionally, the two-stage
+approach (matching against the coarse pattern first and checking for additional
+properties later) might be slower than the current practice of checking for
+structure and additional properties in one pass. For example, the following lint
+
+```rust
+pattern!{
+    pat_if_without_else: Expr =
+        If(
+            _,
+            Block(
+                Expr( If(_, _, ())#inner )
+                | Semi( If(_, _, ())#inner )
+            )#then,
+            ()
+        )
+}
+...
+fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
+    if let Some(result) = pat_if_without_else(expr) {
+        if !block_starts_with_comment(cx, result.then) {
+            ...
+        }
+}
+```
+
+first matches against the pattern and then checks that the `then` block doesn't
+start with a comment. Using clippy's current approach, it's possible to check
+for these conditions earlier:
+
+```rust
+fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
+    if_chain! {
+        if let ast::ExprKind::If(ref check, ref then, None) = expr.node;
+        if !block_starts_with_comment(cx, then);
+        if let Some(inner) = expr_block(then);
+        if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.node;
+        then {
+            ...
+        }
+    }
+}
+```
+
+Whether or not this causes performance regressions depends on actual patterns.
+If it turns out to be a problem, the pattern matching algorithms could be
+extended to allow "early filtering" (see the [Early Filtering](#early-filtering)
+section in Future Possibilities).
+
+That being said, I don't see any conceptual limitations regarding pattern
+matching performance.
+
+#### Applicability
+
+Even though I'd expect that a lot of lints can be written using the proposed
+pattern syntax, it's unlikely that all lints can be expressed using patterns. I
+suspect that there will still be lints that need to be implemented by writing
+custom pattern matching code. This would lead to mix within clippy's codebase
+where some lints are implemented using patterns and others aren't. This
+inconsistency might be considered a drawback.
+
+
+# Rationale and alternatives
+
+Specifying lints using syntax tree patterns has a couple of advantages compared
+to the current approach of manually writing matching code. First, syntax tree
+patterns allow users to describe patterns in a simple and expressive way. This
+makes it easier to write new lints for both novices and experts and also makes
+reading / modifying existing lints simpler.
+
+Another advantage is that lints are independent of specific syntax tree
+implementations (e.g. AST / HIR, ...). When these syntax tree implementations
+change, only the `IsMatch` trait implementations need to be adapted and existing
+lints can remain unchanged. This also means that if the `IsMatch` trait
+implementations were integrated into the compiler, updating the `IsMatch`
+implementations would be required for the compiler to compile successfully. This
+could reduce the number of times clippy breaks because of changes in the
+compiler. Another advantage of the pattern's independence is that converting an
+`EarlyLintPass` lint into a `LatePassLint` wouldn't require rewriting the whole
+pattern matching code. In fact, the pattern might work just fine without any
+adaptions.
+
+
+## Alternatives
+
+### Rust-like pattern syntax
+
+The proposed pattern syntax requires users to know the structure of the
+`PatternTree` (which is very similar to the AST's / HIR's structure) and also
+the pattern syntax. An alternative would be to introduce a pattern syntax that
+is similar to actual Rust syntax (probably like the `quote!` macro). For
+example, a pattern that matches `if` expressions that have `false` in their
+condition could look like this:
+
+```rust
+if false {
+    #[*]
+}
+```
+
+#### Problems
+
+Extending Rust syntax (which is quite complex by itself) with additional syntax
+needed for specifying patterns (alternations, sequences, repetisions, named
+submatches, ...) might become difficult to read and really hard to parse
+properly.
+
+For example, a pattern that matches a binary operation that has `0` on both
+sides might look like this:
+
+```
+0 #[*:BinOpKind] 0
+```
+
+Now consider this slightly more complex example:
+
+```
+1 + 0 #[*:BinOpKind] 0
+```
+
+The parser would need to know the precedence of `#[*:BinOpKind]` because it
+affects the structure of the resulting AST. `1 + 0 + 0` is parsed as `(1 + 0) +
+0` while `1 + 0 * 0` is parsed as `1 + (0 * 0)`. Since the pattern could be any
+`BinOpKind`, the precedence cannot be known in advance.
+
+Another example of a problem would be named submatches. Take a look at this
+pattern:
+
+```rust
+fn test() {
+    1 #foo
+}
+```
+
+Which node is `#foo` referring to? `int`, `ast::Lit`, `ast::Expr`, `ast::Stmt`?
+Naming subpatterns in a rust-like syntax is difficult because a lot of AST nodes
+don't have a syntactic element that can be used to put the name tag on. In these
+situations, the only sensible option would be to assign the name tag to the
+outermost node (`ast::Stmt` in the example above), because the information of
+all child nodes can be retrieved through the outermost node. The problem with
+this then would be that accessing inner nodes (like `ast::Lit`) would again
+require manual pattern matching.
+
+In general, Rust syntax contains a lot of code structure implicitly. This
+structure is reconstructed during parsing (e.g. binary operations are
+reconstructed using operator precedence and left-to-right) and is one of the
+reasons why parsing is a complex task. The advantage of this approach is that
+writing code is simpler for users.
+
+When writing *syntax tree patterns*, each element of the hierarchy might have
+alternatives, repetitions, etc.. Respecting that while still allowing
+human-friendly syntax that contains structure implicitly seems to be really
+complex, if not impossible.
+
+Developing such a syntax would also require to maintain a custom parser that is
+at least as complex as the Rust parser itself. Additionally, future changes in
+the Rust syntax might be incompatible with such a syntax.
+
+In summary, I think that developing such a syntax would introduce a lot of
+complexity to solve a relatively minor problem.
+
+The issue of users not knowing about the *PatternTree* structure could be solved
+by a tool that, given a rust program, generates a pattern that matches only this
+program (similar to the clippy author lint).
+
+For some simple cases (like the first example above), it might be possible to
+successfully mix Rust and pattern syntax. This space could be further explored
+in a future extension.
+
+# Prior art
+
+The pattern syntax is heavily inspired by regular expressions (repetitions,
+alternatives, sequences, ...).
+
+From what I've seen until now, other linters also implement lints that directly
+work on syntax tree data structures, just like clippy does currently. I would
+therefore consider the pattern syntax to be *new*, but please correct me if I'm
+wrong.
+
+# Unresolved questions
+
+#### How to handle multiple matches?
+
+When matching a syntax tree node against a pattern, there are possibly multiple
+ways in which the pattern can be matched. A simple example of this would be the
+following pattern:
+
+```rust
+pattern!{
+    my_pattern: Expr =
+        Array( _* Lit(_)+#literals)
+}
+```
+
+This pattern matches arrays that end with at least one literal. Now given the
+array `[x, 1, 2]`, should `1` be matched as part of the `_*` or the `Lit(_)+`
+part of the pattern? The difference is important because the named submatch
+`#literals` would contain 1 or 2 elements depending how the pattern is matched.
+In regular expressions, this problem is solved by matching "greedy" by default
+and "non-greedy" optionally.
+
+I haven't looked much into this yet because I don't know how relevant it is for
+most lints. The current implementation simply returns the first match it finds.
+
+# Future possibilities
+
+#### Implement rest of Rust Syntax
+
+The current project only implements a small part of the Rust syntax. In the
+future, this should incrementally be extended to more syntax to allow
+implementing more lints. Implementing more of the Rust syntax requires extending
+the `PatternTree` and `IsMatch` implementations, but should be relatively
+straight-forward.
+
+#### Early filtering
+
+As described in the *Drawbacks/Performance* section, allowing additional checks
+during the pattern matching might be beneficial.
+
+The pattern below shows how this could look like:
+
+```rust
+pattern!{
+    pat_if_without_else: Expr =
+        If(
+            _,
+            Block(
+                Expr( If(_, _, ())#inner )
+                | Semi( If(_, _, ())#inner )
+            )#then,
+            ()
+        )
+    where
+        !in_macro(#then.span);
+}
+```
+
+The difference compared to the currently proposed two-stage filtering is that
+using early filtering, the condition (`!in_macro(#then.span)` in this case)
+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
+`a` are the same. Using `=#...` as syntax for backreferences, the lint could be
+implemented like this:
+
+```rust
+pattern!{
+    assign_op_pattern: Expr =
+        Assign(_#target, Binary(_, =#target, _)
+}
+```
+
+#### Match descendant
+
+A lot of lints currently implement custom visitors that check whether any
+subtree (which might not be a direct descendant) of the current node matches
+some properties. This cannot be expressed with the proposed pattern syntax.
+Extending the pattern syntax to allow patterns like "a function that contains at
+least two return statements" could be a practical addition.
+
+#### Negation operator for alternatives
+
+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
+literal types are implemented).
+
+#### Functional composition
+
+Patterns currently don't have any concept of composition. This leads to
+repetitions within patterns. For example, one of the collapsible-if patterns
+currently has to be written like this:
+
+```rust
+pattern!{
+    pat_if_else: Expr =
+        If(
+            _,
+            _,
+            Block_(
+                Block(
+                    Expr((If(_, _, _?) | IfLet(_, _?))#else_) |
+                    Semi((If(_, _, _?) | IfLet(_, _?))#else_)
+                )#block_inner
+            )#block
+        ) |
+        IfLet(
+            _,
+            Block_(
+                Block(
+                    Expr((If(_, _, _?) | IfLet(_, _?))#else_) |
+                    Semi((If(_, _, _?) | IfLet(_, _?))#else_)
+                )#block_inner
+            )#block
+        )
+}
+```
+
+If patterns supported defining functions of subpatterns, the code could be
+simplified as follows:
+
+```rust
+pattern!{
+    fn expr_or_semi(expr: Expr) -> Stmt {
+        Expr(expr) | Semi(expr)
+    }
+    fn if_or_if_let(then: Block, else: Opt<Expr>) -> Expr {
+        If(_, then, else) | IfLet(then, else)
+    }
+    pat_if_else: Expr =
+        if_or_if_let(
+            _,
+            Block_(
+                Block(
+                    expr_or_semi( if_or_if_let(_, _?)#else_ )
+                )#block_inner
+            )#block
+        )
+}
+```
+
+Additionally, common patterns like `expr_or_semi` could be shared between
+different lints.
+
+#### Clippy Pattern Author
+
+Another improvement could be to create a tool that, given some valid Rust
+syntax, generates a pattern that matches this syntax exactly. This would make
+starting to write a pattern easier. A user could take a look at the patterns
+generated for a couple of Rust code examples and use that information to write a
+pattern that matches all of them.
+
+This is similar to clippy's author lint.
+
+#### Supporting other syntaxes
+
+Most of the proposed system is language-agnostic. For example, the pattern
+syntax could also be used to describe patterns for other programming languages.
+
+In order to support other languages' syntaxes, one would need to implement
+another `PatternTree` that sufficiently describes the languages' AST and
+implement `IsMatch` for this `PatternTree` and the languages' AST.
+
+One aspect of this is that it would even be possible to write lints that work on
+the pattern syntax itself. For example, when writing the following pattern
+
+
+```rust
+pattern!{
+    my_pattern: Expr =
+        Array( Lit(Bool(false)) Lit(Bool(false)) )
+}
+```
+
+a lint that works on the pattern syntax's AST could suggest using this pattern
+instead:
+
+```rust
+pattern!{
+    my_pattern: Expr =
+        Array( Lit(Bool(false)){2} )
+}
+```
+
+In the future, clippy could use this system to also provide lints for custom
+syntaxes like those found in macros.
diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs
index 9e15f1504fa..ec7f1dd0d84 100644
--- a/clippy_dev/src/new_lint.rs
+++ b/clippy_dev/src/new_lint.rs
@@ -120,7 +120,7 @@ fn add_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> {
 
     let new_lint = if enable_msrv {
         format!(
-            "store.register_{lint_pass}_pass(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(msrv)));\n    ",
+            "store.register_{lint_pass}_pass(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(msrv())));\n    ",
             lint_pass = lint.pass,
             ctor_arg = if lint.pass == "late" { "_" } else { "" },
             module_name = lint.name,
@@ -238,10 +238,9 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
     result.push_str(&if enable_msrv {
         formatdoc!(
             r#"
-            use clippy_utils::msrvs;
+            use clippy_utils::msrvs::{{self, Msrv}};
             {pass_import}
             use rustc_lint::{{{context_import}, {pass_type}, LintContext}};
-            use rustc_semver::RustcVersion;
             use rustc_session::{{declare_tool_lint, impl_lint_pass}};
 
         "#
@@ -263,12 +262,12 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
         formatdoc!(
             r#"
             pub struct {name_camel} {{
-                msrv: Option<RustcVersion>,
+                msrv: Msrv,
             }}
 
             impl {name_camel} {{
                 #[must_use]
-                pub fn new(msrv: Option<RustcVersion>) -> Self {{
+                pub fn new(msrv: Msrv) -> Self {{
                     Self {{ msrv }}
                 }}
             }}
@@ -357,15 +356,14 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R
         let _ = writedoc!(
             lint_file_contents,
             r#"
-                use clippy_utils::{{meets_msrv, msrvs}};
+                use clippy_utils::msrvs::{{self, Msrv}};
                 use rustc_lint::{{{context_import}, LintContext}};
-                use rustc_semver::RustcVersion;
 
                 use super::{name_upper};
 
                 // TODO: Adjust the parameters as necessary
-                pub(super) fn check(cx: &{context_import}, msrv: Option<RustcVersion>) {{
-                    if !meets_msrv(msrv, todo!("Add a new entry in `clippy_utils/src/msrvs`")) {{
+                pub(super) fn check(cx: &{context_import}, msrv: &Msrv) {{
+                    if !msrv.meets(todo!("Add a new entry in `clippy_utils/src/msrvs`")) {{
                         return;
                     }}
                     todo!();
diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml
index aedff24c12c..dcadd012a44 100644
--- a/clippy_lints/Cargo.toml
+++ b/clippy_lints/Cargo.toml
@@ -19,7 +19,7 @@ quine-mc_cluskey = "0.2"
 regex-syntax = "0.6"
 serde = { version = "1.0", features = ["derive"] }
 serde_json = { version = "1.0", optional = true }
-tempfile = { version = "3.2", optional = true }
+tempfile = { version = "3.3.0", optional = true }
 toml = "0.5"
 unicode-normalization = "0.1"
 unicode-script = { version = "0.5", default-features = false }
diff --git a/clippy_lints/src/almost_complete_letter_range.rs b/clippy_lints/src/almost_complete_letter_range.rs
index df92579a85d..52beaf504a4 100644
--- a/clippy_lints/src/almost_complete_letter_range.rs
+++ b/clippy_lints/src/almost_complete_letter_range.rs
@@ -1,11 +1,10 @@
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::{trim_span, walk_span_to_context};
-use clippy_utils::{meets_msrv, msrvs};
 use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::Span;
 
@@ -33,10 +32,10 @@ declare_clippy_lint! {
 impl_lint_pass!(AlmostCompleteLetterRange => [ALMOST_COMPLETE_LETTER_RANGE]);
 
 pub struct AlmostCompleteLetterRange {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 impl AlmostCompleteLetterRange {
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -46,7 +45,7 @@ impl EarlyLintPass for AlmostCompleteLetterRange {
             let ctxt = e.span.ctxt();
             let sugg = if let Some(start) = walk_span_to_context(start.span, ctxt)
                 && let Some(end) = walk_span_to_context(end.span, ctxt)
-                && meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE)
+                && self.msrv.meets(msrvs::RANGE_INCLUSIVE)
             {
                 Some((trim_span(cx.sess().source_map(), start.between(end)), "..="))
             } else {
@@ -60,7 +59,7 @@ impl EarlyLintPass for AlmostCompleteLetterRange {
         if let PatKind::Range(Some(start), Some(end), kind) = &p.kind
             && matches!(kind.node, RangeEnd::Excluded)
         {
-            let sugg = if meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE) {
+            let sugg = if self.msrv.meets(msrvs::RANGE_INCLUSIVE) {
                 "..="
             } else {
                 "..."
diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs
index 724490fb495..ccf82f132f4 100644
--- a/clippy_lints/src/approx_const.rs
+++ b/clippy_lints/src/approx_const.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::{meets_msrv, msrvs};
+use clippy_utils::msrvs::{self, Msrv};
 use rustc_ast::ast::{FloatTy, LitFloatType, LitKind};
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -63,12 +63,12 @@ const KNOWN_CONSTS: [(f64, &str, usize, Option<RustcVersion>); 19] = [
 ];
 
 pub struct ApproxConstant {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl ApproxConstant {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 
@@ -87,7 +87,7 @@ impl ApproxConstant {
         let s = s.as_str();
         if s.parse::<f64>().is_ok() {
             for &(constant, name, min_digits, msrv) in &KNOWN_CONSTS {
-                if is_approx_const(constant, s, min_digits) && msrv.map_or(true, |msrv| meets_msrv(self.msrv, msrv)) {
+                if is_approx_const(constant, s, min_digits) && msrv.map_or(true, |msrv| self.msrv.meets(msrv)) {
                     span_lint_and_help(
                         cx,
                         APPROX_CONSTANT,
diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs
index 018f10f2588..0710ac0bb0a 100644
--- a/clippy_lints/src/attrs.rs
+++ b/clippy_lints/src/attrs.rs
@@ -2,9 +2,8 @@
 
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::macros::{is_panic, macro_backtrace};
-use clippy_utils::msrvs;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments};
-use clippy_utils::{extract_msrv_attr, meets_msrv};
 use if_chain::if_chain;
 use rustc_ast::{AttrKind, AttrStyle, Attribute, LitKind, MetaItemKind, MetaItemLit, NestedMetaItem};
 use rustc_errors::Applicability;
@@ -14,7 +13,6 @@ use rustc_hir::{
 use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::Span;
 use rustc_span::symbol::Symbol;
@@ -599,7 +597,7 @@ fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool {
 }
 
 pub struct EarlyAttributes {
-    pub msrv: Option<RustcVersion>,
+    pub msrv: Msrv,
 }
 
 impl_lint_pass!(EarlyAttributes => [
@@ -614,7 +612,7 @@ impl EarlyLintPass for EarlyAttributes {
     }
 
     fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
-        check_deprecated_cfg_attr(cx, attr, self.msrv);
+        check_deprecated_cfg_attr(cx, attr, &self.msrv);
         check_mismatched_target_os(cx, attr);
     }
 
@@ -654,9 +652,9 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It
     }
 }
 
-fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: Option<RustcVersion>) {
+fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msrv) {
     if_chain! {
-        if meets_msrv(msrv, msrvs::TOOL_ATTRIBUTES);
+        if msrv.meets(msrvs::TOOL_ATTRIBUTES);
         // check cfg_attr
         if attr.has_name(sym::cfg_attr);
         if let Some(items) = attr.meta_item_list();
diff --git a/clippy_lints/src/blocks_in_if_conditions.rs b/clippy_lints/src/blocks_in_if_conditions.rs
index 9c053247402..569bf27c3e7 100644
--- a/clippy_lints/src/blocks_in_if_conditions.rs
+++ b/clippy_lints/src/blocks_in_if_conditions.rs
@@ -85,8 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInIfConditions {
                             );
                         }
                     } else {
-                        let span =
-                            block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span);
+                        let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span);
                         if span.from_expansion() || expr.span.from_expansion() {
                             return;
                         }
diff --git a/clippy_lints/src/casts/cast_abs_to_unsigned.rs b/clippy_lints/src/casts/cast_abs_to_unsigned.rs
index 3f1edabe6c5..44226298333 100644
--- a/clippy_lints/src/casts/cast_abs_to_unsigned.rs
+++ b/clippy_lints/src/casts/cast_abs_to_unsigned.rs
@@ -1,11 +1,10 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{meets_msrv, msrvs};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty};
-use rustc_semver::RustcVersion;
 
 use super::CAST_ABS_TO_UNSIGNED;
 
@@ -15,9 +14,9 @@ pub(super) fn check(
     cast_expr: &Expr<'_>,
     cast_from: Ty<'_>,
     cast_to: Ty<'_>,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) {
-    if meets_msrv(msrv, msrvs::UNSIGNED_ABS)
+    if msrv.meets(msrvs::UNSIGNED_ABS)
         && let ty::Int(from) = cast_from.kind()
         && let ty::Uint(to) = cast_to.kind()
         && let ExprKind::MethodCall(method_path, receiver, ..) = cast_expr.kind
diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs
index 13c403234da..cf07e050ccc 100644
--- a/clippy_lints/src/casts/cast_lossless.rs
+++ b/clippy_lints/src/casts/cast_lossless.rs
@@ -1,12 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::in_constant;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::is_isize_or_usize;
-use clippy_utils::{in_constant, meets_msrv, msrvs};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, FloatTy, Ty};
-use rustc_semver::RustcVersion;
 
 use super::{utils, CAST_LOSSLESS};
 
@@ -16,7 +16,7 @@ pub(super) fn check(
     cast_op: &Expr<'_>,
     cast_from: Ty<'_>,
     cast_to: Ty<'_>,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) {
     if !should_lint(cx, expr, cast_from, cast_to, msrv) {
         return;
@@ -57,13 +57,7 @@ pub(super) fn check(
     );
 }
 
-fn should_lint(
-    cx: &LateContext<'_>,
-    expr: &Expr<'_>,
-    cast_from: Ty<'_>,
-    cast_to: Ty<'_>,
-    msrv: Option<RustcVersion>,
-) -> bool {
+fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: &Msrv) -> bool {
     // Do not suggest using From in consts/statics until it is valid to do so (see #2267).
     if in_constant(cx, expr.hir_id) {
         return false;
@@ -89,7 +83,7 @@ fn should_lint(
             };
             !is_isize_or_usize(cast_from) && from_nbits < to_nbits
         },
-        (false, true) if matches!(cast_from.kind(), ty::Bool) && meets_msrv(msrv, msrvs::FROM_BOOL) => true,
+        (false, true) if matches!(cast_from.kind(), ty::Bool) && msrv.meets(msrvs::FROM_BOOL) => true,
         (_, _) => {
             matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64))
         },
diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs
index adbcfd3189b..a6376484914 100644
--- a/clippy_lints/src/casts/cast_possible_truncation.rs
+++ b/clippy_lints/src/casts/cast_possible_truncation.rs
@@ -118,12 +118,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
             };
             let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
 
-            let cast_from_ptr_size = def.repr().int.map_or(true, |ty| {
-                matches!(
-                    ty,
-                    IntegerType::Pointer(_),
-                )
-            });
+            let cast_from_ptr_size = def.repr().int.map_or(true, |ty| matches!(ty, IntegerType::Pointer(_),));
             let suffix = match (cast_from_ptr_size, is_isize_or_usize(cast_to)) {
                 (false, false) if from_nbits > to_nbits => "",
                 (true, false) if from_nbits > to_nbits => "",
diff --git a/clippy_lints/src/casts/cast_slice_different_sizes.rs b/clippy_lints/src/casts/cast_slice_different_sizes.rs
index d31d10d22b9..e862f13e69f 100644
--- a/clippy_lints/src/casts/cast_slice_different_sizes.rs
+++ b/clippy_lints/src/casts/cast_slice_different_sizes.rs
@@ -1,16 +1,16 @@
-use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source};
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::{diagnostics::span_lint_and_then, source};
 use if_chain::if_chain;
 use rustc_ast::Mutability;
 use rustc_hir::{Expr, ExprKind, Node};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, layout::LayoutOf, Ty, TypeAndMut};
-use rustc_semver::RustcVersion;
 
 use super::CAST_SLICE_DIFFERENT_SIZES;
 
-pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Option<RustcVersion>) {
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Msrv) {
     // suggestion is invalid if `ptr::slice_from_raw_parts` does not exist
-    if !meets_msrv(msrv, msrvs::PTR_SLICE_RAW_PARTS) {
+    if !msrv.meets(msrvs::PTR_SLICE_RAW_PARTS) {
         return;
     }
 
diff --git a/clippy_lints/src/casts/cast_slice_from_raw_parts.rs b/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
index 284ef165998..627b795d6ed 100644
--- a/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
+++ b/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
@@ -1,12 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{match_def_path, meets_msrv, msrvs, paths};
+use clippy_utils::{match_def_path, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{def_id::DefId, Expr, ExprKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty};
-use rustc_semver::RustcVersion;
 
 use super::CAST_SLICE_FROM_RAW_PARTS;
 
@@ -25,15 +25,9 @@ fn raw_parts_kind(cx: &LateContext<'_>, did: DefId) -> Option<RawPartsKind> {
     }
 }
 
-pub(super) fn check(
-    cx: &LateContext<'_>,
-    expr: &Expr<'_>,
-    cast_expr: &Expr<'_>,
-    cast_to: Ty<'_>,
-    msrv: Option<RustcVersion>,
-) {
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>, msrv: &Msrv) {
     if_chain! {
-        if meets_msrv(msrv, msrvs::PTR_SLICE_RAW_PARTS);
+        if msrv.meets(msrvs::PTR_SLICE_RAW_PARTS);
         if let ty::RawPtr(ptrty) = cast_to.kind();
         if let ty::Slice(_) = ptrty.ty.kind();
         if let ExprKind::Call(fun, [ptr_arg, len_arg]) = cast_expr.peel_blocks().kind;
diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs
index 7148b5e6ebf..c6d505c4a18 100644
--- a/clippy_lints/src/casts/mod.rs
+++ b/clippy_lints/src/casts/mod.rs
@@ -21,11 +21,11 @@ mod ptr_as_ptr;
 mod unnecessary_cast;
 mod utils;
 
-use clippy_utils::{is_hir_ty_cfg_dependant, meets_msrv, msrvs};
+use clippy_utils::is_hir_ty_cfg_dependant;
+use clippy_utils::msrvs::{self, Msrv};
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 
 declare_clippy_lint! {
@@ -648,12 +648,12 @@ declare_clippy_lint! {
 }
 
 pub struct Casts {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl Casts {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -686,7 +686,7 @@ impl_lint_pass!(Casts => [
 impl<'tcx> LateLintPass<'tcx> for Casts {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if !in_external_macro(cx.sess(), expr.span) {
-            ptr_as_ptr::check(cx, expr, self.msrv);
+            ptr_as_ptr::check(cx, expr, &self.msrv);
         }
 
         if expr.span.from_expansion() {
@@ -705,7 +705,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
             if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
                 return;
             }
-            cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, self.msrv);
+            cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, &self.msrv);
             as_ptr_cast_mut::check(cx, expr, cast_expr, cast_to);
             fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to);
             fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);
@@ -717,16 +717,16 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
                     cast_possible_wrap::check(cx, expr, cast_from, cast_to);
                     cast_precision_loss::check(cx, expr, cast_from, cast_to);
                     cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
-                    cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
+                    cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv);
                     cast_nan_to_int::check(cx, expr, cast_expr, cast_from, cast_to);
                 }
-                cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
+                cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv);
                 cast_enum_constructor::check(cx, expr, cast_expr, cast_from);
             }
 
             as_underscore::check(cx, expr, cast_to_hir);
 
-            if meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) {
+            if self.msrv.meets(msrvs::BORROW_AS_PTR) {
                 borrow_as_ptr::check(cx, expr, cast_expr, cast_to_hir);
             }
         }
@@ -734,8 +734,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
         cast_ref_to_mut::check(cx, expr);
         cast_ptr_alignment::check(cx, expr);
         char_lit_as_u8::check(cx, expr);
-        ptr_as_ptr::check(cx, expr, self.msrv);
-        cast_slice_different_sizes::check(cx, expr, self.msrv);
+        ptr_as_ptr::check(cx, expr, &self.msrv);
+        cast_slice_different_sizes::check(cx, expr, &self.msrv);
     }
 
     extract_msrv_attr!(LateContext);
diff --git a/clippy_lints/src/casts/ptr_as_ptr.rs b/clippy_lints/src/casts/ptr_as_ptr.rs
index b9509ca656f..15ffb00da88 100644
--- a/clippy_lints/src/casts/ptr_as_ptr.rs
+++ b/clippy_lints/src/casts/ptr_as_ptr.rs
@@ -1,19 +1,18 @@
 use std::borrow::Cow;
 
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{meets_msrv, msrvs};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, Mutability, TyKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, TypeAndMut};
-use rustc_semver::RustcVersion;
 
 use super::PTR_AS_PTR;
 
-pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Option<RustcVersion>) {
-    if !meets_msrv(msrv, msrvs::POINTER_CAST) {
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) {
+    if !msrv.meets(msrvs::POINTER_CAST) {
         return;
     }
 
diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs
index c8596987e4d..7e23318076c 100644
--- a/clippy_lints/src/casts/unnecessary_cast.rs
+++ b/clippy_lints/src/casts/unnecessary_cast.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::get_parent_expr;
 use clippy_utils::numeric_literal::NumericLiteral;
 use clippy_utils::source::snippet_opt;
+use clippy_utils::{get_parent_expr, path_to_local};
 use if_chain::if_chain;
 use rustc_ast::{LitFloatType, LitIntType, LitKind};
 use rustc_errors::Applicability;
@@ -75,13 +75,26 @@ pub(super) fn check<'tcx>(
     }
 
     if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) {
+        if let Some(id) = path_to_local(cast_expr)
+            && let Some(span) = cx.tcx.hir().opt_span(id)
+            && span.ctxt() != cast_expr.span.ctxt()
+        {
+            // Binding context is different than the identifiers context.
+            // Weird macro wizardry could be involved here.
+            return false;
+        }
+
         span_lint_and_sugg(
             cx,
             UNNECESSARY_CAST,
             expr.span,
             &format!("casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)"),
             "try",
-            cast_str,
+            if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::AddrOf(..))) {
+                format!("{{ {cast_str} }}")
+            } else {
+                cast_str
+            },
             Applicability::MachineApplicable,
         );
         return true;
diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs
index 78e9921f036..9102a89e377 100644
--- a/clippy_lints/src/checked_conversions.rs
+++ b/clippy_lints/src/checked_conversions.rs
@@ -1,14 +1,14 @@
 //! lint on manually implemented checked conversions that could be transformed into `try_from`
 
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{in_constant, is_integer_literal, meets_msrv, msrvs, SpanlessEq};
+use clippy_utils::{in_constant, is_integer_literal, SpanlessEq};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 
 declare_clippy_lint! {
@@ -37,12 +37,12 @@ declare_clippy_lint! {
 }
 
 pub struct CheckedConversions {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl CheckedConversions {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -51,7 +51,7 @@ impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]);
 
 impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
     fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) {
-        if !meets_msrv(self.msrv, msrvs::TRY_FROM) {
+        if !self.msrv.meets(msrvs::TRY_FROM) {
             return;
         }
 
diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs
index 90430b71a0e..b38e09dc09f 100644
--- a/clippy_lints/src/collapsible_if.rs
+++ b/clippy_lints/src/collapsible_if.rs
@@ -160,11 +160,13 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &
         if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind;
         // Prevent triggering on `if c { if let a = b { .. } }`.
         if !matches!(check_inner.kind, ast::ExprKind::Let(..));
-        if expr.span.ctxt() == inner.span.ctxt();
+        let ctxt = expr.span.ctxt();
+        if inner.span.ctxt() == ctxt;
         then {
             span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this `if` statement can be collapsed", |diag| {
-                let lhs = Sugg::ast(cx, check, "..");
-                let rhs = Sugg::ast(cx, check_inner, "..");
+                let mut app = Applicability::MachineApplicable;
+                let lhs = Sugg::ast(cx, check, "..", ctxt, &mut app);
+                let rhs = Sugg::ast(cx, check_inner, "..", ctxt, &mut app);
                 diag.span_suggestion(
                     expr.span,
                     "collapse nested if block",
@@ -173,7 +175,7 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &
                         lhs.and(&rhs),
                         snippet_block(cx, content.span, "..", Some(expr.span)),
                     ),
-                    Applicability::MachineApplicable, // snippet
+                    app, // snippet
                 );
             });
         }
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index 0d3fc43a644..e4d76f07d6b 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -177,6 +177,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::from_raw_with_void_ptr::FROM_RAW_WITH_VOID_PTR_INFO,
     crate::from_str_radix_10::FROM_STR_RADIX_10_INFO,
     crate::functions::DOUBLE_MUST_USE_INFO,
+    crate::functions::MISNAMED_GETTERS_INFO,
     crate::functions::MUST_USE_CANDIDATE_INFO,
     crate::functions::MUST_USE_UNIT_INFO,
     crate::functions::NOT_UNSAFE_PTR_ARG_DEREF_INFO,
@@ -583,6 +584,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::types::TYPE_COMPLEXITY_INFO,
     crate::types::VEC_BOX_INFO,
     crate::undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS_INFO,
+    crate::undocumented_unsafe_blocks::UNNECESSARY_SAFETY_COMMENT_INFO,
     crate::unicode::INVISIBLE_CHARACTERS_INFO,
     crate::unicode::NON_ASCII_LITERAL_INFO,
     crate::unicode::UNICODE_NOT_NFC_INFO,
diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs
index 47ea98956be..38329659e02 100644
--- a/clippy_lints/src/dereference.rs
+++ b/clippy_lints/src/dereference.rs
@@ -1,12 +1,13 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
 use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap};
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 use clippy_utils::sugg::has_enclosing_paren;
 use clippy_utils::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res};
 use clippy_utils::{
-    fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, meets_msrv, msrvs, path_to_local,
-    walk_to_expr_usage,
+    fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, path_to_local, walk_to_expr_usage,
 };
+
 use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::graph::iterate::{CycleDetector, TriColorDepthFirstSearch};
@@ -28,7 +29,6 @@ use rustc_middle::ty::{
     self, Binder, BoundVariableKind, Clause, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind,
     ProjectionPredicate, Ty, TyCtxt, TypeVisitable, TypeckResults,
 };
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{symbol::sym, Span, Symbol};
 use rustc_trait_selection::infer::InferCtxtExt as _;
@@ -181,12 +181,12 @@ pub struct Dereferencing<'tcx> {
     possible_borrowers: Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
 
     // `IntoIterator` for arrays requires Rust 1.53.
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl<'tcx> Dereferencing<'tcx> {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self {
             msrv,
             ..Dereferencing::default()
@@ -286,26 +286,27 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
         match (self.state.take(), kind) {
             (None, kind) => {
                 let expr_ty = typeck.expr_ty(expr);
-                let (position, adjustments) = walk_parents(cx, &mut self.possible_borrowers, expr, self.msrv);
+                let (position, adjustments) = walk_parents(cx, &mut self.possible_borrowers, expr, &self.msrv);
                 match kind {
                     RefOp::Deref => {
+                        let sub_ty = typeck.expr_ty(sub_expr);
                         if let Position::FieldAccess {
                             name,
                             of_union: false,
                         } = position
-                            && !ty_contains_field(typeck.expr_ty(sub_expr), name)
+                            && !ty_contains_field(sub_ty, name)
                         {
                             self.state = Some((
                                 State::ExplicitDerefField { name },
                                 StateData { span: expr.span, hir_id: expr.hir_id, position },
                             ));
-                        } else if position.is_deref_stable() {
+                        } else if position.is_deref_stable() && sub_ty.is_ref() {
                             self.state = Some((
                                 State::ExplicitDeref { mutability: None },
                                 StateData { span: expr.span, hir_id: expr.hir_id, position },
                             ));
                         }
-                    }
+                    },
                     RefOp::Method(target_mut)
                         if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
                             && position.lint_explicit_deref() =>
@@ -320,7 +321,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
                             StateData {
                                 span: expr.span,
                                 hir_id: expr.hir_id,
-                                position
+                                position,
                             },
                         ));
                     },
@@ -394,7 +395,11 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
                                     msg,
                                     snip_expr,
                                 }),
-                                StateData { span: expr.span, hir_id: expr.hir_id, position },
+                                StateData {
+                                    span: expr.span,
+                                    hir_id: expr.hir_id,
+                                    position,
+                                },
                             ));
                         } else if position.is_deref_stable()
                             // Auto-deref doesn't combine with other adjustments
@@ -406,7 +411,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
                                 StateData {
                                     span: expr.span,
                                     hir_id: expr.hir_id,
-                                    position
+                                    position,
                                 },
                             ));
                         }
@@ -698,7 +703,7 @@ fn walk_parents<'tcx>(
     cx: &LateContext<'tcx>,
     possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
     e: &'tcx Expr<'_>,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) -> (Position, &'tcx [Adjustment<'tcx>]) {
     let mut adjustments = [].as_slice();
     let mut precedence = 0i8;
@@ -862,7 +867,11 @@ fn walk_parents<'tcx>(
                             } && impl_ty.is_ref()
                             && let infcx = cx.tcx.infer_ctxt().build()
                             && infcx
-                                .type_implements_trait(trait_id, [impl_ty.into()].into_iter().chain(subs.iter().copied()), cx.param_env)
+                                .type_implements_trait(
+                                    trait_id,
+                                    [impl_ty.into()].into_iter().chain(subs.iter().copied()),
+                                    cx.param_env,
+                                )
                                 .must_apply_modulo_regions()
                         {
                             return Some(Position::MethodReceiverRefImpl)
@@ -1078,7 +1087,7 @@ fn needless_borrow_impl_arg_position<'tcx>(
     param_ty: ParamTy,
     mut expr: &Expr<'tcx>,
     precedence: i8,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) -> Position {
     let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait();
     let sized_trait_def_id = cx.tcx.lang_items().sized_trait();
@@ -1178,7 +1187,7 @@ fn needless_borrow_impl_arg_position<'tcx>(
                 && let ty::Param(param_ty) = trait_predicate.self_ty().kind()
                 && let GenericArgKind::Type(ty) = substs_with_referent_ty[param_ty.index as usize].unpack()
                 && ty.is_array()
-                && !meets_msrv(msrv, msrvs::ARRAY_INTO_ITERATOR)
+                && !msrv.meets(msrvs::ARRAY_INTO_ITERATOR)
             {
                 return false;
             }
diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs
index d870e0ceef4..9e596ca8157 100644
--- a/clippy_lints/src/derive.rs
+++ b/clippy_lints/src/derive.rs
@@ -14,8 +14,8 @@ use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::traits::Reveal;
 use rustc_middle::ty::{
-    self, Binder, BoundConstness, Clause, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef,
-    Ty, TyCtxt,
+    self, Binder, BoundConstness, Clause, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate,
+    TraitRef, Ty, TyCtxt,
 };
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Span;
diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs
index ae5f9424b23..cdc23a4d227 100644
--- a/clippy_lints/src/doc.rs
+++ b/clippy_lints/src/doc.rs
@@ -253,7 +253,7 @@ declare_clippy_lint! {
     /// ```
     #[clippy::version = "1.66.0"]
     pub UNNECESSARY_SAFETY_DOC,
-    style,
+    restriction,
     "`pub fn` or `pub trait` with `# Safety` docs"
 }
 
diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs
index f34cbee0355..3543910c3b5 100644
--- a/clippy_lints/src/eta_reduction.rs
+++ b/clippy_lints/src/eta_reduction.rs
@@ -11,7 +11,7 @@ use rustc_hir::{Closure, Expr, ExprKind, Param, PatKind, Unsafety};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
 use rustc_middle::ty::binding::BindingMode;
-use rustc_middle::ty::{self, Ty, TypeVisitable};
+use rustc_middle::ty::{self, EarlyBinder, SubstsRef, Ty, TypeVisitable};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::symbol::sym;
 
@@ -125,7 +125,12 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
                     if let Some(mut snippet) = snippet_opt(cx, callee.span) {
                         if let Some(fn_mut_id) = cx.tcx.lang_items().fn_mut_trait()
                             && let args = cx.tcx.erase_late_bound_regions(substs.as_closure().sig()).inputs()
-                            && implements_trait(cx, callee_ty.peel_refs(), fn_mut_id, &args.iter().copied().map(Into::into).collect::<Vec<_>>())
+                            && implements_trait(
+                                   cx,
+                                   callee_ty.peel_refs(),
+                                   fn_mut_id,
+                                   &args.iter().copied().map(Into::into).collect::<Vec<_>>(),
+                               )
                             && path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr))
                         {
                                 // Mutable closure is used after current expr; we cannot consume it.
@@ -152,7 +157,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
             if check_sig(cx, closure_ty, call_ty);
             then {
                 span_lint_and_then(cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, "redundant closure", |diag| {
-                    let name = get_ufcs_type_name(cx, method_def_id);
+                    let name = get_ufcs_type_name(cx, method_def_id, substs);
                     diag.span_suggestion(
                         expr.span,
                         "replace the closure with the method itself",
@@ -222,7 +227,7 @@ fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure_ty: Ty<'tcx>, call_ty: Ty<'tc
     cx.tcx.erase_late_bound_regions(closure_sig) == cx.tcx.erase_late_bound_regions(call_sig)
 }
 
-fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: DefId) -> String {
+fn get_ufcs_type_name<'tcx>(cx: &LateContext<'tcx>, method_def_id: DefId, substs: SubstsRef<'tcx>) -> String {
     let assoc_item = cx.tcx.associated_item(method_def_id);
     let def_id = assoc_item.container_id(cx.tcx);
     match assoc_item.container {
@@ -231,6 +236,15 @@ fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: DefId) -> String {
             let ty = cx.tcx.type_of(def_id);
             match ty.kind() {
                 ty::Adt(adt, _) => cx.tcx.def_path_str(adt.did()),
+                ty::Array(..)
+                | ty::Dynamic(..)
+                | ty::Never
+                | ty::RawPtr(_)
+                | ty::Ref(..)
+                | ty::Slice(_)
+                | ty::Tuple(_) => {
+                    format!("<{}>", EarlyBinder(ty).subst(cx.tcx, substs))
+                },
                 _ => ty.to_string(),
             }
         },
diff --git a/clippy_lints/src/exit.rs b/clippy_lints/src/exit.rs
index 407dd1b3957..9c8b0d076df 100644
--- a/clippy_lints/src/exit.rs
+++ b/clippy_lints/src/exit.rs
@@ -7,21 +7,34 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
 
 declare_clippy_lint! {
     /// ### What it does
-    /// `exit()`  terminates the program and doesn't provide a
-    /// stack trace.
+    /// Detects calls to the `exit()` function which terminates the program.
     ///
     /// ### Why is this bad?
-    /// Ideally a program is terminated by finishing
+    /// Exit terminates the program at the location it is called. For unrecoverable
+    /// errors `panics` should be used to provide a stacktrace and potentualy other
+    /// information. A normal termination or one with an error code should happen in
     /// the main function.
     ///
     /// ### Example
-    /// ```ignore
+    /// ```
     /// std::process::exit(0)
     /// ```
+    ///
+    /// Use instead:
+    ///
+    /// ```ignore
+    /// // To provide a stacktrace and additional information
+    /// panic!("message");
+    ///
+    /// // or a main method with a return
+    /// fn main() -> Result<(), i32> {
+    ///     Ok(())
+    /// }
+    /// ```
     #[clippy::version = "1.41.0"]
     pub EXIT,
     restriction,
-    "`std::process::exit` is called, terminating the program"
+    "detects `std::process::exit` calls"
 }
 
 declare_lint_pass!(Exit => [EXIT]);
diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs
index f0fe845d330..f0995a81329 100644
--- a/clippy_lints/src/format_args.rs
+++ b/clippy_lints/src/format_args.rs
@@ -1,19 +1,22 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::macros::FormatParamKind::{Implicit, Named, Numbered, Starred};
+use clippy_utils::is_diag_trait_item;
+use clippy_utils::macros::FormatParamKind::{Implicit, Named, NamedInline, Numbered, Starred};
 use clippy_utils::macros::{
     is_format_macro, is_panic, root_macro_call, Count, FormatArg, FormatArgsExpn, FormatParam, FormatParamUsage,
 };
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
-use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs};
 use if_chain::if_chain;
 use itertools::Itertools;
-use rustc_errors::Applicability;
+use rustc_errors::{
+    Applicability,
+    SuggestionStyle::{CompletelyHidden, ShowCode},
+};
 use rustc_hir::{Expr, ExprKind, HirId, QPath};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::ty::adjustment::{Adjust, Adjustment};
 use rustc_middle::ty::Ty;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::def_id::DefId;
 use rustc_span::edition::Edition::Edition2021;
@@ -103,19 +106,25 @@ declare_clippy_lint! {
     /// format!("{var:.prec$}");
     /// ```
     ///
-    /// ### Known Problems
-    ///
-    /// There may be a false positive if the format string is expanded from certain proc macros:
-    ///
-    /// ```ignore
-    /// println!(indoc!("{}"), var);
+    /// If allow-mixed-uninlined-format-args is set to false in clippy.toml,
+    /// the following code will also trigger the lint:
+    /// ```rust
+    /// # let var = 42;
+    /// format!("{} {}", var, 1+2);
     /// ```
+    /// Use instead:
+    /// ```rust
+    /// # let var = 42;
+    /// format!("{var} {}", 1+2);
+    /// ```
+    ///
+    /// ### Known Problems
     ///
     /// If a format string contains a numbered argument that cannot be inlined
     /// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`.
     #[clippy::version = "1.65.0"]
     pub UNINLINED_FORMAT_ARGS,
-    pedantic,
+    style,
     "using non-inlined variables in `format!` calls"
 }
 
@@ -158,13 +167,17 @@ impl_lint_pass!(FormatArgs => [
 ]);
 
 pub struct FormatArgs {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
+    ignore_mixed: bool,
 }
 
 impl FormatArgs {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
-        Self { msrv }
+    pub fn new(msrv: Msrv, allow_mixed_uninlined_format_args: bool) -> Self {
+        Self {
+            msrv,
+            ignore_mixed: allow_mixed_uninlined_format_args,
+        }
     }
 }
 
@@ -188,8 +201,8 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
                 check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value);
                 check_to_string_in_format_args(cx, name, arg.param.value);
             }
-            if meets_msrv(self.msrv, msrvs::FORMAT_ARGS_CAPTURE) {
-                check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_id);
+            if self.msrv.meets(msrvs::FORMAT_ARGS_CAPTURE) {
+                check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_id, self.ignore_mixed);
             }
         }
     }
@@ -267,7 +280,13 @@ fn check_unused_format_specifier(cx: &LateContext<'_>, arg: &FormatArg<'_>) {
     }
 }
 
-fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_site: Span, def_id: DefId) {
+fn check_uninlined_args(
+    cx: &LateContext<'_>,
+    args: &FormatArgsExpn<'_>,
+    call_site: Span,
+    def_id: DefId,
+    ignore_mixed: bool,
+) {
     if args.format_string.span.from_expansion() {
         return;
     }
@@ -282,14 +301,13 @@ fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_si
     // we cannot remove any other arguments in the format string,
     // because the index numbers might be wrong after inlining.
     // Example of an un-inlinable format:  print!("{}{1}", foo, 2)
-    if !args.params().all(|p| check_one_arg(args, &p, &mut fixes)) || fixes.is_empty() {
+    if !args.params().all(|p| check_one_arg(args, &p, &mut fixes, ignore_mixed)) || fixes.is_empty() {
         return;
     }
 
-    // Temporarily ignore multiline spans: https://github.com/rust-lang/rust/pull/102729#discussion_r988704308
-    if fixes.iter().any(|(span, _)| cx.sess().source_map().is_multiline(*span)) {
-        return;
-    }
+    // multiline span display suggestion is sometimes broken: https://github.com/rust-lang/rust/pull/102729#discussion_r988704308
+    // in those cases, make the code suggestion hidden
+    let multiline_fix = fixes.iter().any(|(span, _)| cx.sess().source_map().is_multiline(*span));
 
     span_lint_and_then(
         cx,
@@ -297,12 +315,22 @@ fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_si
         call_site,
         "variables can be used directly in the `format!` string",
         |diag| {
-            diag.multipart_suggestion("change this to", fixes, Applicability::MachineApplicable);
+            diag.multipart_suggestion_with_style(
+                "change this to",
+                fixes,
+                Applicability::MachineApplicable,
+                if multiline_fix { CompletelyHidden } else { ShowCode },
+            );
         },
     );
 }
 
-fn check_one_arg(args: &FormatArgsExpn<'_>, param: &FormatParam<'_>, fixes: &mut Vec<(Span, String)>) -> bool {
+fn check_one_arg(
+    args: &FormatArgsExpn<'_>,
+    param: &FormatParam<'_>,
+    fixes: &mut Vec<(Span, String)>,
+    ignore_mixed: bool,
+) -> bool {
     if matches!(param.kind, Implicit | Starred | Named(_) | Numbered)
         && let ExprKind::Path(QPath::Resolved(None, path)) = param.value.kind
         && let [segment] = path.segments
@@ -317,8 +345,10 @@ fn check_one_arg(args: &FormatArgsExpn<'_>, param: &FormatParam<'_>, fixes: &mut
         fixes.push((arg_span, String::new()));
         true  // successful inlining, continue checking
     } else {
-        // if we can't inline a numbered argument, we can't continue
-        param.kind != Numbered
+        // Do not continue inlining (return false) in case
+        // * if we can't inline a numbered argument, e.g. `print!("{0} ...", foo.bar, ...)`
+        // * if allow_mixed_uninlined_format_args is false and this arg hasn't been inlined already
+        param.kind != Numbered && (!ignore_mixed || matches!(param.kind, NamedInline(_)))
     }
 }
 
@@ -330,12 +360,7 @@ fn outermost_expn_data(expn_data: ExpnData) -> ExpnData {
     }
 }
 
-fn check_format_in_format_args(
-    cx: &LateContext<'_>,
-    call_site: Span,
-    name: Symbol,
-    arg: &Expr<'_>,
-) {
+fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symbol, arg: &Expr<'_>) {
     let expn_data = arg.span.ctxt().outer_expn_data();
     if expn_data.call_site.from_expansion() {
         return;
@@ -408,7 +433,10 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex
 
 /// Returns true if `hir_id` is referred to by multiple format params
 fn is_aliased(args: &FormatArgsExpn<'_>, hir_id: HirId) -> bool {
-    args.params().filter(|param| param.value.hir_id == hir_id).at_most_one().is_err()
+    args.params()
+        .filter(|param| param.value.hir_id == hir_id)
+        .at_most_one()
+        .is_err()
 }
 
 fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>)
@@ -418,7 +446,11 @@ where
     let mut n_total = 0;
     let mut n_needed = 0;
     loop {
-        if let Some(Adjustment { kind: Adjust::Deref(overloaded_deref), target }) = iter.next() {
+        if let Some(Adjustment {
+            kind: Adjust::Deref(overloaded_deref),
+            target,
+        }) = iter.next()
+        {
             n_total += 1;
             if overloaded_deref.is_some() {
                 n_needed = n_total;
diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs
index 8b24a4962fb..8621504c1b4 100644
--- a/clippy_lints/src/from_over_into.rs
+++ b/clippy_lints/src/from_over_into.rs
@@ -1,7 +1,8 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::macros::span_is_local;
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::path_def_id;
 use clippy_utils::source::snippet_opt;
-use clippy_utils::{meets_msrv, msrvs, path_def_id};
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_path, Visitor};
 use rustc_hir::{
@@ -10,7 +11,6 @@ use rustc_hir::{
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::nested_filter::OnlyBodies;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::symbol::{kw, sym};
 use rustc_span::{Span, Symbol};
@@ -49,12 +49,12 @@ declare_clippy_lint! {
 }
 
 pub struct FromOverInto {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl FromOverInto {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         FromOverInto { msrv }
     }
 }
@@ -63,7 +63,7 @@ impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]);
 
 impl<'tcx> LateLintPass<'tcx> for FromOverInto {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
-        if !meets_msrv(self.msrv, msrvs::RE_REBALANCING_COHERENCE) || !span_is_local(item.span) {
+        if !self.msrv.meets(msrvs::RE_REBALANCING_COHERENCE) || !span_is_local(item.span) {
             return;
         }
 
diff --git a/clippy_lints/src/functions/misnamed_getters.rs b/clippy_lints/src/functions/misnamed_getters.rs
new file mode 100644
index 00000000000..27acad45ccf
--- /dev/null
+++ b/clippy_lints/src/functions/misnamed_getters.rs
@@ -0,0 +1,125 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet;
+use rustc_errors::Applicability;
+use rustc_hir::{intravisit::FnKind, Body, ExprKind, FnDecl, HirId, ImplicitSelfKind, Unsafety};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_span::Span;
+
+use std::iter;
+
+use super::MISNAMED_GETTERS;
+
+pub fn check_fn(
+    cx: &LateContext<'_>,
+    kind: FnKind<'_>,
+    decl: &FnDecl<'_>,
+    body: &Body<'_>,
+    span: Span,
+    _hir_id: HirId,
+) {
+    let FnKind::Method(ref ident, sig) = kind else {
+            return;
+        };
+
+    // Takes only &(mut) self
+    if decl.inputs.len() != 1 {
+        return;
+    }
+
+    let name = ident.name.as_str();
+
+    let name = match decl.implicit_self {
+        ImplicitSelfKind::MutRef => {
+            let Some(name) = name.strip_suffix("_mut") else {
+                    return;
+                };
+            name
+        },
+        ImplicitSelfKind::Imm | ImplicitSelfKind::Mut | ImplicitSelfKind::ImmRef => name,
+        ImplicitSelfKind::None => return,
+    };
+
+    let name = if sig.header.unsafety == Unsafety::Unsafe {
+        name.strip_suffix("_unchecked").unwrap_or(name)
+    } else {
+        name
+    };
+
+    // Body must be &(mut) <self_data>.name
+    // self_data is not neccessarilly self, to also lint sub-getters, etc…
+
+    let block_expr = if_chain! {
+        if let ExprKind::Block(block,_) = body.value.kind;
+        if block.stmts.is_empty();
+        if let Some(block_expr) = block.expr;
+        then {
+            block_expr
+        } else {
+            return;
+        }
+    };
+    let expr_span = block_expr.span;
+
+    // Accept &<expr>, &mut <expr> and <expr>
+    let expr = if let ExprKind::AddrOf(_, _, tmp) = block_expr.kind {
+        tmp
+    } else {
+        block_expr
+    };
+    let (self_data, used_ident) = if_chain! {
+        if let ExprKind::Field(self_data, ident) = expr.kind;
+        if ident.name.as_str() != name;
+        then {
+            (self_data, ident)
+        } else {
+            return;
+        }
+    };
+
+    let mut used_field = None;
+    let mut correct_field = None;
+    let typeck_results = cx.typeck_results();
+    for adjusted_type in iter::once(typeck_results.expr_ty(self_data))
+        .chain(typeck_results.expr_adjustments(self_data).iter().map(|adj| adj.target))
+    {
+        let ty::Adt(def,_) = adjusted_type.kind() else {
+            continue;
+        };
+
+        for f in def.all_fields() {
+            if f.name.as_str() == name {
+                correct_field = Some(f);
+            }
+            if f.name == used_ident.name {
+                used_field = Some(f);
+            }
+        }
+    }
+
+    let Some(used_field) = used_field else {
+        // Can happen if the field access is a tuple. We don't lint those because the getter name could not start with a number.
+        return;
+    };
+
+    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
+        return;
+    };
+
+    if cx.tcx.type_of(used_field.did) == cx.tcx.type_of(correct_field.did) {
+        let left_span = block_expr.span.until(used_ident.span);
+        let snippet = snippet(cx, left_span, "..");
+        let sugg = format!("{snippet}{name}");
+        span_lint_and_then(
+            cx,
+            MISNAMED_GETTERS,
+            span,
+            "getter function appears to return the wrong field",
+            |diag| {
+                diag.span_suggestion(expr_span, "consider using", sugg, Applicability::MaybeIncorrect);
+            },
+        );
+    }
+}
diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs
index ae0e0833446..91e6ffe6447 100644
--- a/clippy_lints/src/functions/mod.rs
+++ b/clippy_lints/src/functions/mod.rs
@@ -1,3 +1,4 @@
+mod misnamed_getters;
 mod must_use;
 mod not_unsafe_ptr_arg_deref;
 mod result;
@@ -260,6 +261,48 @@ declare_clippy_lint! {
     "function returning `Result` with large `Err` type"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for getter methods that return a field that doesn't correspond
+    /// to the name of the method, when there is a field's whose name matches that of the method.
+    ///
+    /// ### Why is this bad?
+    /// It is most likely that such a  method is a bug caused by a typo or by copy-pasting.
+    ///
+    /// ### Example
+
+    /// ```rust
+    /// struct A {
+    ///     a: String,
+    ///     b: String,
+    /// }
+    ///
+    /// impl A {
+    ///     fn a(&self) -> &str{
+    ///         &self.b
+    ///     }
+    /// }
+
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// struct A {
+    ///     a: String,
+    ///     b: String,
+    /// }
+    ///
+    /// impl A {
+    ///     fn a(&self) -> &str{
+    ///         &self.a
+    ///     }
+    /// }
+    /// ```
+    #[clippy::version = "1.67.0"]
+    pub MISNAMED_GETTERS,
+    suspicious,
+    "getter method returning the wrong field"
+}
+
 #[derive(Copy, Clone)]
 pub struct Functions {
     too_many_arguments_threshold: u64,
@@ -286,6 +329,7 @@ impl_lint_pass!(Functions => [
     MUST_USE_CANDIDATE,
     RESULT_UNIT_ERR,
     RESULT_LARGE_ERR,
+    MISNAMED_GETTERS,
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Functions {
@@ -301,6 +345,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
         too_many_arguments::check_fn(cx, kind, decl, span, hir_id, self.too_many_arguments_threshold);
         too_many_lines::check_fn(cx, kind, span, body, self.too_many_lines_threshold);
         not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, hir_id);
+        misnamed_getters::check_fn(cx, kind, decl, body, span, hir_id);
     }
 
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
diff --git a/clippy_lints/src/functions/result.rs b/clippy_lints/src/functions/result.rs
index f7e30b051a6..23da145d038 100644
--- a/clippy_lints/src/functions/result.rs
+++ b/clippy_lints/src/functions/result.rs
@@ -94,7 +94,9 @@ fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty
         if let hir::ItemKind::Enum(ref def, _) = item.kind;
         then {
             let variants_size = AdtVariantInfo::new(cx, *adt, subst);
-            if variants_size[0].size >= large_err_threshold {
+            if let Some((first_variant, variants)) = variants_size.split_first()
+                && first_variant.size >= large_err_threshold
+            {
                 span_lint_and_then(
                     cx,
                     RESULT_LARGE_ERR,
@@ -102,11 +104,11 @@ fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty
                     "the `Err`-variant returned from this function is very large",
                     |diag| {
                         diag.span_label(
-                            def.variants[variants_size[0].ind].span,
+                            def.variants[first_variant.ind].span,
                             format!("the largest variant contains at least {} bytes", variants_size[0].size),
                         );
 
-                        for variant in &variants_size[1..] {
+                        for variant in variants {
                             if variant.size >= large_err_threshold {
                                 let variant_def = &def.variants[variant.ind];
                                 diag.span_label(
diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs
index a9425a40f88..61934a91426 100644
--- a/clippy_lints/src/future_not_send.rs
+++ b/clippy_lints/src/future_not_send.rs
@@ -91,7 +91,9 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend {
                                 infcx
                                     .err_ctxt()
                                     .maybe_note_obligation_cause_for_async_await(db, &obligation);
-                                if let PredicateKind::Clause(Clause::Trait(trait_pred)) = obligation.predicate.kind().skip_binder() {
+                                if let PredicateKind::Clause(Clause::Trait(trait_pred)) =
+                                    obligation.predicate.kind().skip_binder()
+                                {
                                     db.note(&format!(
                                         "`{}` doesn't implement `{}`",
                                         trait_pred.self_ty(),
diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs
index 0d6718c168a..9cadaaa493e 100644
--- a/clippy_lints/src/if_then_some_else_none.rs
+++ b/clippy_lints/src/if_then_some_else_none.rs
@@ -1,14 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::eager_or_lazy::switch_to_eager_eval;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_macro_callsite;
-use clippy_utils::{
-    contains_return, higher, is_else_clause, is_res_lang_ctor, meets_msrv, msrvs, path_res, peel_blocks,
-};
+use clippy_utils::{contains_return, higher, is_else_clause, is_res_lang_ctor, path_res, peel_blocks};
 use rustc_hir::LangItem::{OptionNone, OptionSome};
 use rustc_hir::{Expr, ExprKind, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 
 declare_clippy_lint! {
@@ -47,12 +45,12 @@ declare_clippy_lint! {
 }
 
 pub struct IfThenSomeElseNone {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl IfThenSomeElseNone {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -61,7 +59,7 @@ impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]);
 
 impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-        if !meets_msrv(self.msrv, msrvs::BOOL_THEN) {
+        if !self.msrv.meets(msrvs::BOOL_THEN) {
             return;
         }
 
@@ -94,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
             } else {
                 format!("{{ /* snippet */ {arg_snip} }}")
             };
-            let method_name = if switch_to_eager_eval(cx, expr) && meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) {
+            let method_name = if switch_to_eager_eval(cx, expr) && self.msrv.meets(msrvs::BOOL_THEN_SOME) {
                 "then_some"
             } else {
                 method_body.insert_str(0, "|| ");
diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs
index 0d5099bde6d..cf35b1f175c 100644
--- a/clippy_lints/src/index_refutable_slice.rs
+++ b/clippy_lints/src/index_refutable_slice.rs
@@ -1,8 +1,9 @@
 use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::higher::IfLet;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::ty::is_copy;
-use clippy_utils::{is_expn_of, is_lint_allowed, meets_msrv, msrvs, path_to_local};
+use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local};
 use if_chain::if_chain;
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
 use rustc_errors::Applicability;
@@ -11,7 +12,6 @@ use rustc_hir::intravisit::{self, Visitor};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{symbol::Ident, Span};
 
@@ -47,18 +47,17 @@ declare_clippy_lint! {
     /// ```
     #[clippy::version = "1.59.0"]
     pub INDEX_REFUTABLE_SLICE,
-    nursery,
+    pedantic,
     "avoid indexing on slices which could be destructed"
 }
 
-#[derive(Copy, Clone)]
 pub struct IndexRefutableSlice {
     max_suggested_slice: u64,
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl IndexRefutableSlice {
-    pub fn new(max_suggested_slice_pattern_length: u64, msrv: Option<RustcVersion>) -> Self {
+    pub fn new(max_suggested_slice_pattern_length: u64, msrv: Msrv) -> Self {
         Self {
             max_suggested_slice: max_suggested_slice_pattern_length,
             msrv,
@@ -74,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice {
             if !expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some();
             if let Some(IfLet {let_pat, if_then, ..}) = IfLet::hir(cx, expr);
             if !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id);
-            if meets_msrv(self.msrv, msrvs::SLICE_PATTERNS);
+            if self.msrv.meets(msrvs::SLICE_PATTERNS);
 
             let found_slices = find_slice_values(cx, let_pat);
             if !found_slices.is_empty();
diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs
index 60754b224fc..dd1b23e7d9d 100644
--- a/clippy_lints/src/instant_subtraction.rs
+++ b/clippy_lints/src/instant_subtraction.rs
@@ -1,13 +1,11 @@
-use clippy_utils::{
-    diagnostics::{self, span_lint_and_sugg},
-    meets_msrv, msrvs, source,
-    sugg::Sugg,
-    ty,
-};
+use clippy_utils::diagnostics::{self, span_lint_and_sugg};
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::source;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{source_map::Spanned, sym};
 
@@ -68,12 +66,12 @@ declare_clippy_lint! {
 }
 
 pub struct InstantSubtraction {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl InstantSubtraction {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -101,7 +99,7 @@ impl LateLintPass<'_> for InstantSubtraction {
                 } else {
                     if_chain! {
                         if !expr.span.from_expansion();
-                        if meets_msrv(self.msrv, msrvs::TRY_FROM);
+                        if self.msrv.meets(msrvs::TRY_FROM);
 
                         if is_an_instant(cx, lhs);
                         if is_a_duration(cx, rhs);
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 601990cd6a3..7b17d8a156d 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -52,10 +52,9 @@ extern crate declare_clippy_lint;
 use std::io;
 use std::path::PathBuf;
 
-use clippy_utils::parse_msrv;
+use clippy_utils::msrvs::Msrv;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_lint::{Lint, LintId};
-use rustc_semver::RustcVersion;
 use rustc_session::Session;
 
 #[cfg(feature = "internal")]
@@ -322,48 +321,10 @@ pub use crate::utils::conf::{lookup_conf_file, Conf};
 /// Used in `./src/driver.rs`.
 pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
     // NOTE: Do not add any more pre-expansion passes. These should be removed eventually.
+    let msrv = Msrv::read(&conf.msrv, sess);
+    let msrv = move || msrv.clone();
 
-    let msrv = conf.msrv.as_ref().and_then(|s| {
-        parse_msrv(s, None, None).or_else(|| {
-            sess.err(format!(
-                "error reading Clippy's configuration file. `{s}` is not a valid Rust version"
-            ));
-            None
-        })
-    });
-
-    store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv }));
-}
-
-fn read_msrv(conf: &Conf, sess: &Session) -> Option<RustcVersion> {
-    let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION")
-        .ok()
-        .and_then(|v| parse_msrv(&v, None, None));
-    let clippy_msrv = conf.msrv.as_ref().and_then(|s| {
-        parse_msrv(s, None, None).or_else(|| {
-            sess.err(format!(
-                "error reading Clippy's configuration file. `{s}` is not a valid Rust version"
-            ));
-            None
-        })
-    });
-
-    if let Some(cargo_msrv) = cargo_msrv {
-        if let Some(clippy_msrv) = clippy_msrv {
-            // if both files have an msrv, let's compare them and emit a warning if they differ
-            if clippy_msrv != cargo_msrv {
-                sess.warn(format!(
-                    "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`"
-                ));
-            }
-
-            Some(clippy_msrv)
-        } else {
-            Some(cargo_msrv)
-        }
-    } else {
-        clippy_msrv
-    }
+    store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv: msrv() }));
 }
 
 #[doc(hidden)]
@@ -595,43 +556,44 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
     store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports));
 
-    let msrv = read_msrv(conf, sess);
+    let msrv = Msrv::read(&conf.msrv, sess);
+    let msrv = move || msrv.clone();
     let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
     let allow_expect_in_tests = conf.allow_expect_in_tests;
     let allow_unwrap_in_tests = conf.allow_unwrap_in_tests;
-    store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv)));
+    store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv())));
     store.register_late_pass(move |_| {
         Box::new(methods::Methods::new(
             avoid_breaking_exported_api,
-            msrv,
+            msrv(),
             allow_expect_in_tests,
             allow_unwrap_in_tests,
         ))
     });
-    store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv)));
+    store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv())));
     let matches_for_let_else = conf.matches_for_let_else;
-    store.register_late_pass(move |_| Box::new(manual_let_else::ManualLetElse::new(msrv, matches_for_let_else)));
-    store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv)));
-    store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv)));
-    store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(msrv)));
-    store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv)));
-    store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv)));
-    store.register_late_pass(move |_| Box::new(checked_conversions::CheckedConversions::new(msrv)));
-    store.register_late_pass(move |_| Box::new(mem_replace::MemReplace::new(msrv)));
-    store.register_late_pass(move |_| Box::new(ranges::Ranges::new(msrv)));
-    store.register_late_pass(move |_| Box::new(from_over_into::FromOverInto::new(msrv)));
-    store.register_late_pass(move |_| Box::new(use_self::UseSelf::new(msrv)));
-    store.register_late_pass(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(msrv)));
+    store.register_late_pass(move |_| Box::new(manual_let_else::ManualLetElse::new(msrv(), matches_for_let_else)));
+    store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv())));
+    store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv())));
+    store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(msrv())));
+    store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv())));
+    store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv())));
+    store.register_late_pass(move |_| Box::new(checked_conversions::CheckedConversions::new(msrv())));
+    store.register_late_pass(move |_| Box::new(mem_replace::MemReplace::new(msrv())));
+    store.register_late_pass(move |_| Box::new(ranges::Ranges::new(msrv())));
+    store.register_late_pass(move |_| Box::new(from_over_into::FromOverInto::new(msrv())));
+    store.register_late_pass(move |_| Box::new(use_self::UseSelf::new(msrv())));
+    store.register_late_pass(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(msrv())));
     store.register_late_pass(move |_| Box::new(needless_question_mark::NeedlessQuestionMark));
-    store.register_late_pass(move |_| Box::new(casts::Casts::new(msrv)));
-    store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv)));
+    store.register_late_pass(move |_| Box::new(casts::Casts::new(msrv())));
+    store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv())));
     store.register_late_pass(|_| Box::new(size_of_in_element_count::SizeOfInElementCount));
     store.register_late_pass(|_| Box::new(same_name_method::SameNameMethod));
     let max_suggested_slice_pattern_length = conf.max_suggested_slice_pattern_length;
     store.register_late_pass(move |_| {
         Box::new(index_refutable_slice::IndexRefutableSlice::new(
             max_suggested_slice_pattern_length,
-            msrv,
+            msrv(),
         ))
     });
     store.register_late_pass(|_| Box::<shadow::Shadow>::default());
@@ -648,7 +610,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|_| Box::new(borrow_deref_ref::BorrowDerefRef));
     store.register_late_pass(|_| Box::new(no_effect::NoEffect));
     store.register_late_pass(|_| Box::new(temporary_assignment::TemporaryAssignment));
-    store.register_late_pass(move |_| Box::new(transmute::Transmute::new(msrv)));
+    store.register_late_pass(move |_| Box::new(transmute::Transmute::new(msrv())));
     let cognitive_complexity_threshold = conf.cognitive_complexity_threshold;
     store.register_late_pass(move |_| {
         Box::new(cognitive_complexity::CognitiveComplexity::new(
@@ -806,7 +768,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)));
     store.register_late_pass(|_| Box::<redundant_pub_crate::RedundantPubCrate>::default());
     store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress));
-    store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv)));
+    store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv())));
     store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse));
     store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend));
     store.register_late_pass(|_| Box::new(if_let_mutex::IfLetMutex));
@@ -840,7 +802,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|_| Box::<vec_init_then_push::VecInitThenPush>::default());
     store.register_late_pass(|_| Box::new(redundant_slicing::RedundantSlicing));
     store.register_late_pass(|_| Box::new(from_str_radix_10::FromStrRadix10));
-    store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv)));
+    store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv())));
     store.register_late_pass(|_| Box::new(bool_assert_comparison::BoolAssertComparison));
     store.register_early_pass(move || Box::new(module_style::ModStyle));
     store.register_late_pass(|_| Box::new(unused_async::UnusedAsync));
@@ -865,14 +827,15 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         ))
     });
     store.register_late_pass(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks));
-    store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv)));
+    let allow_mixed_uninlined = conf.allow_mixed_uninlined_format_args;
+    store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv(), allow_mixed_uninlined)));
     store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray));
     store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes));
     store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit));
     store.register_late_pass(|_| Box::new(return_self_not_must_use::ReturnSelfNotMustUse));
     store.register_late_pass(|_| Box::new(init_numbered_fields::NumberedFields));
     store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames));
-    store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv)));
+    store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv())));
     store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation));
     store.register_late_pass(|_| Box::<only_used_in_recursion::OnlyUsedInRecursion>::default());
     let allow_dbg_in_tests = conf.allow_dbg_in_tests;
@@ -896,20 +859,20 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
     store.register_early_pass(|| Box::<duplicate_mod::DuplicateMod>::default());
     store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding));
-    store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv)));
+    store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv())));
     store.register_late_pass(|_| Box::new(swap_ptr_to_ref::SwapPtrToRef));
     store.register_late_pass(|_| Box::new(mismatching_type_param_order::TypeParamMismatch));
     store.register_late_pass(|_| Box::new(read_zero_byte_vec::ReadZeroByteVec));
     store.register_late_pass(|_| Box::new(default_instead_of_iter_empty::DefaultIterEmpty));
-    store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv)));
-    store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(msrv)));
+    store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv())));
+    store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(msrv())));
     let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
     store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold)));
     store.register_late_pass(|_| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked));
     store.register_late_pass(|_| Box::<std_instead_of_core::StdReexports>::default());
-    store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv)));
+    store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv())));
     store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone));
-    store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv)));
+    store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv())));
     store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew));
     store.register_late_pass(|_| Box::new(unused_peekable::UnusedPeekable));
     store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments));
@@ -920,7 +883,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|_| Box::new(missing_trait_methods::MissingTraitMethods));
     store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr));
     store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow));
-    store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv)));
+    store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv())));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs
index 220941dcd5d..7cf1a6b8084 100644
--- a/clippy_lints/src/lifetimes.rs
+++ b/clippy_lints/src/lifetimes.rs
@@ -3,7 +3,7 @@ use clippy_utils::trait_ref_of_method;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter};
 use rustc_hir::intravisit::{
-    walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound,
+    walk_fn_decl, walk_generic_arg, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound,
     walk_poly_trait_ref, walk_trait_ref, walk_ty, Visitor,
 };
 use rustc_hir::lang_items;
@@ -481,7 +481,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
                 sub_visitor.visit_fn_decl(decl);
                 self.nested_elision_site_lts.append(&mut sub_visitor.all_lts());
             },
-            TyKind::TraitObject(bounds, ref lt, _) => {
+            TyKind::TraitObject(bounds, lt, _) => {
                 if !lt.is_elided() {
                     self.unelided_trait_object_lifetime = true;
                 }
@@ -497,14 +497,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
         if let GenericArg::Lifetime(l) = generic_arg && let LifetimeName::Param(def_id) = l.res {
             self.lifetime_generic_arg_spans.entry(def_id).or_insert(l.ident.span);
         }
-        // Replace with `walk_generic_arg` if/when https://github.com/rust-lang/rust/pull/103692 lands.
-        // walk_generic_arg(self, generic_arg);
-        match generic_arg {
-            GenericArg::Lifetime(lt) => self.visit_lifetime(lt),
-            GenericArg::Type(ty) => self.visit_ty(ty),
-            GenericArg::Const(ct) => self.visit_anon_const(&ct.value),
-            GenericArg::Infer(inf) => self.visit_infer(inf),
-        }
+        walk_generic_arg(self, generic_arg);
     }
 }
 
diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs
index 594f6af76b3..f5617a905ff 100644
--- a/clippy_lints/src/macro_use.rs
+++ b/clippy_lints/src/macro_use.rs
@@ -35,7 +35,8 @@ struct PathAndSpan {
     span: Span,
 }
 
-/// `MacroRefData` includes the name of the macro.
+/// `MacroRefData` includes the name of the macro
+/// and the path from `SourceMap::span_to_filename`.
 #[derive(Debug, Clone)]
 pub struct MacroRefData {
     name: String,
diff --git a/clippy_lints/src/manual_bits.rs b/clippy_lints/src/manual_bits.rs
index 6655c92b1da..462d73cf0b9 100644
--- a/clippy_lints/src/manual_bits.rs
+++ b/clippy_lints/src/manual_bits.rs
@@ -1,12 +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::{get_parent_expr, meets_msrv, msrvs};
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{self, Ty};
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::sym;
 
@@ -34,12 +34,12 @@ declare_clippy_lint! {
 
 #[derive(Clone)]
 pub struct ManualBits {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl ManualBits {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -48,7 +48,7 @@ impl_lint_pass!(ManualBits => [MANUAL_BITS]);
 
 impl<'tcx> LateLintPass<'tcx> for ManualBits {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if !meets_msrv(self.msrv, msrvs::MANUAL_BITS) {
+        if !self.msrv.meets(msrvs::MANUAL_BITS) {
             return;
         }
 
diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs
index 02dc8755dd6..bb6d628af3b 100644
--- a/clippy_lints/src/manual_clamp.rs
+++ b/clippy_lints/src/manual_clamp.rs
@@ -1,28 +1,25 @@
+use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
+use clippy_utils::higher::If;
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::implements_trait;
+use clippy_utils::visitors::is_const_evaluatable;
+use clippy_utils::MaybePath;
+use clippy_utils::{
+    eq_expr_value, is_diag_trait_item, is_trait_method, path_res, path_to_local_id, peel_blocks, peel_blocks_with_stmt,
+};
 use itertools::Itertools;
+use rustc_errors::Applicability;
 use rustc_errors::Diagnostic;
 use rustc_hir::{
     def::Res, Arm, BinOpKind, Block, Expr, ExprKind, Guard, HirId, PatKind, PathSegment, PrimTy, QPath, StmtKind,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::Ty;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{symbol::sym, Span};
 use std::ops::Deref;
 
-use clippy_utils::{
-    diagnostics::{span_lint_and_then, span_lint_hir_and_then},
-    eq_expr_value,
-    higher::If,
-    is_diag_trait_item, is_trait_method, meets_msrv, msrvs, path_res, path_to_local_id, peel_blocks,
-    peel_blocks_with_stmt,
-    sugg::Sugg,
-    ty::implements_trait,
-    visitors::is_const_evaluatable,
-    MaybePath,
-};
-use rustc_errors::Applicability;
-
 declare_clippy_lint! {
     /// ### What it does
     /// Identifies good opportunities for a clamp function from std or core, and suggests using it.
@@ -87,11 +84,11 @@ declare_clippy_lint! {
 impl_lint_pass!(ManualClamp => [MANUAL_CLAMP]);
 
 pub struct ManualClamp {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl ManualClamp {
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -114,7 +111,7 @@ struct InputMinMax<'tcx> {
 
 impl<'tcx> LateLintPass<'tcx> for ManualClamp {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-        if !meets_msrv(self.msrv, msrvs::CLAMP) {
+        if !self.msrv.meets(msrvs::CLAMP) {
             return;
         }
         if !expr.span.from_expansion() {
@@ -130,7 +127,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualClamp {
     }
 
     fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
-        if !meets_msrv(self.msrv, msrvs::CLAMP) {
+        if !self.msrv.meets(msrvs::CLAMP) {
             return;
         }
         for suggestion in is_two_if_pattern(cx, block) {
diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs
index bb8c142f8e4..5ab049d8d13 100644
--- a/clippy_lints/src/manual_is_ascii_check.rs
+++ b/clippy_lints/src/manual_is_ascii_check.rs
@@ -1,15 +1,12 @@
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::{diagnostics::span_lint_and_sugg, in_constant, macros::root_macro_call, source::snippet};
 use rustc_ast::LitKind::{Byte, Char};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{def_id::DefId, sym};
 
-use clippy_utils::{
-    diagnostics::span_lint_and_sugg, in_constant, macros::root_macro_call, meets_msrv, msrvs, source::snippet,
-};
-
 declare_clippy_lint! {
     /// ### What it does
     /// Suggests to use dedicated built-in methods,
@@ -45,12 +42,12 @@ declare_clippy_lint! {
 impl_lint_pass!(ManualIsAsciiCheck => [MANUAL_IS_ASCII_CHECK]);
 
 pub struct ManualIsAsciiCheck {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl ManualIsAsciiCheck {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -70,11 +67,11 @@ enum CharRange {
 
 impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if !meets_msrv(self.msrv, msrvs::IS_ASCII_DIGIT) {
+        if !self.msrv.meets(msrvs::IS_ASCII_DIGIT) {
             return;
         }
 
-        if in_constant(cx, expr.hir_id) && !meets_msrv(self.msrv, msrvs::IS_ASCII_DIGIT_CONST) {
+        if in_constant(cx, expr.hir_id) && !self.msrv.meets(msrvs::IS_ASCII_DIGIT_CONST) {
             return;
         }
 
diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs
index 1846596fa4c..874d36ca9f4 100644
--- a/clippy_lints/src/manual_let_else.rs
+++ b/clippy_lints/src/manual_let_else.rs
@@ -1,16 +1,16 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::higher::IfLetOrMatch;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::peel_blocks;
+use clippy_utils::source::snippet_with_context;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::visitors::{for_each_expr, Descend};
-use clippy_utils::{meets_msrv, msrvs, peel_blocks};
 use if_chain::if_chain;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::symbol::sym;
 use rustc_span::Span;
@@ -50,13 +50,13 @@ declare_clippy_lint! {
 }
 
 pub struct ManualLetElse {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
     matches_behaviour: MatchLintBehaviour,
 }
 
 impl ManualLetElse {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>, matches_behaviour: MatchLintBehaviour) -> Self {
+    pub fn new(msrv: Msrv, matches_behaviour: MatchLintBehaviour) -> Self {
         Self {
             msrv,
             matches_behaviour,
@@ -69,7 +69,7 @@ impl_lint_pass!(ManualLetElse => [MANUAL_LET_ELSE]);
 impl<'tcx> LateLintPass<'tcx> for ManualLetElse {
     fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &'tcx Stmt<'tcx>) {
         let if_let_or_match = if_chain! {
-            if meets_msrv(self.msrv, msrvs::LET_ELSE);
+            if self.msrv.meets(msrvs::LET_ELSE);
             if !in_external_macro(cx.sess(), stmt.span);
             if let StmtKind::Local(local) = stmt.kind;
             if let Some(init) = local.init;
@@ -141,20 +141,18 @@ fn emit_manual_let_else(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, pat:
             // * unused binding collision detection with existing ones
             // * putting patterns with at the top level | inside ()
             // for this to be machine applicable.
-            let app = Applicability::HasPlaceholders;
+            let mut app = Applicability::HasPlaceholders;
+            let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", &mut app);
+            let (sn_expr, _) = snippet_with_context(cx, expr.span, span.ctxt(), "", &mut app);
+            let (sn_else, _) = snippet_with_context(cx, else_body.span, span.ctxt(), "", &mut app);
 
-            if let Some(sn_pat) = snippet_opt(cx, pat.span) &&
-                let Some(sn_expr) = snippet_opt(cx, expr.span) &&
-                let Some(sn_else) = snippet_opt(cx, else_body.span)
-            {
-                let else_bl = if matches!(else_body.kind, ExprKind::Block(..)) {
-                    sn_else
-                } else {
-                    format!("{{ {sn_else} }}")
-                };
-                let sugg = format!("let {sn_pat} = {sn_expr} else {else_bl};");
-                diag.span_suggestion(span, "consider writing", sugg, app);
-            }
+            let else_bl = if matches!(else_body.kind, ExprKind::Block(..)) {
+                sn_else.into_owned()
+            } else {
+                format!("{{ {sn_else} }}")
+            };
+            let sugg = format!("let {sn_pat} = {sn_expr} else {else_bl};");
+            diag.span_suggestion(span, "consider writing", sugg, app);
         },
     );
 }
diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs
index 4877cee0cc1..bca193be9e7 100644
--- a/clippy_lints/src/manual_non_exhaustive.rs
+++ b/clippy_lints/src/manual_non_exhaustive.rs
@@ -1,6 +1,7 @@
 use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
+use clippy_utils::is_doc_hidden;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_opt;
-use clippy_utils::{is_doc_hidden, meets_msrv, msrvs};
 use rustc_ast::ast::{self, VisibilityKind};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
@@ -8,7 +9,6 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
 use rustc_hir::{self as hir, Expr, ExprKind, QPath};
 use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 use rustc_middle::ty::DefIdTree;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::def_id::{DefId, LocalDefId};
 use rustc_span::{sym, Span};
@@ -63,12 +63,12 @@ declare_clippy_lint! {
 
 #[expect(clippy::module_name_repetitions)]
 pub struct ManualNonExhaustiveStruct {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl ManualNonExhaustiveStruct {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -77,14 +77,14 @@ impl_lint_pass!(ManualNonExhaustiveStruct => [MANUAL_NON_EXHAUSTIVE]);
 
 #[expect(clippy::module_name_repetitions)]
 pub struct ManualNonExhaustiveEnum {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
     constructed_enum_variants: FxHashSet<(DefId, DefId)>,
     potential_enums: Vec<(LocalDefId, LocalDefId, Span, Span)>,
 }
 
 impl ManualNonExhaustiveEnum {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self {
             msrv,
             constructed_enum_variants: FxHashSet::default(),
@@ -97,7 +97,7 @@ impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]);
 
 impl EarlyLintPass for ManualNonExhaustiveStruct {
     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
-        if !meets_msrv(self.msrv, msrvs::NON_EXHAUSTIVE) {
+        if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) {
             return;
         }
 
@@ -149,7 +149,7 @@ impl EarlyLintPass for ManualNonExhaustiveStruct {
 
 impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
-        if !meets_msrv(self.msrv, msrvs::NON_EXHAUSTIVE) {
+        if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) {
             return;
         }
 
diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs
index 6f25a2ed8e4..8d447c37150 100644
--- a/clippy_lints/src/manual_rem_euclid.rs
+++ b/clippy_lints/src/manual_rem_euclid.rs
@@ -1,12 +1,12 @@
 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::{in_constant, meets_msrv, msrvs, path_to_local};
+use clippy_utils::{in_constant, path_to_local};
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 
 declare_clippy_lint! {
@@ -34,12 +34,12 @@ declare_clippy_lint! {
 }
 
 pub struct ManualRemEuclid {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl ManualRemEuclid {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -48,11 +48,11 @@ impl_lint_pass!(ManualRemEuclid => [MANUAL_REM_EUCLID]);
 
 impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if !meets_msrv(self.msrv, msrvs::REM_EUCLID) {
+        if !self.msrv.meets(msrvs::REM_EUCLID) {
             return;
         }
 
-        if in_constant(cx, expr.hir_id) && !meets_msrv(self.msrv, msrvs::REM_EUCLID_CONST) {
+        if in_constant(cx, expr.hir_id) && !self.msrv.meets(msrvs::REM_EUCLID_CONST) {
             return;
         }
 
diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs
index d6438ca7fec..c1e6c82487d 100644
--- a/clippy_lints/src/manual_retain.rs
+++ b/clippy_lints/src/manual_retain.rs
@@ -1,8 +1,8 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet;
 use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
 use clippy_utils::{get_parent_expr, match_def_path, paths, SpanlessEq};
-use clippy_utils::{meets_msrv, msrvs};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
@@ -50,12 +50,12 @@ declare_clippy_lint! {
 }
 
 pub struct ManualRetain {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl ManualRetain {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -71,9 +71,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualRetain {
             && let hir::ExprKind::MethodCall(_, target_expr, [], _) = &collect_expr.kind
             && let Some(collect_def_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id)
             && match_def_path(cx, collect_def_id, &paths::CORE_ITER_COLLECT) {
-            check_into_iter(cx, parent_expr, left_expr, target_expr, self.msrv);
-            check_iter(cx, parent_expr, left_expr, target_expr, self.msrv);
-            check_to_owned(cx, parent_expr, left_expr, target_expr, self.msrv);
+            check_into_iter(cx, parent_expr, left_expr, target_expr, &self.msrv);
+            check_iter(cx, parent_expr, left_expr, target_expr, &self.msrv);
+            check_to_owned(cx, parent_expr, left_expr, target_expr, &self.msrv);
         }
     }
 
@@ -85,7 +85,7 @@ fn check_into_iter(
     parent_expr: &hir::Expr<'_>,
     left_expr: &hir::Expr<'_>,
     target_expr: &hir::Expr<'_>,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) {
     if let hir::ExprKind::MethodCall(_, into_iter_expr, [_], _) = &target_expr.kind
         && let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id)
@@ -104,7 +104,7 @@ fn check_iter(
     parent_expr: &hir::Expr<'_>,
     left_expr: &hir::Expr<'_>,
     target_expr: &hir::Expr<'_>,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) {
     if let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind
         && let Some(copied_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id)
@@ -127,9 +127,9 @@ fn check_to_owned(
     parent_expr: &hir::Expr<'_>,
     left_expr: &hir::Expr<'_>,
     target_expr: &hir::Expr<'_>,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) {
-    if meets_msrv(msrv,  msrvs::STRING_RETAIN)
+    if msrv.meets(msrvs::STRING_RETAIN)
         && let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind
         && let Some(to_owned_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id)
         && match_def_path(cx, to_owned_def_id, &paths::TO_OWNED_METHOD)
@@ -215,10 +215,10 @@ fn match_acceptable_def_path(cx: &LateContext<'_>, collect_def_id: DefId) -> boo
         .any(|&method| match_def_path(cx, collect_def_id, method))
 }
 
-fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: Option<RustcVersion>) -> bool {
+fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: &Msrv) -> bool {
     let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs();
     ACCEPTABLE_TYPES.iter().any(|(ty, acceptable_msrv)| {
         is_type_diagnostic_item(cx, expr_ty, *ty)
-            && acceptable_msrv.map_or(true, |acceptable_msrv| meets_msrv(msrv, acceptable_msrv))
+            && acceptable_msrv.map_or(true, |acceptable_msrv| msrv.meets(acceptable_msrv))
     })
 }
diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs
index 0976940afac..de166b9765f 100644
--- a/clippy_lints/src/manual_strip.rs
+++ b/clippy_lints/src/manual_strip.rs
@@ -1,8 +1,9 @@
 use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet;
 use clippy_utils::usage::mutated_variables;
-use clippy_utils::{eq_expr_value, higher, match_def_path, meets_msrv, msrvs, paths};
+use clippy_utils::{eq_expr_value, higher, match_def_path, paths};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_hir::def::Res;
@@ -11,7 +12,6 @@ use rustc_hir::BinOpKind;
 use rustc_hir::{BorrowKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::Spanned;
 use rustc_span::Span;
@@ -48,12 +48,12 @@ declare_clippy_lint! {
 }
 
 pub struct ManualStrip {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl ManualStrip {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -68,7 +68,7 @@ enum StripKind {
 
 impl<'tcx> LateLintPass<'tcx> for ManualStrip {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) {
+        if !self.msrv.meets(msrvs::STR_STRIP_PREFIX) {
             return;
         }
 
diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs
index 7d8171ead89..7b15a307fec 100644
--- a/clippy_lints/src/matches/mod.rs
+++ b/clippy_lints/src/matches/mod.rs
@@ -23,13 +23,13 @@ mod single_match;
 mod try_err;
 mod wild_in_or_pats;
 
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::{snippet_opt, walk_span_to_context};
-use clippy_utils::{higher, in_constant, is_span_match, meets_msrv, msrvs};
+use clippy_utils::{higher, in_constant, is_span_match};
 use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat};
 use rustc_lexer::{tokenize, TokenKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{Span, SpanData, SyntaxContext};
 
@@ -930,13 +930,13 @@ declare_clippy_lint! {
 
 #[derive(Default)]
 pub struct Matches {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
     infallible_destructuring_match_linted: bool,
 }
 
 impl Matches {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self {
             msrv,
             ..Matches::default()
@@ -1000,9 +1000,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
 
             if !from_expansion && !contains_cfg_arm(cx, expr, ex, arms) {
                 if source == MatchSource::Normal {
-                    if !(meets_msrv(self.msrv, msrvs::MATCHES_MACRO)
-                        && match_like_matches::check_match(cx, expr, ex, arms))
-                    {
+                    if !(self.msrv.meets(msrvs::MATCHES_MACRO) && match_like_matches::check_match(cx, expr, ex, arms)) {
                         match_same_arms::check(cx, arms);
                     }
 
@@ -1034,7 +1032,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
             collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else);
             if !from_expansion {
                 if let Some(else_expr) = if_let.if_else {
-                    if meets_msrv(self.msrv, msrvs::MATCHES_MACRO) {
+                    if self.msrv.meets(msrvs::MATCHES_MACRO) {
                         match_like_matches::check_if_let(
                             cx,
                             expr,
diff --git a/clippy_lints/src/matches/try_err.rs b/clippy_lints/src/matches/try_err.rs
index c6cba81d871..704c34c32bf 100644
--- a/clippy_lints/src/matches/try_err.rs
+++ b/clippy_lints/src/matches/try_err.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{get_parent_expr, is_res_lang_ctor, match_def_path, path_res, paths};
+use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::ResultErr;
@@ -107,7 +107,7 @@ fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'t
 fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
     if_chain! {
         if let ty::Adt(def, subst) = ty.kind();
-        if match_def_path(cx, def.did(), &paths::POLL);
+        if cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did());
         let ready_ty = subst.type_at(0);
 
         if let ty::Adt(ready_def, ready_subst) = ready_ty.kind();
@@ -124,7 +124,7 @@ fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<
 fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
     if_chain! {
         if let ty::Adt(def, subst) = ty.kind();
-        if match_def_path(cx, def.did(), &paths::POLL);
+        if cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did());
         let ready_ty = subst.type_at(0);
 
         if let ty::Adt(ready_def, ready_subst) = ready_ty.kind();
diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs
index 0c4d9f100f7..35024ec1224 100644
--- a/clippy_lints/src/mem_replace.rs
+++ b/clippy_lints/src/mem_replace.rs
@@ -1,14 +1,14 @@
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::{snippet, snippet_with_applicability};
 use clippy_utils::ty::is_non_aggregate_primitive_type;
-use clippy_utils::{is_default_equivalent, is_res_lang_ctor, meets_msrv, msrvs, path_res};
+use clippy_utils::{is_default_equivalent, is_res_lang_ctor, path_res};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::OptionNone;
 use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::Span;
 use rustc_span::symbol::sym;
@@ -227,12 +227,12 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<
 }
 
 pub struct MemReplace {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl MemReplace {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -248,7 +248,7 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace {
             then {
                 check_replace_option_with_none(cx, src, dest, expr.span);
                 check_replace_with_uninit(cx, src, dest, expr.span);
-                if meets_msrv(self.msrv, msrvs::MEM_TAKE) {
+                if self.msrv.meets(msrvs::MEM_TAKE) {
                     check_replace_with_default(cx, src, dest, expr.span);
                 }
             }
diff --git a/clippy_lints/src/methods/cloned_instead_of_copied.rs b/clippy_lints/src/methods/cloned_instead_of_copied.rs
index e9aeab2d5b6..4e6ec61f6a8 100644
--- a/clippy_lints/src/methods/cloned_instead_of_copied.rs
+++ b/clippy_lints/src/methods/cloned_instead_of_copied.rs
@@ -1,25 +1,25 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_trait_method;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::ty::{get_iterator_item_ty, is_copy};
-use clippy_utils::{is_trait_method, meets_msrv, msrvs};
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_semver::RustcVersion;
 use rustc_span::{sym, Span};
 
 use super::CLONED_INSTEAD_OF_COPIED;
 
-pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: Option<RustcVersion>) {
+pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: &Msrv) {
     let recv_ty = cx.typeck_results().expr_ty_adjusted(recv);
     let inner_ty = match recv_ty.kind() {
         // `Option<T>` -> `T`
         ty::Adt(adt, subst)
-            if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && meets_msrv(msrv, msrvs::OPTION_COPIED) =>
+            if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && msrv.meets(msrvs::OPTION_COPIED) =>
         {
             subst.type_at(0)
         },
-        _ if is_trait_method(cx, expr, sym::Iterator) && meets_msrv(msrv, msrvs::ITERATOR_COPIED) => {
+        _ if is_trait_method(cx, expr, sym::Iterator) && msrv.meets(msrvs::ITERATOR_COPIED) => {
             match get_iterator_item_ty(cx, recv_ty) {
                 // <T as Iterator>::Item
                 Some(ty) => ty,
diff --git a/clippy_lints/src/methods/err_expect.rs b/clippy_lints/src/methods/err_expect.rs
index 720d9a68c85..ae03da0d3f9 100644
--- a/clippy_lints/src/methods/err_expect.rs
+++ b/clippy_lints/src/methods/err_expect.rs
@@ -1,27 +1,27 @@
 use super::ERR_EXPECT;
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::ty::has_debug_impl;
-use clippy_utils::{meets_msrv, msrvs, ty::is_type_diagnostic_item};
+use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_errors::Applicability;
 use rustc_lint::LateContext;
 use rustc_middle::ty;
 use rustc_middle::ty::Ty;
-use rustc_semver::RustcVersion;
 use rustc_span::{sym, Span};
 
 pub(super) fn check(
     cx: &LateContext<'_>,
     _expr: &rustc_hir::Expr<'_>,
     recv: &rustc_hir::Expr<'_>,
-    msrv: Option<RustcVersion>,
     expect_span: Span,
     err_span: Span,
+    msrv: &Msrv,
 ) {
     if_chain! {
         if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
         // Test the version to make sure the lint can be showed (expect_err has been
         // introduced in rust 1.17.0 : https://github.com/rust-lang/rust/pull/38982)
-        if meets_msrv(msrv, msrvs::EXPECT_ERR);
+        if msrv.meets(msrvs::EXPECT_ERR);
 
         // Grabs the `Result<T, E>` type
         let result_type = cx.typeck_results().expr_ty(recv);
diff --git a/clippy_lints/src/methods/filter_map_next.rs b/clippy_lints/src/methods/filter_map_next.rs
index ddf8a1f09b8..175e04f8ac0 100644
--- a/clippy_lints/src/methods/filter_map_next.rs
+++ b/clippy_lints/src/methods/filter_map_next.rs
@@ -1,10 +1,10 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::is_trait_method;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet;
-use clippy_utils::{is_trait_method, meets_msrv, msrvs};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
-use rustc_semver::RustcVersion;
 use rustc_span::sym;
 
 use super::FILTER_MAP_NEXT;
@@ -14,10 +14,10 @@ pub(super) fn check<'tcx>(
     expr: &'tcx hir::Expr<'_>,
     recv: &'tcx hir::Expr<'_>,
     arg: &'tcx hir::Expr<'_>,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) {
     if is_trait_method(cx, expr, sym::Iterator) {
-        if !meets_msrv(msrv, msrvs::ITERATOR_FIND_MAP) {
+        if !msrv.meets(msrvs::ITERATOR_FIND_MAP) {
             return;
         }
 
diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs
index d8c821bc9ee..5c620d02716 100644
--- a/clippy_lints/src/methods/inefficient_to_string.rs
+++ b/clippy_lints/src/methods/inefficient_to_string.rs
@@ -7,7 +7,7 @@ use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty};
-use rustc_span::symbol::{Symbol, sym};
+use rustc_span::symbol::{sym, Symbol};
 
 use super::INEFFICIENT_TO_STRING;
 
diff --git a/clippy_lints/src/methods/is_digit_ascii_radix.rs b/clippy_lints/src/methods/is_digit_ascii_radix.rs
index 304024e8066..301aff5ae6a 100644
--- a/clippy_lints/src/methods/is_digit_ascii_radix.rs
+++ b/clippy_lints/src/methods/is_digit_ascii_radix.rs
@@ -1,23 +1,22 @@
 //! Lint for `c.is_digit(10)`
 
 use super::IS_DIGIT_ASCII_RADIX;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::{
-    consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, meets_msrv, msrvs,
-    source::snippet_with_applicability,
+    consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, source::snippet_with_applicability,
 };
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
-use rustc_semver::RustcVersion;
 
 pub(super) fn check<'tcx>(
     cx: &LateContext<'tcx>,
     expr: &'tcx Expr<'_>,
     self_arg: &'tcx Expr<'_>,
     radix: &'tcx Expr<'_>,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) {
-    if !meets_msrv(msrv, msrvs::IS_ASCII_DIGIT) {
+    if !msrv.meets(msrvs::IS_ASCII_DIGIT) {
         return;
     }
 
diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs
index 6bc783c6d50..52cc1e0464b 100644
--- a/clippy_lints/src/methods/map_clone.rs
+++ b/clippy_lints/src/methods/map_clone.rs
@@ -1,7 +1,8 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
-use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs, peel_blocks};
+use clippy_utils::{is_diag_trait_item, peel_blocks};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -9,19 +10,12 @@ use rustc_lint::LateContext;
 use rustc_middle::mir::Mutability;
 use rustc_middle::ty;
 use rustc_middle::ty::adjustment::Adjust;
-use rustc_semver::RustcVersion;
 use rustc_span::symbol::Ident;
 use rustc_span::{sym, Span};
 
 use super::MAP_CLONE;
 
-pub(super) fn check(
-    cx: &LateContext<'_>,
-    e: &hir::Expr<'_>,
-    recv: &hir::Expr<'_>,
-    arg: &hir::Expr<'_>,
-    msrv: Option<RustcVersion>,
-) {
+pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>, msrv: &Msrv) {
     if_chain! {
         if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id);
         if cx.tcx.impl_of_method(method_id)
@@ -97,10 +91,10 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
     );
 }
 
-fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: Option<RustcVersion>) {
+fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: &Msrv) {
     let mut applicability = Applicability::MachineApplicable;
 
-    let (message, sugg_method) = if is_copy && meets_msrv(msrv, msrvs::ITERATOR_COPIED) {
+    let (message, sugg_method) = if is_copy && msrv.meets(msrvs::ITERATOR_COPIED) {
         ("you are using an explicit closure for copying elements", "copied")
     } else {
         ("you are using an explicit closure for cloning elements", "cloned")
diff --git a/clippy_lints/src/methods/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs
index 74fdead216b..3122f72ee91 100644
--- a/clippy_lints/src/methods/map_unwrap_or.rs
+++ b/clippy_lints/src/methods/map_unwrap_or.rs
@@ -1,12 +1,11 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::usage::mutated_variables;
-use clippy_utils::{meets_msrv, msrvs};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
-use rustc_semver::RustcVersion;
 use rustc_span::symbol::sym;
 
 use super::MAP_UNWRAP_OR;
@@ -19,13 +18,13 @@ pub(super) fn check<'tcx>(
     recv: &'tcx hir::Expr<'_>,
     map_arg: &'tcx hir::Expr<'_>,
     unwrap_arg: &'tcx hir::Expr<'_>,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) -> bool {
     // lint if the caller of `map()` is an `Option`
     let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option);
     let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
 
-    if is_result && !meets_msrv(msrv, msrvs::RESULT_MAP_OR_ELSE) {
+    if is_result && !msrv.meets(msrvs::RESULT_MAP_OR_ELSE) {
         return false;
     }
 
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index 38165ab4fb2..d2913680cbb 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -104,8 +104,9 @@ mod zst_offset;
 use bind_instead_of_map::BindInsteadOfMap;
 use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item};
-use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, meets_msrv, msrvs, return_ty};
+use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, return_ty};
 use if_chain::if_chain;
 use rustc_hir as hir;
 use rustc_hir::{Expr, ExprKind, TraitItem, TraitItemKind};
@@ -113,7 +114,6 @@ use rustc_hir_analysis::hir_ty_to_ty;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, TraitRef, Ty};
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{sym, Span};
 
@@ -3163,7 +3163,7 @@ declare_clippy_lint! {
 
 pub struct Methods {
     avoid_breaking_exported_api: bool,
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
     allow_expect_in_tests: bool,
     allow_unwrap_in_tests: bool,
 }
@@ -3172,7 +3172,7 @@ impl Methods {
     #[must_use]
     pub fn new(
         avoid_breaking_exported_api: bool,
-        msrv: Option<RustcVersion>,
+        msrv: Msrv,
         allow_expect_in_tests: bool,
         allow_unwrap_in_tests: bool,
     ) -> Self {
@@ -3325,7 +3325,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
                 single_char_add_str::check(cx, expr, receiver, args);
                 into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, receiver);
                 single_char_pattern::check(cx, expr, method_call.ident.name, receiver, args);
-                unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, self.msrv);
+                unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, &self.msrv);
             },
             hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
                 let mut info = BinaryExprInfo {
@@ -3501,7 +3501,7 @@ impl Methods {
                 ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
                 ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
                 ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
-                ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv),
+                ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, &self.msrv),
                 ("collect", []) if is_trait_method(cx, expr, sym::Iterator) => {
                     needless_collect::check(cx, span, expr, recv, call_span);
                     match method_call(recv) {
@@ -3512,7 +3512,7 @@ impl Methods {
                             map_collect_result_unit::check(cx, expr, m_recv, m_arg);
                         },
                         Some(("take", take_self_arg, [take_arg], _, _)) => {
-                            if meets_msrv(self.msrv, msrvs::STR_REPEAT) {
+                            if self.msrv.meets(msrvs::STR_REPEAT) {
                                 manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg);
                             }
                         },
@@ -3539,7 +3539,7 @@ impl Methods {
                 },
                 ("expect", [_]) => match method_call(recv) {
                     Some(("ok", recv, [], _, _)) => ok_expect::check(cx, expr, recv),
-                    Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span),
+                    Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, span, err_span, &self.msrv),
                     _ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests),
                 },
                 ("expect_err", [_]) => expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests),
@@ -3578,7 +3578,7 @@ impl Methods {
                     unit_hash::check(cx, expr, recv, arg);
                 },
                 ("is_file", []) => filetype_is_file::check(cx, expr, recv),
-                ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv),
+                ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, &self.msrv),
                 ("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
                 ("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
                 ("iter" | "iter_mut" | "into_iter", []) => {
@@ -3601,7 +3601,7 @@ impl Methods {
                 },
                 (name @ ("map" | "map_err"), [m_arg]) => {
                     if name == "map" {
-                        map_clone::check(cx, expr, recv, m_arg, self.msrv);
+                        map_clone::check(cx, expr, recv, m_arg, &self.msrv);
                         if let Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) = method_call(recv) {
                             iter_kv_map::check(cx, map_name, expr, recv2, m_arg);
                         }
@@ -3610,8 +3610,8 @@ impl Methods {
                     }
                     if let Some((name, recv2, args, span2,_)) = method_call(recv) {
                         match (name, args) {
-                            ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv),
-                            ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, self.msrv),
+                            ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, &self.msrv),
+                            ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, &self.msrv),
                             ("filter", [f_arg]) => {
                                 filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false);
                             },
@@ -3632,7 +3632,7 @@ impl Methods {
                         match (name2, args2) {
                             ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false),
                             ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg),
-                            ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, self.msrv),
+                            ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, &self.msrv),
                             ("iter", []) => iter_next_slice::check(cx, expr, recv2),
                             ("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg),
                             ("skip_while", [_]) => skip_while_next::check(cx, expr),
@@ -3680,10 +3680,10 @@ impl Methods {
                     vec_resize_to_zero::check(cx, expr, count_arg, default_arg, span);
                 },
                 ("seek", [arg]) => {
-                    if meets_msrv(self.msrv, msrvs::SEEK_FROM_CURRENT) {
+                    if self.msrv.meets(msrvs::SEEK_FROM_CURRENT) {
                         seek_from_current::check(cx, expr, recv, arg);
                     }
-                    if meets_msrv(self.msrv, msrvs::SEEK_REWIND) {
+                    if self.msrv.meets(msrvs::SEEK_REWIND) {
                         seek_to_start_instead_of_rewind::check(cx, expr, recv, arg, span);
                     }
                 },
@@ -3699,7 +3699,7 @@ impl Methods {
                 ("splitn" | "rsplitn", [count_arg, pat_arg]) => {
                     if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
                         suspicious_splitn::check(cx, name, expr, recv, count);
-                        str_splitn::check(cx, name, expr, recv, pat_arg, count, self.msrv);
+                        str_splitn::check(cx, name, expr, recv, pat_arg, count, &self.msrv);
                     }
                 },
                 ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => {
@@ -3717,7 +3717,7 @@ impl Methods {
                 },
                 ("take", []) => needless_option_take::check(cx, expr, recv),
                 ("then", [arg]) => {
-                    if !meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) {
+                    if !self.msrv.meets(msrvs::BOOL_THEN_SOME) {
                         return;
                     }
                     unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some");
@@ -3760,7 +3760,7 @@ impl Methods {
                 },
                 ("unwrap_or_else", [u_arg]) => match method_call(recv) {
                     Some(("map", recv, [map_arg], _, _))
-                        if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {},
+                        if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, &self.msrv) => {},
                     _ => {
                         unwrap_or_else_default::check(cx, expr, recv, u_arg);
                         unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs
index e6eb64bcbde..3e33f919337 100644
--- a/clippy_lints/src/methods/option_as_ref_deref.rs
+++ b/clippy_lints/src/methods/option_as_ref_deref.rs
@@ -1,13 +1,13 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{match_def_path, meets_msrv, msrvs, path_to_local_id, paths, peel_blocks};
+use clippy_utils::{match_def_path, path_to_local_id, paths, peel_blocks};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_semver::RustcVersion;
 use rustc_span::sym;
 
 use super::OPTION_AS_REF_DEREF;
@@ -19,9 +19,9 @@ pub(super) fn check(
     as_ref_recv: &hir::Expr<'_>,
     map_arg: &hir::Expr<'_>,
     is_mut: bool,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) {
-    if !meets_msrv(msrv, msrvs::OPTION_AS_DEREF) {
+    if !msrv.meets(msrvs::OPTION_AS_DEREF) {
         return;
     }
 
diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs
index 1acac59144c..3c01ce1fecd 100644
--- a/clippy_lints/src/methods/str_splitn.rs
+++ b/clippy_lints/src/methods/str_splitn.rs
@@ -1,9 +1,10 @@
 use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::usage::local_used_after_expr;
 use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
-use clippy_utils::{is_diag_item_method, match_def_path, meets_msrv, msrvs, path_to_local_id, paths};
+use clippy_utils::{is_diag_item_method, match_def_path, path_to_local_id, paths};
 use core::ops::ControlFlow;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
@@ -12,7 +13,6 @@ use rustc_hir::{
 };
 use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_semver::RustcVersion;
 use rustc_span::{sym, Span, Symbol, SyntaxContext};
 
 use super::{MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN};
@@ -24,7 +24,7 @@ pub(super) fn check(
     self_arg: &Expr<'_>,
     pat_arg: &Expr<'_>,
     count: u128,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) {
     if count < 2 || !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
         return;
@@ -34,7 +34,7 @@ pub(super) fn check(
         IterUsageKind::Nth(n) => count > n + 1,
         IterUsageKind::NextTuple => count > 2,
     };
-    let manual = count == 2 && meets_msrv(msrv, msrvs::STR_SPLIT_ONCE);
+    let manual = count == 2 && msrv.meets(msrvs::STR_SPLIT_ONCE);
 
     match parse_iter_usage(cx, expr.span.ctxt(), cx.tcx.hir().parent_iter(expr.hir_id)) {
         Some(usage) if needless(usage.kind) => lint_needless(cx, method_name, expr, self_arg, pat_arg),
diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs
index 7ff13b95626..17b0507682a 100644
--- a/clippy_lints/src/methods/unnecessary_to_owned.rs
+++ b/clippy_lints/src/methods/unnecessary_to_owned.rs
@@ -1,13 +1,11 @@
 use super::implicit_clone::is_clone_like;
 use super::unnecessary_iter_cloned::{self, is_into_iter};
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs};
 use clippy_utils::visitors::find_all_ret_expressions;
-use clippy_utils::{
-    fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty,
-};
-use clippy_utils::{meets_msrv, msrvs};
+use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty};
 use rustc_errors::Applicability;
 use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, Node};
 use rustc_hir_typeck::{FnCtxt, Inherited};
@@ -16,14 +14,9 @@ use rustc_lint::LateContext;
 use rustc_middle::mir::Mutability;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
-use rustc_middle::ty::EarlyBinder;
-use rustc_middle::ty::{self, Clause, ParamTy, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
-use rustc_semver::RustcVersion;
+use rustc_middle::ty::{self, Clause, EarlyBinder, ParamTy, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
 use rustc_span::{sym, Symbol};
-use rustc_trait_selection::traits::{
-    query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause,
-};
-use std::cmp::max;
+use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause};
 
 use super::UNNECESSARY_TO_OWNED;
 
@@ -33,7 +26,7 @@ pub fn check<'tcx>(
     method_name: Symbol,
     receiver: &'tcx Expr<'_>,
     args: &'tcx [Expr<'_>],
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) {
     if_chain! {
         if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
@@ -204,7 +197,7 @@ fn check_into_iter_call_arg(
     expr: &Expr<'_>,
     method_name: Symbol,
     receiver: &Expr<'_>,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) -> bool {
     if_chain! {
         if let Some(parent) = get_parent_expr(cx, expr);
@@ -219,7 +212,7 @@ fn check_into_iter_call_arg(
             if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) {
                 return true;
             }
-            let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, msrvs::ITERATOR_COPIED) {
+            let cloned_or_copied = if is_copy(cx, item_ty) && msrv.meets(msrvs::ITERATOR_COPIED) {
                 "copied"
             } else {
                 "cloned"
@@ -267,11 +260,22 @@ fn check_other_call_arg<'tcx>(
         if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef);
         if trait_predicate.def_id() == deref_trait_id || trait_predicate.def_id() == as_ref_trait_id;
         let receiver_ty = cx.typeck_results().expr_ty(receiver);
-        if can_change_type(cx, maybe_arg, receiver_ty);
         // We can't add an `&` when the trait is `Deref` because `Target = &T` won't match
         // `Target = T`.
-        if n_refs > 0 || is_copy(cx, receiver_ty) || trait_predicate.def_id() != deref_trait_id;
-        let n_refs = max(n_refs, usize::from(!is_copy(cx, receiver_ty)));
+        if let Some((n_refs, receiver_ty)) = if n_refs > 0 || is_copy(cx, receiver_ty) {
+            Some((n_refs, receiver_ty))
+        } else if trait_predicate.def_id() != deref_trait_id {
+            Some((1, cx.tcx.mk_ref(
+                cx.tcx.lifetimes.re_erased,
+                ty::TypeAndMut {
+                    ty: receiver_ty,
+                    mutbl: Mutability::Not,
+                },
+            )))
+        } else {
+            None
+        };
+        if can_change_type(cx, maybe_arg, receiver_ty);
         if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
         then {
             span_lint_and_sugg(
@@ -345,13 +349,13 @@ fn get_input_traits_and_projections<'tcx>(
                 if trait_predicate.trait_ref.self_ty() == input {
                     trait_predicates.push(trait_predicate);
                 }
-            }
+            },
             PredicateKind::Clause(Clause::Projection(projection_predicate)) => {
                 if projection_predicate.projection_ty.self_ty() == input {
                     projection_predicates.push(projection_predicate);
                 }
-            }
-            _ => {}
+            },
+            _ => {},
         }
     }
     (trait_predicates, projection_predicates)
@@ -403,10 +407,12 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
 
                         let mut trait_predicates = cx.tcx.param_env(callee_def_id)
                             .caller_bounds().iter().filter(|predicate| {
-                            if let PredicateKind::Clause(Clause::Trait(trait_predicate)) =  predicate.kind().skip_binder()
-                                && trait_predicate.trait_ref.self_ty() == *param_ty {
-                                    true
-                                } else {
+                            if let PredicateKind::Clause(Clause::Trait(trait_predicate))
+                                    = predicate.kind().skip_binder()
+                                && trait_predicate.trait_ref.self_ty() == *param_ty
+                            {
+                                true
+                            } else {
                                 false
                             }
                         });
@@ -466,12 +472,7 @@ fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id:
 
 /// Returns true if the named method can be used to convert the receiver to its "owned"
 /// representation.
-fn is_to_owned_like<'a>(
-    cx: &LateContext<'a>,
-    call_expr: &Expr<'a>,
-    method_name: Symbol,
-    method_def_id: DefId,
-) -> bool {
+fn is_to_owned_like<'a>(cx: &LateContext<'a>, call_expr: &Expr<'a>, method_name: Symbol, method_def_id: DefId) -> bool {
     is_clone_like(cx, method_name.as_str(), method_def_id)
         || is_cow_into_owned(cx, method_name, method_def_id)
         || is_to_string_on_string_like(cx, call_expr, method_name, method_def_id)
diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs
index 71cc0d0a81c..5bc04bc17fb 100644
--- a/clippy_lints/src/missing_const_for_fn.rs
+++ b/clippy_lints/src/missing_const_for_fn.rs
@@ -1,9 +1,8 @@
 use clippy_utils::diagnostics::span_lint;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::qualify_min_const_fn::is_min_const_fn;
 use clippy_utils::ty::has_drop;
-use clippy_utils::{
-    fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, meets_msrv, msrvs, trait_ref_of_method,
-};
+use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, trait_ref_of_method};
 use rustc_hir as hir;
 use rustc_hir::def_id::CRATE_DEF_ID;
 use rustc_hir::intravisit::FnKind;
@@ -11,7 +10,6 @@ use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId};
 use rustc_hir_analysis::hir_ty_to_ty;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::Span;
 
@@ -75,12 +73,12 @@ declare_clippy_lint! {
 impl_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]);
 
 pub struct MissingConstForFn {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl MissingConstForFn {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -95,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
         span: Span,
         hir_id: HirId,
     ) {
-        if !meets_msrv(self.msrv, msrvs::CONST_IF_MATCH) {
+        if !self.msrv.meets(msrvs::CONST_IF_MATCH) {
             return;
         }
 
@@ -152,7 +150,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
 
         let mir = cx.tcx.optimized_mir(def_id);
 
-        if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv) {
+        if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, &self.msrv) {
             if cx.tcx.is_const_fn_raw(def_id.to_def_id()) {
                 cx.tcx.sess.span_err(span, err.as_ref());
             }
diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs
index 6fd100762b4..9942e8115b7 100644
--- a/clippy_lints/src/missing_doc.rs
+++ b/clippy_lints/src/missing_doc.rs
@@ -8,6 +8,7 @@
 use clippy_utils::attrs::is_doc_hidden;
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::is_from_proc_macro;
+use if_chain::if_chain;
 use rustc_ast::ast::{self, MetaItem, MetaItemKind};
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs
index d8647a99105..3ef0c663459 100644
--- a/clippy_lints/src/mutable_debug_assertion.rs
+++ b/clippy_lints/src/mutable_debug_assertion.rs
@@ -92,10 +92,6 @@ impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> {
                 self.found = true;
                 return;
             },
-            ExprKind::If(..) => {
-                self.found = true;
-                return;
-            },
             ExprKind::Path(_) => {
                 if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
                     if adj
diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs
index 75e12715458..2f0b7ce16e5 100644
--- a/clippy_lints/src/needless_pass_by_value.rs
+++ b/clippy_lints/src/needless_pass_by_value.rs
@@ -1,7 +1,9 @@
 use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
 use clippy_utils::ptr::get_spans;
 use clippy_utils::source::{snippet, snippet_opt};
-use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy, is_type_diagnostic_item, is_type_lang_item};
+use clippy_utils::ty::{
+    implements_trait, implements_trait_with_env, is_copy, is_type_diagnostic_item, is_type_lang_item,
+};
 use clippy_utils::{get_trait_def_id, is_self, paths};
 use if_chain::if_chain;
 use rustc_ast::ast::Attribute;
@@ -124,7 +126,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
             .filter_map(|obligation| {
                 // Note that we do not want to deal with qualified predicates here.
                 match obligation.predicate.kind().no_bound_vars() {
-                    Some(ty::PredicateKind::Clause(ty::Clause::Trait(pred))) if pred.def_id() != sized_trait => Some(pred),
+                    Some(ty::PredicateKind::Clause(ty::Clause::Trait(pred))) if pred.def_id() != sized_trait => {
+                        Some(pred)
+                    },
                     _ => None,
                 }
             })
diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs
index 819646bb678..79c1ae4861e 100644
--- a/clippy_lints/src/no_effect.rs
+++ b/clippy_lints/src/no_effect.rs
@@ -6,7 +6,8 @@ use clippy_utils::ty::has_drop;
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, PatKind, Stmt, StmtKind, UnsafeSource};
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use std::ops::Deref;
 
@@ -159,8 +160,11 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
     if_chain! {
         if let StmtKind::Semi(expr) = stmt.kind;
+        let ctxt = stmt.span.ctxt();
+        if expr.span.ctxt() == ctxt;
         if let Some(reduced) = reduce_expression(cx, expr);
-        if !&reduced.iter().any(|e| e.span.from_expansion());
+        if !in_external_macro(cx.sess(), stmt.span);
+        if reduced.iter().all(|e| e.span.ctxt() == ctxt);
         then {
             if let ExprKind::Index(..) = &expr.kind {
                 let snippet = if let (Some(arr), Some(func)) =
diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs
index 92920bbad6e..e395ff54cb1 100644
--- a/clippy_lints/src/ptr.rs
+++ b/clippy_lints/src/ptr.rs
@@ -490,7 +490,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
                         ty_name: name.ident.name,
                         method_renames,
                         ref_prefix: RefPrefix {
-                            lt: lt.clone(),
+                            lt: *lt,
                             mutability,
                         },
                         deref_ty,
@@ -693,9 +693,10 @@ fn matches_preds<'tcx>(
             cx.tcx,
             ObligationCause::dummy(),
             cx.param_env,
-            cx.tcx.mk_predicate(Binder::dummy(
-                PredicateKind::Clause(Clause::Projection(p.with_self_ty(cx.tcx, ty))),
-            )),
+            cx.tcx
+                .mk_predicate(Binder::dummy(PredicateKind::Clause(Clause::Projection(
+                    p.with_self_ty(cx.tcx, ty),
+                )))),
         )),
         ExistentialPredicate::AutoTrait(p) => infcx
             .type_implements_trait(p, [ty], cx.param_env)
diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs
index c6fbb5e805a..0a1b9d173cf 100644
--- a/clippy_lints/src/ranges.rs
+++ b/clippy_lints/src/ranges.rs
@@ -1,16 +1,16 @@
 use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::higher;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, path_to_local};
+use clippy_utils::{get_parent_expr, in_constant, is_integer_const, path_to_local};
 use if_chain::if_chain;
 use rustc_ast::ast::RangeLimits;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, HirId};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::{Span, Spanned};
 use std::cmp::Ordering;
@@ -161,12 +161,12 @@ declare_clippy_lint! {
 }
 
 pub struct Ranges {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl Ranges {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -181,7 +181,7 @@ impl_lint_pass!(Ranges => [
 impl<'tcx> LateLintPass<'tcx> for Ranges {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if let ExprKind::Binary(ref op, l, r) = expr.kind {
-            if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) {
+            if self.msrv.meets(msrvs::RANGE_CONTAINS) {
                 check_possible_range_contains(cx, op.node, l, r, expr, expr.span);
             }
         }
diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs
index 8e675d34a18..2a42e73488f 100644
--- a/clippy_lints/src/redundant_closure_call.rs
+++ b/clippy_lints/src/redundant_closure_call.rs
@@ -81,8 +81,8 @@ impl EarlyLintPass for RedundantClosureCall {
                         "try not to call a closure in the expression where it is declared",
                         |diag| {
                             if fn_decl.inputs.is_empty() {
-                                let app = Applicability::MachineApplicable;
-                                let mut hint = Sugg::ast(cx, body, "..");
+                                let mut app = Applicability::MachineApplicable;
+                                let mut hint = Sugg::ast(cx, body, "..", closure.span.ctxt(), &mut app);
 
                                 if asyncness.is_async() {
                                     // `async x` is a syntax error, so it becomes `async { x }`
diff --git a/clippy_lints/src/redundant_field_names.rs b/clippy_lints/src/redundant_field_names.rs
index 40b03068f6c..61bff4a0e38 100644
--- a/clippy_lints/src/redundant_field_names.rs
+++ b/clippy_lints/src/redundant_field_names.rs
@@ -1,10 +1,9 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::{meets_msrv, msrvs};
+use clippy_utils::msrvs::{self, Msrv};
 use rustc_ast::ast::{Expr, ExprKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 
 declare_clippy_lint! {
@@ -37,12 +36,12 @@ declare_clippy_lint! {
 }
 
 pub struct RedundantFieldNames {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl RedundantFieldNames {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -51,7 +50,7 @@ impl_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]);
 
 impl EarlyLintPass for RedundantFieldNames {
     fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
-        if !meets_msrv(self.msrv, msrvs::FIELD_INIT_SHORTHAND) {
+        if !self.msrv.meets(msrvs::FIELD_INIT_SHORTHAND) {
             return;
         }
 
diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs
index 60ba62c4a43..3aa2490bc44 100644
--- a/clippy_lints/src/redundant_static_lifetimes.rs
+++ b/clippy_lints/src/redundant_static_lifetimes.rs
@@ -1,10 +1,9 @@
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet;
-use clippy_utils::{meets_msrv, msrvs};
 use rustc_ast::ast::{Item, ItemKind, Ty, TyKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 
 declare_clippy_lint! {
@@ -34,12 +33,12 @@ declare_clippy_lint! {
 }
 
 pub struct RedundantStaticLifetimes {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl RedundantStaticLifetimes {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -96,7 +95,7 @@ impl RedundantStaticLifetimes {
 
 impl EarlyLintPass for RedundantStaticLifetimes {
     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
-        if !meets_msrv(self.msrv, msrvs::STATIC_IN_CONST) {
+        if !self.msrv.meets(msrvs::STATIC_IN_CONST) {
             return;
         }
 
diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs
index 2b2a41d1601..81143d7799e 100644
--- a/clippy_lints/src/returns.rs
+++ b/clippy_lints/src/returns.rs
@@ -12,6 +12,7 @@ use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::subst::GenericArgKind;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Span;
+use rustc_span::{BytePos, Pos};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -209,13 +210,14 @@ fn check_final_expr<'tcx>(
             if cx.tcx.hir().attrs(expr.hir_id).is_empty() {
                 let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
                 if !borrows {
-                    emit_return_lint(
-                        cx,
-                        peeled_drop_expr.span,
-                        semi_spans,
-                        inner.as_ref().map(|i| i.span),
-                        replacement,
-                    );
+                    // check if expr return nothing
+                    let ret_span = if inner.is_none() && replacement == RetReplacement::Empty {
+                        extend_span_to_previous_non_ws(cx, peeled_drop_expr.span)
+                    } else {
+                        peeled_drop_expr.span
+                    };
+
+                    emit_return_lint(cx, ret_span, semi_spans, inner.as_ref().map(|i| i.span), replacement);
                 }
             }
         },
@@ -289,3 +291,16 @@ fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>)
     })
     .is_some()
 }
+
+// Go backwards while encountering whitespace and extend the given Span to that point.
+fn extend_span_to_previous_non_ws(cx: &LateContext<'_>, sp: Span) -> Span {
+    if let Ok(prev_source) = cx.sess().source_map().span_to_prev_source(sp) {
+        let ws = [' ', '\t', '\n'];
+        if let Some(non_ws_pos) = prev_source.rfind(|c| !ws.contains(&c)) {
+            let len = prev_source.len() - non_ws_pos - 1;
+            return sp.with_lo(sp.lo() - BytePos::from_usize(len));
+        }
+    }
+
+    sp
+}
diff --git a/clippy_lints/src/same_name_method.rs b/clippy_lints/src/same_name_method.rs
index caab5851baf..91326558cd8 100644
--- a/clippy_lints/src/same_name_method.rs
+++ b/clippy_lints/src/same_name_method.rs
@@ -55,11 +55,11 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
             if matches!(cx.tcx.def_kind(id.owner_id), DefKind::Impl)
                 && let item = cx.tcx.hir().item(id)
                 && let ItemKind::Impl(Impl {
-                  items,
-                  of_trait,
-                  self_ty,
-                  ..
-                                      }) = &item.kind
+                    items,
+                    of_trait,
+                    self_ty,
+                    ..
+                }) = &item.kind
                 && let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind
             {
                 if !map.contains_key(res) {
diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs
index 424a6e9264e..83e651aba8e 100644
--- a/clippy_lints/src/transmute/mod.rs
+++ b/clippy_lints/src/transmute/mod.rs
@@ -16,10 +16,10 @@ mod utils;
 mod wrong_transmute;
 
 use clippy_utils::in_constant;
+use clippy_utils::msrvs::Msrv;
 use if_chain::if_chain;
 use rustc_hir::{Expr, ExprKind, QPath};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::symbol::sym;
 
@@ -410,7 +410,7 @@ declare_clippy_lint! {
 }
 
 pub struct Transmute {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 impl_lint_pass!(Transmute => [
     CROSSPOINTER_TRANSMUTE,
@@ -431,7 +431,7 @@ impl_lint_pass!(Transmute => [
 ]);
 impl Transmute {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -461,7 +461,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
                 let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
                     | crosspointer_transmute::check(cx, e, from_ty, to_ty)
                     | transmuting_null::check(cx, e, arg, to_ty)
-                    | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv)
+                    | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, &self.msrv)
                     | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context)
                     | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
                     | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg)
diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs
index 12d0b866e1c..3dde4eee671 100644
--- a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs
+++ b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs
@@ -1,12 +1,12 @@
 use super::TRANSMUTE_PTR_TO_REF;
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{meets_msrv, msrvs, sugg};
+use clippy_utils::sugg;
 use rustc_errors::Applicability;
 use rustc_hir::{self as hir, Expr, GenericArg, Mutability, Path, TyKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty, TypeVisitable};
-use rustc_semver::RustcVersion;
 
 /// Checks for `transmute_ptr_to_ref` lint.
 /// Returns `true` if it's triggered, otherwise returns `false`.
@@ -17,7 +17,7 @@ pub(super) fn check<'tcx>(
     to_ty: Ty<'tcx>,
     arg: &'tcx Expr<'_>,
     path: &'tcx Path<'_>,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) -> bool {
     match (&from_ty.kind(), &to_ty.kind()) {
         (ty::RawPtr(from_ptr_ty), ty::Ref(_, to_ref_ty, mutbl)) => {
@@ -37,7 +37,7 @@ pub(super) fn check<'tcx>(
 
                     let sugg = if let Some(ty) = get_explicit_type(path) {
                         let ty_snip = snippet_with_applicability(cx, ty.span, "..", &mut app);
-                        if meets_msrv(msrv, msrvs::POINTER_CAST) {
+                        if msrv.meets(msrvs::POINTER_CAST) {
                             format!("{deref}{}.cast::<{ty_snip}>()", arg.maybe_par())
                         } else if from_ptr_ty.has_erased_regions() {
                             sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {ty_snip}"))).to_string()
@@ -46,7 +46,7 @@ pub(super) fn check<'tcx>(
                         }
                     } else if from_ptr_ty.ty == *to_ref_ty {
                         if from_ptr_ty.has_erased_regions() {
-                            if meets_msrv(msrv, msrvs::POINTER_CAST) {
+                            if msrv.meets(msrvs::POINTER_CAST) {
                                 format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_par())
                             } else {
                                 sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {to_ref_ty}")))
diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs
index e8f15a44473..2e1b6d8d4ea 100644
--- a/clippy_lints/src/undocumented_unsafe_blocks.rs
+++ b/clippy_lints/src/undocumented_unsafe_blocks.rs
@@ -1,6 +1,10 @@
+use std::ops::ControlFlow;
+
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::source::walk_span_to_context;
+use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
 use clippy_utils::{get_parent_node, is_lint_allowed};
+use hir::HirId;
 use rustc_data_structures::sync::Lrc;
 use rustc_hir as hir;
 use rustc_hir::{Block, BlockCheckMode, ItemKind, Node, UnsafeSource};
@@ -59,11 +63,39 @@ declare_clippy_lint! {
     restriction,
     "creating an unsafe block without explaining why it is safe"
 }
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for `// SAFETY: ` comments on safe code.
+    ///
+    /// ### Why is this bad?
+    /// Safe code has no safety requirements, so there is no need to
+    /// describe safety invariants.
+    ///
+    /// ### Example
+    /// ```rust
+    /// use std::ptr::NonNull;
+    /// let a = &mut 42;
+    ///
+    /// // SAFETY: references are guaranteed to be non-null.
+    /// let ptr = NonNull::new(a).unwrap();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// use std::ptr::NonNull;
+    /// let a = &mut 42;
+    ///
+    /// let ptr = NonNull::new(a).unwrap();
+    /// ```
+    #[clippy::version = "1.67.0"]
+    pub UNNECESSARY_SAFETY_COMMENT,
+    restriction,
+    "annotating safe code with a safety comment"
+}
 
-declare_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS]);
+declare_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS, UNNECESSARY_SAFETY_COMMENT]);
 
-impl LateLintPass<'_> for UndocumentedUnsafeBlocks {
-    fn check_block(&mut self, cx: &LateContext<'_>, block: &'_ Block<'_>) {
+impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
+    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
         if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
             && !in_external_macro(cx.tcx.sess, block.span)
             && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id)
@@ -87,35 +119,175 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks {
                 "consider adding a safety comment on the preceding line",
             );
         }
+
+        if let Some(tail) = block.expr
+            && !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, tail.hir_id)
+            && !in_external_macro(cx.tcx.sess, tail.span)
+            && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, tail.span, tail.hir_id)
+            && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, tail, pos)
+        {
+            span_lint_and_help(
+                cx,
+                UNNECESSARY_SAFETY_COMMENT,
+                tail.span,
+                "expression has unnecessary safety comment",
+                Some(help_span),
+                "consider removing the safety comment",
+            );
+        }
     }
 
-    fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
-        if let hir::ItemKind::Impl(imple) = item.kind
-            && imple.unsafety == hir::Unsafety::Unsafe
-            && !in_external_macro(cx.tcx.sess, item.span)
-            && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, item.hir_id())
-            && !is_unsafe_from_proc_macro(cx, item.span)
-            && !item_has_safety_comment(cx, item)
+    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &hir::Stmt<'tcx>) {
+        let (
+            hir::StmtKind::Local(&hir::Local { init: Some(expr), .. })
+            | hir::StmtKind::Expr(expr)
+            | hir::StmtKind::Semi(expr)
+        ) = stmt.kind else { return };
+        if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, stmt.hir_id)
+            && !in_external_macro(cx.tcx.sess, stmt.span)
+            && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, stmt.span, stmt.hir_id)
+            && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, expr, pos)
         {
+            span_lint_and_help(
+                cx,
+                UNNECESSARY_SAFETY_COMMENT,
+                stmt.span,
+                "statement has unnecessary safety comment",
+                Some(help_span),
+                "consider removing the safety comment",
+            );
+        }
+    }
+
+    fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
+        if in_external_macro(cx.tcx.sess, item.span) {
+            return;
+        }
+
+        let mk_spans = |pos: BytePos| {
             let source_map = cx.tcx.sess.source_map();
+            let span = Span::new(pos, pos, SyntaxContext::root(), None);
+            let help_span = source_map.span_extend_to_next_char(span, '\n', true);
             let span = if source_map.is_multiline(item.span) {
                 source_map.span_until_char(item.span, '\n')
             } else {
                 item.span
             };
+            (span, help_span)
+        };
 
-            span_lint_and_help(
-                cx,
-                UNDOCUMENTED_UNSAFE_BLOCKS,
-                span,
-                "unsafe impl missing a safety comment",
-                None,
-                "consider adding a safety comment on the preceding line",
-            );
+        let item_has_safety_comment = item_has_safety_comment(cx, item);
+        match (&item.kind, item_has_safety_comment) {
+            // lint unsafe impl without safety comment
+            (hir::ItemKind::Impl(impl_), HasSafetyComment::No) if impl_.unsafety == hir::Unsafety::Unsafe => {
+                if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, item.hir_id())
+                    && !is_unsafe_from_proc_macro(cx, item.span)
+                {
+                    let source_map = cx.tcx.sess.source_map();
+                    let span = if source_map.is_multiline(item.span) {
+                        source_map.span_until_char(item.span, '\n')
+                    } else {
+                        item.span
+                    };
+
+                    span_lint_and_help(
+                        cx,
+                        UNDOCUMENTED_UNSAFE_BLOCKS,
+                        span,
+                        "unsafe impl missing a safety comment",
+                        None,
+                        "consider adding a safety comment on the preceding line",
+                    );
+                }
+            },
+            // lint safe impl with unnecessary safety comment
+            (hir::ItemKind::Impl(impl_), HasSafetyComment::Yes(pos)) if impl_.unsafety == hir::Unsafety::Normal => {
+                if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) {
+                    let (span, help_span) = mk_spans(pos);
+
+                    span_lint_and_help(
+                        cx,
+                        UNNECESSARY_SAFETY_COMMENT,
+                        span,
+                        "impl has unnecessary safety comment",
+                        Some(help_span),
+                        "consider removing the safety comment",
+                    );
+                }
+            },
+            (hir::ItemKind::Impl(_), _) => {},
+            // const and static items only need a safety comment if their body is an unsafe block, lint otherwise
+            (&hir::ItemKind::Const(.., body) | &hir::ItemKind::Static(.., body), HasSafetyComment::Yes(pos)) => {
+                if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, body.hir_id) {
+                    let body = cx.tcx.hir().body(body);
+                    if !matches!(
+                        body.value.kind, hir::ExprKind::Block(block, _)
+                        if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
+                    ) {
+                        let (span, help_span) = mk_spans(pos);
+
+                        span_lint_and_help(
+                            cx,
+                            UNNECESSARY_SAFETY_COMMENT,
+                            span,
+                            &format!("{} has unnecessary safety comment", item.kind.descr()),
+                            Some(help_span),
+                            "consider removing the safety comment",
+                        );
+                    }
+                }
+            },
+            // Aside from unsafe impls and consts/statics with an unsafe block, items in general
+            // do not have safety invariants that need to be documented, so lint those.
+            (_, HasSafetyComment::Yes(pos)) => {
+                if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) {
+                    let (span, help_span) = mk_spans(pos);
+
+                    span_lint_and_help(
+                        cx,
+                        UNNECESSARY_SAFETY_COMMENT,
+                        span,
+                        &format!("{} has unnecessary safety comment", item.kind.descr()),
+                        Some(help_span),
+                        "consider removing the safety comment",
+                    );
+                }
+            },
+            _ => (),
         }
     }
 }
 
+fn expr_has_unnecessary_safety_comment<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx hir::Expr<'tcx>,
+    comment_pos: BytePos,
+) -> Option<Span> {
+    // this should roughly be the reverse of `block_parents_have_safety_comment`
+    if for_each_expr_with_closures(cx, expr, |expr| match expr.kind {
+        hir::ExprKind::Block(
+            Block {
+                rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
+                ..
+            },
+            _,
+        ) => ControlFlow::Break(()),
+        // statements will be handled by check_stmt itself again
+        hir::ExprKind::Block(..) => ControlFlow::Continue(Descend::No),
+        _ => ControlFlow::Continue(Descend::Yes),
+    })
+    .is_some()
+    {
+        return None;
+    }
+
+    let source_map = cx.tcx.sess.source_map();
+    let span = Span::new(comment_pos, comment_pos, SyntaxContext::root(), None);
+    let help_span = source_map.span_extend_to_next_char(span, '\n', true);
+
+    Some(help_span)
+}
+
 fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, span: Span) -> bool {
     let source_map = cx.sess().source_map();
     let file_pos = source_map.lookup_byte_offset(span.lo());
@@ -170,85 +342,134 @@ fn block_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
     // won't work. This is to avoid dealing with where such a comment should be place relative to
     // attributes and doc comments.
 
-    span_from_macro_expansion_has_safety_comment(cx, span) || span_in_body_has_safety_comment(cx, span)
+    matches!(
+        span_from_macro_expansion_has_safety_comment(cx, span),
+        HasSafetyComment::Yes(_)
+    ) || span_in_body_has_safety_comment(cx, span)
+}
+
+enum HasSafetyComment {
+    Yes(BytePos),
+    No,
+    Maybe,
 }
 
 /// Checks if the lines immediately preceding the item contain a safety comment.
 #[allow(clippy::collapsible_match)]
-fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> bool {
-    if span_from_macro_expansion_has_safety_comment(cx, item.span) {
-        return true;
+fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSafetyComment {
+    match span_from_macro_expansion_has_safety_comment(cx, item.span) {
+        HasSafetyComment::Maybe => (),
+        has_safety_comment => return has_safety_comment,
     }
 
-    if item.span.ctxt() == SyntaxContext::root() {
-        if let Some(parent_node) = get_parent_node(cx.tcx, item.hir_id()) {
-            let comment_start = match parent_node {
-                Node::Crate(parent_mod) => {
-                    comment_start_before_impl_in_mod(cx, parent_mod, parent_mod.spans.inner_span, item)
-                },
-                Node::Item(parent_item) => {
-                    if let ItemKind::Mod(parent_mod) = &parent_item.kind {
-                        comment_start_before_impl_in_mod(cx, parent_mod, parent_item.span, item)
-                    } else {
-                        // Doesn't support impls in this position. Pretend a comment was found.
-                        return true;
-                    }
-                },
-                Node::Stmt(stmt) => {
-                    if let Some(stmt_parent) = get_parent_node(cx.tcx, stmt.hir_id) {
-                        match stmt_parent {
-                            Node::Block(block) => walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo),
-                            _ => {
-                                // Doesn't support impls in this position. Pretend a comment was found.
-                                return true;
-                            },
-                        }
-                    } else {
-                        // Problem getting the parent node. Pretend a comment was found.
-                        return true;
-                    }
-                },
-                _ => {
+    if item.span.ctxt() != SyntaxContext::root() {
+        return HasSafetyComment::No;
+    }
+    if let Some(parent_node) = get_parent_node(cx.tcx, item.hir_id()) {
+        let comment_start = match parent_node {
+            Node::Crate(parent_mod) => {
+                comment_start_before_item_in_mod(cx, parent_mod, parent_mod.spans.inner_span, item)
+            },
+            Node::Item(parent_item) => {
+                if let ItemKind::Mod(parent_mod) = &parent_item.kind {
+                    comment_start_before_item_in_mod(cx, parent_mod, parent_item.span, item)
+                } else {
                     // Doesn't support impls in this position. Pretend a comment was found.
-                    return true;
-                },
-            };
+                    return HasSafetyComment::Maybe;
+                }
+            },
+            Node::Stmt(stmt) => {
+                if let Some(Node::Block(block)) = get_parent_node(cx.tcx, stmt.hir_id) {
+                    walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo)
+                } else {
+                    // Problem getting the parent node. Pretend a comment was found.
+                    return HasSafetyComment::Maybe;
+                }
+            },
+            _ => {
+                // Doesn't support impls in this position. Pretend a comment was found.
+                return HasSafetyComment::Maybe;
+            },
+        };
 
-            let source_map = cx.sess().source_map();
-            if let Some(comment_start) = comment_start
-                && let Ok(unsafe_line) = source_map.lookup_line(item.span.lo())
-                && let Ok(comment_start_line) = source_map.lookup_line(comment_start)
-                && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf)
-                && let Some(src) = unsafe_line.sf.src.as_deref()
-            {
-                unsafe_line.sf.lines(|lines| {
-                    comment_start_line.line < unsafe_line.line && text_has_safety_comment(
+        let source_map = cx.sess().source_map();
+        if let Some(comment_start) = comment_start
+            && let Ok(unsafe_line) = source_map.lookup_line(item.span.lo())
+            && let Ok(comment_start_line) = source_map.lookup_line(comment_start)
+            && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf)
+            && let Some(src) = unsafe_line.sf.src.as_deref()
+        {
+            return unsafe_line.sf.lines(|lines| {
+                if comment_start_line.line >= unsafe_line.line {
+                    HasSafetyComment::No
+                } else {
+                    match text_has_safety_comment(
                         src,
                         &lines[comment_start_line.line + 1..=unsafe_line.line],
                         unsafe_line.sf.start_pos.to_usize(),
-                    )
-                })
-            } else {
-                // Problem getting source text. Pretend a comment was found.
-                true
-            }
-        } else {
-            // No parent node. Pretend a comment was found.
-            true
+                    ) {
+                        Some(b) => HasSafetyComment::Yes(b),
+                        None => HasSafetyComment::No,
+                    }
+                }
+            });
+        }
+    }
+    HasSafetyComment::Maybe
+}
+
+/// Checks if the lines immediately preceding the item contain a safety comment.
+#[allow(clippy::collapsible_match)]
+fn stmt_has_safety_comment(cx: &LateContext<'_>, span: Span, hir_id: HirId) -> HasSafetyComment {
+    match span_from_macro_expansion_has_safety_comment(cx, span) {
+        HasSafetyComment::Maybe => (),
+        has_safety_comment => return has_safety_comment,
+    }
+
+    if span.ctxt() != SyntaxContext::root() {
+        return HasSafetyComment::No;
+    }
+
+    if let Some(parent_node) = get_parent_node(cx.tcx, hir_id) {
+        let comment_start = match parent_node {
+            Node::Block(block) => walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo),
+            _ => return HasSafetyComment::Maybe,
+        };
+
+        let source_map = cx.sess().source_map();
+        if let Some(comment_start) = comment_start
+            && let Ok(unsafe_line) = source_map.lookup_line(span.lo())
+            && let Ok(comment_start_line) = source_map.lookup_line(comment_start)
+            && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf)
+            && let Some(src) = unsafe_line.sf.src.as_deref()
+        {
+            return unsafe_line.sf.lines(|lines| {
+                if comment_start_line.line >= unsafe_line.line {
+                    HasSafetyComment::No
+                } else {
+                    match text_has_safety_comment(
+                        src,
+                        &lines[comment_start_line.line + 1..=unsafe_line.line],
+                        unsafe_line.sf.start_pos.to_usize(),
+                    ) {
+                        Some(b) => HasSafetyComment::Yes(b),
+                        None => HasSafetyComment::No,
+                    }
+                }
+            });
         }
-    } else {
-        false
     }
+    HasSafetyComment::Maybe
 }
 
-fn comment_start_before_impl_in_mod(
+fn comment_start_before_item_in_mod(
     cx: &LateContext<'_>,
     parent_mod: &hir::Mod<'_>,
     parent_mod_span: Span,
-    imple: &hir::Item<'_>,
+    item: &hir::Item<'_>,
 ) -> Option<BytePos> {
     parent_mod.item_ids.iter().enumerate().find_map(|(idx, item_id)| {
-        if *item_id == imple.item_id() {
+        if *item_id == item.item_id() {
             if idx == 0 {
                 // mod A { /* comment */ unsafe impl T {} ... }
                 // ^------------------------------------------^ returns the start of this span
@@ -270,11 +491,11 @@ fn comment_start_before_impl_in_mod(
     })
 }
 
-fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
+fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span) -> HasSafetyComment {
     let source_map = cx.sess().source_map();
     let ctxt = span.ctxt();
     if ctxt == SyntaxContext::root() {
-        false
+        HasSafetyComment::Maybe
     } else {
         // From a macro expansion. Get the text from the start of the macro declaration to start of the
         // unsafe block.
@@ -286,15 +507,22 @@ fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span
             && let Some(src) = unsafe_line.sf.src.as_deref()
         {
             unsafe_line.sf.lines(|lines| {
-                macro_line.line < unsafe_line.line && text_has_safety_comment(
-                    src,
-                    &lines[macro_line.line + 1..=unsafe_line.line],
-                    unsafe_line.sf.start_pos.to_usize(),
-                )
+                if macro_line.line < unsafe_line.line {
+                    match text_has_safety_comment(
+                        src,
+                        &lines[macro_line.line + 1..=unsafe_line.line],
+                        unsafe_line.sf.start_pos.to_usize(),
+                    ) {
+                        Some(b) => HasSafetyComment::Yes(b),
+                        None => HasSafetyComment::No,
+                    }
+                } else {
+                    HasSafetyComment::No
+                }
             })
         } else {
             // Problem getting source text. Pretend a comment was found.
-            true
+            HasSafetyComment::Maybe
         }
     }
 }
@@ -333,7 +561,7 @@ fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
                     src,
                     &lines[body_line.line + 1..=unsafe_line.line],
                     unsafe_line.sf.start_pos.to_usize(),
-                )
+                ).is_some()
             })
         } else {
             // Problem getting source text. Pretend a comment was found.
@@ -345,30 +573,34 @@ fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
 }
 
 /// Checks if the given text has a safety comment for the immediately proceeding line.
-fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> bool {
+fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> Option<BytePos> {
     let mut lines = line_starts
         .array_windows::<2>()
         .rev()
         .map_while(|[start, end]| {
             let start = start.to_usize() - offset;
             let end = end.to_usize() - offset;
-            src.get(start..end).map(|text| (start, text.trim_start()))
+            let text = src.get(start..end)?;
+            let trimmed = text.trim_start();
+            Some((start + (text.len() - trimmed.len()), trimmed))
         })
         .filter(|(_, text)| !text.is_empty());
 
     let Some((line_start, line)) = lines.next() else {
-        return false;
+        return None;
     };
     // Check for a sequence of line comments.
     if line.starts_with("//") {
-        let mut line = line;
+        let (mut line, mut line_start) = (line, line_start);
         loop {
             if line.to_ascii_uppercase().contains("SAFETY:") {
-                return true;
+                return Some(BytePos(
+                    u32::try_from(line_start).unwrap() + u32::try_from(offset).unwrap(),
+                ));
             }
             match lines.next() {
-                Some((_, x)) if x.starts_with("//") => line = x,
-                _ => return false,
+                Some((s, x)) if x.starts_with("//") => (line, line_start) = (x, s),
+                _ => return None,
             }
         }
     }
@@ -377,16 +609,19 @@ fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) ->
     let (mut line_start, mut line) = (line_start, line);
     loop {
         if line.starts_with("/*") {
-            let src = src[line_start..line_starts.last().unwrap().to_usize() - offset].trim_start();
+            let src = &src[line_start..line_starts.last().unwrap().to_usize() - offset];
             let mut tokens = tokenize(src);
-            return src[..tokens.next().unwrap().len as usize]
+            return (src[..tokens.next().unwrap().len as usize]
                 .to_ascii_uppercase()
                 .contains("SAFETY:")
-                && tokens.all(|t| t.kind == TokenKind::Whitespace);
+                && tokens.all(|t| t.kind == TokenKind::Whitespace))
+            .then_some(BytePos(
+                u32::try_from(line_start).unwrap() + u32::try_from(offset).unwrap(),
+            ));
         }
         match lines.next() {
             Some(x) => (line_start, line) = x,
-            None => return false,
+            None => return None,
         }
     }
 }
diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs
index bb6fb38e969..7355260ae4a 100644
--- a/clippy_lints/src/unnested_or_patterns.rs
+++ b/clippy_lints/src/unnested_or_patterns.rs
@@ -2,14 +2,14 @@
 
 use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_maybe_qself, eq_pat, eq_path};
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::{meets_msrv, msrvs, over};
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::over;
 use rustc_ast::mut_visit::*;
 use rustc_ast::ptr::P;
 use rustc_ast::{self as ast, Mutability, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
 use rustc_ast_pretty::pprust;
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::DUMMY_SP;
 
@@ -45,14 +45,13 @@ declare_clippy_lint! {
     "unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`"
 }
 
-#[derive(Clone, Copy)]
 pub struct UnnestedOrPatterns {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl UnnestedOrPatterns {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
@@ -61,13 +60,13 @@ impl_lint_pass!(UnnestedOrPatterns => [UNNESTED_OR_PATTERNS]);
 
 impl EarlyLintPass for UnnestedOrPatterns {
     fn check_arm(&mut self, cx: &EarlyContext<'_>, a: &ast::Arm) {
-        if meets_msrv(self.msrv, msrvs::OR_PATTERNS) {
+        if self.msrv.meets(msrvs::OR_PATTERNS) {
             lint_unnested_or_patterns(cx, &a.pat);
         }
     }
 
     fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
-        if meets_msrv(self.msrv, msrvs::OR_PATTERNS) {
+        if self.msrv.meets(msrvs::OR_PATTERNS) {
             if let ast::ExprKind::Let(pat, _, _) = &e.kind {
                 lint_unnested_or_patterns(cx, pat);
             }
@@ -75,13 +74,13 @@ impl EarlyLintPass for UnnestedOrPatterns {
     }
 
     fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) {
-        if meets_msrv(self.msrv, msrvs::OR_PATTERNS) {
+        if self.msrv.meets(msrvs::OR_PATTERNS) {
             lint_unnested_or_patterns(cx, &p.pat);
         }
     }
 
     fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) {
-        if meets_msrv(self.msrv, msrvs::OR_PATTERNS) {
+        if self.msrv.meets(msrvs::OR_PATTERNS) {
             lint_unnested_or_patterns(cx, &l.pat);
         }
     }
diff --git a/clippy_lints/src/unused_rounding.rs b/clippy_lints/src/unused_rounding.rs
index aac6719a8dc..097568cd1f7 100644
--- a/clippy_lints/src/unused_rounding.rs
+++ b/clippy_lints/src/unused_rounding.rs
@@ -1,4 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
 use rustc_ast::ast::{Expr, ExprKind, MethodCall};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
@@ -29,22 +30,16 @@ declare_clippy_lint! {
 }
 declare_lint_pass!(UnusedRounding => [UNUSED_ROUNDING]);
 
-fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> {
+fn is_useless_rounding<'a>(cx: &EarlyContext<'_>, expr: &'a Expr) -> Option<(&'a str, String)> {
     if let ExprKind::MethodCall(box MethodCall { seg:name_ident, receiver, .. }) = &expr.kind
         && let method_name = name_ident.ident.name.as_str()
         && (method_name == "ceil" || method_name == "round" || method_name == "floor")
         && let ExprKind::Lit(token_lit) = &receiver.kind
-        && token_lit.is_semantic_float() {
-            let mut f_str = token_lit.symbol.to_string();
-            let f = f_str.trim_end_matches('_').parse::<f64>().unwrap();
-            if let Some(suffix) = token_lit.suffix {
-                f_str.push_str(suffix.as_str());
-            }
-            if f.fract() == 0.0 {
-                Some((method_name, f_str))
-            } else {
-                None
-            }
+        && token_lit.is_semantic_float()
+        && let Ok(f) = token_lit.symbol.as_str().replace('_', "").parse::<f64>() {
+            (f.fract() == 0.0).then(||
+                (method_name, snippet(cx, receiver.span, "..").to_string())
+            )
         } else {
             None
         }
@@ -52,7 +47,7 @@ fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> {
 
 impl EarlyLintPass for UnusedRounding {
     fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
-        if let Some((method_name, float)) = is_useless_rounding(expr) {
+        if let Some((method_name, float)) = is_useless_rounding(cx, expr) {
             span_lint_and_sugg(
                 cx,
                 UNUSED_ROUNDING,
diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs
index e2860db71a5..4c755d812a0 100644
--- a/clippy_lints/src/use_self.rs
+++ b/clippy_lints/src/use_self.rs
@@ -1,6 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_from_proc_macro;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::ty::same_type_and_consts;
-use clippy_utils::{is_from_proc_macro, meets_msrv, msrvs};
 use if_chain::if_chain;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
@@ -14,7 +15,6 @@ use rustc_hir::{
 };
 use rustc_hir_analysis::hir_ty_to_ty;
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::Span;
 
@@ -57,13 +57,13 @@ declare_clippy_lint! {
 
 #[derive(Default)]
 pub struct UseSelf {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
     stack: Vec<StackItem>,
 }
 
 impl UseSelf {
     #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
+    pub fn new(msrv: Msrv) -> Self {
         Self {
             msrv,
             ..Self::default()
@@ -199,7 +199,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
     fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
         if_chain! {
             if !hir_ty.span.from_expansion();
-            if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS);
+            if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS);
             if let Some(&StackItem::Check {
                 impl_id,
                 in_body,
@@ -228,7 +228,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
         if_chain! {
             if !expr.span.from_expansion();
-            if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS);
+            if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS);
             if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last();
             if cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id);
             then {} else { return; }
@@ -248,7 +248,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
     fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) {
         if_chain! {
             if !pat.span.from_expansion();
-            if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS);
+            if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS);
             if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last();
             // get the path from the pattern
             if let PatKind::Path(QPath::Resolved(_, path))
diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs
index b37d4239477..b6dc8cd7ab1 100644
--- a/clippy_lints/src/utils/conf.rs
+++ b/clippy_lints/src/utils/conf.rs
@@ -402,6 +402,10 @@ define_Conf! {
     /// A list of paths to types that should be treated like `Arc`, i.e. ignored but
     /// for the generic parameters for determining interior mutability
     (ignore_interior_mutability: Vec<String> = Vec::from(["bytes::Bytes".into()])),
+    /// Lint: UNINLINED_FORMAT_ARGS.
+    ///
+    /// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)`
+    (allow_mixed_uninlined_format_args: bool = true),
 }
 
 /// Search for the configuration file.
diff --git a/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs
index 1e994e3f2b1..9876a8a765c 100644
--- a/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs
+++ b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs
@@ -41,7 +41,7 @@ impl LateLintPass<'_> for MsrvAttrImpl {
                     .type_of(f.did)
                     .walk()
                     .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_)))
-                    .any(|t| match_type(cx, t.expect_ty(), &paths::RUSTC_VERSION))
+                    .any(|t| match_type(cx, t.expect_ty(), &paths::MSRV))
             });
             if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs));
             then {
diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs
index cd8575c90e8..7987a233bdc 100644
--- a/clippy_utils/src/attrs.rs
+++ b/clippy_utils/src/attrs.rs
@@ -125,19 +125,19 @@ fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[ast::Attribute], name: &'
     }
 }
 
-pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'static str) -> Option<ast::Attribute> {
-    let mut unique_attr = None;
+pub fn get_unique_attr<'a>(
+    sess: &'a Session,
+    attrs: &'a [ast::Attribute],
+    name: &'static str,
+) -> Option<&'a ast::Attribute> {
+    let mut unique_attr: Option<&ast::Attribute> = None;
     for attr in get_attr(sess, attrs, name) {
-        match attr.style {
-            ast::AttrStyle::Inner if unique_attr.is_none() => unique_attr = Some(attr.clone()),
-            ast::AttrStyle::Inner => {
-                sess.struct_span_err(attr.span, &format!("`{name}` is defined multiple times"))
-                    .span_note(unique_attr.as_ref().unwrap().span, "first definition found here")
-                    .emit();
-            },
-            ast::AttrStyle::Outer => {
-                sess.span_err(attr.span, format!("`{name}` cannot be an outer attribute"));
-            },
+        if let Some(duplicate) = unique_attr {
+            sess.struct_span_err(attr.span, &format!("`{name}` is defined multiple times"))
+                .span_note(duplicate.span, "first definition found here")
+                .emit();
+        } else {
+            unique_attr = Some(attr);
         }
     }
     unique_attr
diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs
index f74f7dadfa9..96711936968 100644
--- a/clippy_utils/src/eager_or_lazy.rs
+++ b/clippy_utils/src/eager_or_lazy.rs
@@ -91,6 +91,16 @@ fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg:
     }
 }
 
+fn res_has_significant_drop(res: Res, cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
+    if let Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) = res {
+        cx.typeck_results()
+            .expr_ty(e)
+            .has_significant_drop(cx.tcx, cx.param_env)
+    } else {
+        false
+    }
+}
+
 #[expect(clippy::too_many_lines)]
 fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion {
     struct V<'cx, 'tcx> {
@@ -113,13 +123,8 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
                     },
                     args,
                 ) => match self.cx.qpath_res(path, hir_id) {
-                    Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) => {
-                        if self
-                            .cx
-                            .typeck_results()
-                            .expr_ty(e)
-                            .has_significant_drop(self.cx.tcx, self.cx.param_env)
-                        {
+                    res @ (Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_)) => {
+                        if res_has_significant_drop(res, self.cx, e) {
                             self.eagerness = ForceNoChange;
                             return;
                         }
@@ -147,6 +152,12 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
                     self.eagerness |= NoChange;
                     return;
                 },
+                ExprKind::Path(ref path) => {
+                    if res_has_significant_drop(self.cx.qpath_res(path, e.hir_id), self.cx, e) {
+                        self.eagerness = ForceNoChange;
+                        return;
+                    }
+                },
                 ExprKind::MethodCall(name, ..) => {
                     self.eagerness |= self
                         .cx
@@ -206,7 +217,6 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
                 | ExprKind::Match(..)
                 | ExprKind::Closure { .. }
                 | ExprKind::Field(..)
-                | ExprKind::Path(_)
                 | ExprKind::AddrOf(..)
                 | ExprKind::Struct(..)
                 | ExprKind::Repeat(..)
diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs
index 9e2682925a2..90192f46cbf 100644
--- a/clippy_utils/src/lib.rs
+++ b/clippy_utils/src/lib.rs
@@ -105,8 +105,6 @@ use rustc_middle::ty::{
     layout::IntegerExt, BorrowKind, ClosureKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeVisitable, UpvarCapture,
 };
 use rustc_middle::ty::{FloatTy, IntTy, UintTy};
-use rustc_semver::RustcVersion;
-use rustc_session::Session;
 use rustc_span::hygiene::{ExpnKind, MacroKind};
 use rustc_span::source_map::SourceMap;
 use rustc_span::sym;
@@ -118,36 +116,17 @@ use crate::consts::{constant, Constant};
 use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param};
 use crate::visitors::for_each_expr;
 
-pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
-    if let Ok(version) = RustcVersion::parse(msrv) {
-        return Some(version);
-    } else if let Some(sess) = sess {
-        if let Some(span) = span {
-            sess.span_err(span, format!("`{msrv}` is not a valid Rust version"));
-        }
-    }
-    None
-}
-
-pub fn meets_msrv(msrv: Option<RustcVersion>, lint_msrv: RustcVersion) -> bool {
-    msrv.map_or(true, |msrv| msrv.meets(lint_msrv))
-}
-
 #[macro_export]
 macro_rules! extract_msrv_attr {
     ($context:ident) => {
         fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) {
             let sess = rustc_lint::LintContext::sess(cx);
-            match $crate::get_unique_inner_attr(sess, attrs, "msrv") {
-                Some(msrv_attr) => {
-                    if let Some(msrv) = msrv_attr.value_str() {
-                        self.msrv = $crate::parse_msrv(&msrv.to_string(), Some(sess), Some(msrv_attr.span));
-                    } else {
-                        sess.span_err(msrv_attr.span, "bad clippy attribute");
-                    }
-                },
-                _ => (),
-            }
+            self.msrv.enter_lint_attrs(sess, attrs);
+        }
+
+        fn exit_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) {
+            let sess = rustc_lint::LintContext::sess(cx);
+            self.msrv.exit_lint_attrs(sess, attrs);
         }
     };
 }
diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs
index 79b19e6fb3e..12a512f78a6 100644
--- a/clippy_utils/src/msrvs.rs
+++ b/clippy_utils/src/msrvs.rs
@@ -1,4 +1,11 @@
+use std::sync::OnceLock;
+
+use rustc_ast::Attribute;
 use rustc_semver::RustcVersion;
+use rustc_session::Session;
+use rustc_span::Span;
+
+use crate::attrs::get_unique_attr;
 
 macro_rules! msrv_aliases {
     ($($major:literal,$minor:literal,$patch:literal {
@@ -40,3 +47,97 @@ msrv_aliases! {
     1,16,0 { STR_REPEAT }
     1,55,0 { SEEK_REWIND }
 }
+
+fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
+    if let Ok(version) = RustcVersion::parse(msrv) {
+        return Some(version);
+    } else if let Some(sess) = sess {
+        if let Some(span) = span {
+            sess.span_err(span, format!("`{msrv}` is not a valid Rust version"));
+        }
+    }
+    None
+}
+
+/// Tracks the current MSRV from `clippy.toml`, `Cargo.toml` or set via `#[clippy::msrv]`
+#[derive(Debug, Clone, Default)]
+pub struct Msrv {
+    stack: Vec<RustcVersion>,
+}
+
+impl Msrv {
+    fn new(initial: Option<RustcVersion>) -> Self {
+        Self {
+            stack: Vec::from_iter(initial),
+        }
+    }
+
+    fn read_inner(conf_msrv: &Option<String>, sess: &Session) -> Self {
+        let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION")
+            .ok()
+            .and_then(|v| parse_msrv(&v, None, None));
+        let clippy_msrv = conf_msrv.as_ref().and_then(|s| {
+            parse_msrv(s, None, None).or_else(|| {
+                sess.err(format!(
+                    "error reading Clippy's configuration file. `{s}` is not a valid Rust version"
+                ));
+                None
+            })
+        });
+
+        // if both files have an msrv, let's compare them and emit a warning if they differ
+        if let Some(cargo_msrv) = cargo_msrv
+            && let Some(clippy_msrv) = clippy_msrv
+            && clippy_msrv != cargo_msrv
+        {
+            sess.warn(format!(
+                "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`"
+            ));
+        }
+
+        Self::new(clippy_msrv.or(cargo_msrv))
+    }
+
+    /// Set the initial MSRV from the Clippy config file or from Cargo due to the `rust-version`
+    /// field in `Cargo.toml`
+    ///
+    /// Returns a `&'static Msrv` as `Copy` types are more easily passed to the
+    /// `register_{late,early}_pass` callbacks
+    pub fn read(conf_msrv: &Option<String>, sess: &Session) -> &'static Self {
+        static PARSED: OnceLock<Msrv> = OnceLock::new();
+
+        PARSED.get_or_init(|| Self::read_inner(conf_msrv, sess))
+    }
+
+    pub fn current(&self) -> Option<RustcVersion> {
+        self.stack.last().copied()
+    }
+
+    pub fn meets(&self, required: RustcVersion) -> bool {
+        self.current().map_or(true, |version| version.meets(required))
+    }
+
+    fn parse_attr(sess: &Session, attrs: &[Attribute]) -> Option<RustcVersion> {
+        if let Some(msrv_attr) = get_unique_attr(sess, attrs, "msrv") {
+            if let Some(msrv) = msrv_attr.value_str() {
+                return parse_msrv(&msrv.to_string(), Some(sess), Some(msrv_attr.span));
+            }
+
+            sess.span_err(msrv_attr.span, "bad clippy attribute");
+        }
+
+        None
+    }
+
+    pub fn enter_lint_attrs(&mut self, sess: &Session, attrs: &[Attribute]) {
+        if let Some(version) = Self::parse_attr(sess, attrs) {
+            self.stack.push(version);
+        }
+    }
+
+    pub fn exit_lint_attrs(&mut self, sess: &Session, attrs: &[Attribute]) {
+        if Self::parse_attr(sess, attrs).is_some() {
+            self.stack.pop();
+        }
+    }
+}
diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs
index 6c09c146082..6417f0f3c71 100644
--- a/clippy_utils/src/paths.rs
+++ b/clippy_utils/src/paths.rs
@@ -60,6 +60,8 @@ pub const LATE_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "LateLintPass"];
 #[cfg(feature = "internal")]
 pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
 pub const MEM_SWAP: [&str; 3] = ["core", "mem", "swap"];
+#[cfg(feature = "internal")]
+pub const MSRV: [&str; 3] = ["clippy_utils", "msrvs", "Msrv"];
 pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
 pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"];
 pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
@@ -72,7 +74,6 @@ pub const PEEKABLE: [&str; 5] = ["core", "iter", "adapters", "peekable", "Peekab
 pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"];
 #[cfg_attr(not(unix), allow(clippy::invalid_paths))]
 pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "PermissionsExt", "from_mode"];
-pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
 pub const PTR_COPY: [&str; 3] = ["core", "intrinsics", "copy"];
 pub const PTR_COPY_NONOVERLAPPING: [&str; 3] = ["core", "intrinsics", "copy_nonoverlapping"];
 pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
@@ -101,8 +102,6 @@ pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
 pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"];
 pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
 pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
-#[cfg(feature = "internal")]
-pub const RUSTC_VERSION: [&str; 2] = ["rustc_semver", "RustcVersion"];
 pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"];
 pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
 pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"];
diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs
index b8c2dd5ab9e..480e8e55cf3 100644
--- a/clippy_utils/src/qualify_min_const_fn.rs
+++ b/clippy_utils/src/qualify_min_const_fn.rs
@@ -3,6 +3,7 @@
 // of terminologies might not be relevant in the context of Clippy. Note that its behavior might
 // differ from the time of `rustc` even if the name stays the same.
 
+use crate::msrvs::Msrv;
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_middle::mir::{
@@ -18,20 +19,22 @@ use std::borrow::Cow;
 
 type McfResult = Result<(), (Span, Cow<'static, str>)>;
 
-pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: Option<RustcVersion>) -> McfResult {
+pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv) -> McfResult {
     let def_id = body.source.def_id();
     let mut current = def_id;
     loop {
         let predicates = tcx.predicates_of(current);
         for (predicate, _) in predicates.predicates {
             match predicate.kind().skip_binder() {
-                ty::PredicateKind::Clause(ty::Clause::RegionOutlives(_))
-                | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(_))
+                ty::PredicateKind::Clause(
+                    ty::Clause::RegionOutlives(_)
+                    | ty::Clause::TypeOutlives(_)
+                    | ty::Clause::Projection(_)
+                    | ty::Clause::Trait(..),
+                )
                 | ty::PredicateKind::WellFormed(_)
-                | ty::PredicateKind::Clause(ty::Clause::Projection(_))
                 | ty::PredicateKind::ConstEvaluatable(..)
                 | ty::PredicateKind::ConstEquate(..)
-                | ty::PredicateKind::Clause(ty::Clause::Trait(..))
                 | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue,
                 ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {predicate:#?}"),
                 ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {predicate:#?}"),
@@ -281,7 +284,7 @@ fn check_terminator<'tcx>(
     tcx: TyCtxt<'tcx>,
     body: &Body<'tcx>,
     terminator: &Terminator<'tcx>,
-    msrv: Option<RustcVersion>,
+    msrv: &Msrv,
 ) -> McfResult {
     let span = terminator.source_info.span;
     match &terminator.kind {
@@ -365,7 +368,7 @@ fn check_terminator<'tcx>(
     }
 }
 
-fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<RustcVersion>) -> bool {
+fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
     tcx.is_const_fn(def_id)
         && tcx.lookup_const_stability(def_id).map_or(true, |const_stab| {
             if let rustc_attr::StabilityLevel::Stable { since, .. } = const_stab.level {
@@ -384,15 +387,12 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<RustcVersion>) -> bo
 
                 let since = rustc_span::Symbol::intern(short_version);
 
-                crate::meets_msrv(
-                    msrv,
-                    RustcVersion::parse(since.as_str()).unwrap_or_else(|err| {
-                        panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}")
-                    }),
-                )
+                msrv.meets(RustcVersion::parse(since.as_str()).unwrap_or_else(|err| {
+                    panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}")
+                }))
             } else {
                 // Unstable const fn with the feature enabled.
-                msrv.is_none()
+                msrv.current().is_none()
             }
         })
 }
diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs
index eacfa91ba55..cd5dcfdaca3 100644
--- a/clippy_utils/src/source.rs
+++ b/clippy_utils/src/source.rs
@@ -5,6 +5,7 @@
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LintContext};
+use rustc_session::Session;
 use rustc_span::hygiene;
 use rustc_span::source_map::{original_sp, SourceMap};
 use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext, DUMMY_SP};
@@ -205,10 +206,19 @@ pub fn snippet_with_applicability<'a, T: LintContext>(
     default: &'a str,
     applicability: &mut Applicability,
 ) -> Cow<'a, str> {
+    snippet_with_applicability_sess(cx.sess(), span, default, applicability)
+}
+
+fn snippet_with_applicability_sess<'a>(
+    sess: &Session,
+    span: Span,
+    default: &'a str,
+    applicability: &mut Applicability,
+) -> Cow<'a, str> {
     if *applicability != Applicability::Unspecified && span.from_expansion() {
         *applicability = Applicability::MaybeIncorrect;
     }
-    snippet_opt(cx, span).map_or_else(
+    snippet_opt_sess(sess, span).map_or_else(
         || {
             if *applicability == Applicability::MachineApplicable {
                 *applicability = Applicability::HasPlaceholders;
@@ -226,8 +236,12 @@ pub fn snippet_with_macro_callsite<'a, T: LintContext>(cx: &T, span: Span, defau
 }
 
 /// Converts a span to a code snippet. Returns `None` if not available.
-pub fn snippet_opt<T: LintContext>(cx: &T, span: Span) -> Option<String> {
-    cx.sess().source_map().span_to_snippet(span).ok()
+pub fn snippet_opt(cx: &impl LintContext, span: Span) -> Option<String> {
+    snippet_opt_sess(cx.sess(), span)
+}
+
+fn snippet_opt_sess(sess: &Session, span: Span) -> Option<String> {
+    sess.source_map().span_to_snippet(span).ok()
 }
 
 /// Converts a span (from a block) to a code snippet if available, otherwise use default.
@@ -277,8 +291,8 @@ pub fn snippet_block<'a, T: LintContext>(
 
 /// Same as `snippet_block`, but adapts the applicability level by the rules of
 /// `snippet_with_applicability`.
-pub fn snippet_block_with_applicability<'a, T: LintContext>(
-    cx: &T,
+pub fn snippet_block_with_applicability<'a>(
+    cx: &impl LintContext,
     span: Span,
     default: &'a str,
     indent_relative_to: Option<Span>,
@@ -299,7 +313,17 @@ pub fn snippet_block_with_applicability<'a, T: LintContext>(
 ///
 /// This will also return whether or not the snippet is a macro call.
 pub fn snippet_with_context<'a>(
-    cx: &LateContext<'_>,
+    cx: &impl LintContext,
+    span: Span,
+    outer: SyntaxContext,
+    default: &'a str,
+    applicability: &mut Applicability,
+) -> (Cow<'a, str>, bool) {
+    snippet_with_context_sess(cx.sess(), span, outer, default, applicability)
+}
+
+fn snippet_with_context_sess<'a>(
+    sess: &Session,
     span: Span,
     outer: SyntaxContext,
     default: &'a str,
@@ -318,7 +342,7 @@ pub fn snippet_with_context<'a>(
     );
 
     (
-        snippet_with_applicability(cx, span, default, applicability),
+        snippet_with_applicability_sess(sess, span, default, applicability),
         is_macro_call,
     )
 }
diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs
index 3cacdb49377..b66604f33db 100644
--- a/clippy_utils/src/sugg.rs
+++ b/clippy_utils/src/sugg.rs
@@ -176,25 +176,28 @@ impl<'a> Sugg<'a> {
     }
 
     /// Prepare a suggestion from an expression.
-    pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self {
+    pub fn ast(
+        cx: &EarlyContext<'_>,
+        expr: &ast::Expr,
+        default: &'a str,
+        ctxt: SyntaxContext,
+        app: &mut Applicability,
+    ) -> Self {
         use rustc_ast::ast::RangeLimits;
 
-        let snippet_without_expansion = |cx, span: Span, default| {
-            if span.from_expansion() {
-                snippet_with_macro_callsite(cx, span, default)
-            } else {
-                snippet(cx, span, default)
-            }
-        };
-
+        #[expect(clippy::match_wildcard_for_single_variants)]
         match expr.kind {
+            _ if expr.span.ctxt() != ctxt => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0),
             ast::ExprKind::AddrOf(..)
             | ast::ExprKind::Box(..)
             | ast::ExprKind::Closure { .. }
             | ast::ExprKind::If(..)
             | ast::ExprKind::Let(..)
             | ast::ExprKind::Unary(..)
-            | ast::ExprKind::Match(..) => Sugg::MaybeParen(snippet_without_expansion(cx, expr.span, default)),
+            | ast::ExprKind::Match(..) => match snippet_with_context(cx, expr.span, ctxt, default, app) {
+                (snip, false) => Sugg::MaybeParen(snip),
+                (snip, true) => Sugg::NonParen(snip),
+            },
             ast::ExprKind::Async(..)
             | ast::ExprKind::Block(..)
             | ast::ExprKind::Break(..)
@@ -224,45 +227,49 @@ impl<'a> Sugg<'a> {
             | ast::ExprKind::Array(..)
             | ast::ExprKind::While(..)
             | ast::ExprKind::Await(..)
-            | ast::ExprKind::Err => Sugg::NonParen(snippet_without_expansion(cx, expr.span, default)),
+            | ast::ExprKind::Err => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0),
             ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp(
                 AssocOp::DotDot,
-                lhs.as_ref()
-                    .map_or("".into(), |lhs| snippet_without_expansion(cx, lhs.span, default)),
-                rhs.as_ref()
-                    .map_or("".into(), |rhs| snippet_without_expansion(cx, rhs.span, default)),
+                lhs.as_ref().map_or("".into(), |lhs| {
+                    snippet_with_context(cx, lhs.span, ctxt, default, app).0
+                }),
+                rhs.as_ref().map_or("".into(), |rhs| {
+                    snippet_with_context(cx, rhs.span, ctxt, default, app).0
+                }),
             ),
             ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::Closed) => Sugg::BinOp(
                 AssocOp::DotDotEq,
-                lhs.as_ref()
-                    .map_or("".into(), |lhs| snippet_without_expansion(cx, lhs.span, default)),
-                rhs.as_ref()
-                    .map_or("".into(), |rhs| snippet_without_expansion(cx, rhs.span, default)),
+                lhs.as_ref().map_or("".into(), |lhs| {
+                    snippet_with_context(cx, lhs.span, ctxt, default, app).0
+                }),
+                rhs.as_ref().map_or("".into(), |rhs| {
+                    snippet_with_context(cx, rhs.span, ctxt, default, app).0
+                }),
             ),
             ast::ExprKind::Assign(ref lhs, ref rhs, _) => Sugg::BinOp(
                 AssocOp::Assign,
-                snippet_without_expansion(cx, lhs.span, default),
-                snippet_without_expansion(cx, rhs.span, default),
+                snippet_with_context(cx, lhs.span, ctxt, default, app).0,
+                snippet_with_context(cx, rhs.span, ctxt, default, app).0,
             ),
             ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => Sugg::BinOp(
                 astbinop2assignop(op),
-                snippet_without_expansion(cx, lhs.span, default),
-                snippet_without_expansion(cx, rhs.span, default),
+                snippet_with_context(cx, lhs.span, ctxt, default, app).0,
+                snippet_with_context(cx, rhs.span, ctxt, default, app).0,
             ),
             ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp(
                 AssocOp::from_ast_binop(op.node),
-                snippet_without_expansion(cx, lhs.span, default),
-                snippet_without_expansion(cx, rhs.span, default),
+                snippet_with_context(cx, lhs.span, ctxt, default, app).0,
+                snippet_with_context(cx, rhs.span, ctxt, default, app).0,
             ),
             ast::ExprKind::Cast(ref lhs, ref ty) => Sugg::BinOp(
                 AssocOp::As,
-                snippet_without_expansion(cx, lhs.span, default),
-                snippet_without_expansion(cx, ty.span, default),
+                snippet_with_context(cx, lhs.span, ctxt, default, app).0,
+                snippet_with_context(cx, ty.span, ctxt, default, app).0,
             ),
             ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp(
                 AssocOp::Colon,
-                snippet_without_expansion(cx, lhs.span, default),
-                snippet_without_expansion(cx, ty.span, default),
+                snippet_with_context(cx, lhs.span, ctxt, default, app).0,
+                snippet_with_context(cx, ty.span, ctxt, default, app).0,
             ),
         }
     }
diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs
index 2ceda3511fe..bfb2d472a39 100644
--- a/clippy_utils/src/ty.rs
+++ b/clippy_utils/src/ty.rs
@@ -9,7 +9,10 @@ use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::{Expr, FnDecl, LangItem, TyKind, Unsafety};
-use rustc_infer::infer::{TyCtxtInferExt, type_variable::{TypeVariableOrigin, TypeVariableOriginKind}};
+use rustc_infer::infer::{
+    type_variable::{TypeVariableOrigin, TypeVariableOriginKind},
+    TyCtxtInferExt,
+};
 use rustc_lint::LateContext;
 use rustc_middle::mir::interpret::{ConstValue, Scalar};
 use rustc_middle::ty::{
@@ -189,7 +192,13 @@ pub fn implements_trait<'tcx>(
     trait_id: DefId,
     ty_params: &[GenericArg<'tcx>],
 ) -> bool {
-    implements_trait_with_env(cx.tcx, cx.param_env, ty, trait_id, ty_params.iter().map(|&arg| Some(arg)))
+    implements_trait_with_env(
+        cx.tcx,
+        cx.param_env,
+        ty,
+        trait_id,
+        ty_params.iter().map(|&arg| Some(arg)),
+    )
 }
 
 /// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context.
@@ -212,7 +221,11 @@ pub fn implements_trait_with_env<'tcx>(
         kind: TypeVariableOriginKind::MiscVariable,
         span: DUMMY_SP,
     };
-    let ty_params = tcx.mk_substs(ty_params.into_iter().map(|arg| arg.unwrap_or_else(|| infcx.next_ty_var(orig).into())));
+    let ty_params = tcx.mk_substs(
+        ty_params
+            .into_iter()
+            .map(|arg| arg.unwrap_or_else(|| infcx.next_ty_var(orig).into())),
+    );
     infcx
         .type_implements_trait(trait_id, [ty.into()].into_iter().chain(ty_params), param_env)
         .must_apply_modulo_regions()
@@ -712,7 +725,9 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> O
                 }
                 inputs = Some(i);
             },
-            PredicateKind::Clause(ty::Clause::Projection(p)) if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() => {
+            PredicateKind::Clause(ty::Clause::Projection(p))
+                if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() =>
+            {
                 if output.is_some() {
                     // Multiple different fn trait impls. Is this even allowed?
                     return None;
@@ -992,14 +1007,12 @@ pub fn make_projection<'tcx>(
 
             debug_assert!(
                 generic_count == substs.len(),
-                "wrong number of substs for `{:?}`: found `{}` expected `{}`.\n\
+                "wrong number of substs for `{:?}`: found `{}` expected `{generic_count}`.\n\
                     note: the expected parameters are: {:#?}\n\
-                    the given arguments are: `{:#?}`",
+                    the given arguments are: `{substs:#?}`",
                 assoc_item.def_id,
                 substs.len(),
-                generic_count,
                 params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>(),
-                substs,
             );
 
             if let Some((idx, (param, arg))) = params
@@ -1017,14 +1030,11 @@ pub fn make_projection<'tcx>(
             {
                 debug_assert!(
                     false,
-                    "mismatched subst type at index {}: expected a {}, found `{:?}`\n\
+                    "mismatched subst type at index {idx}: expected a {}, found `{arg:?}`\n\
                         note: the expected parameters are {:#?}\n\
-                        the given arguments are {:#?}",
-                    idx,
+                        the given arguments are {substs:#?}",
                     param.descr(),
-                    arg,
-                    params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>(),
-                    substs,
+                    params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>()
                 );
             }
         }
diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs
index d4294f18fd5..863fb60fcfc 100644
--- a/clippy_utils/src/visitors.rs
+++ b/clippy_utils/src/visitors.rs
@@ -170,22 +170,22 @@ where
         cb: F,
     }
 
-    struct WithStmtGuarg<'a, F> {
+    struct WithStmtGuard<'a, F> {
         val: &'a mut RetFinder<F>,
         prev_in_stmt: bool,
     }
 
     impl<F> RetFinder<F> {
-        fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> {
+        fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuard<'_, F> {
             let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt);
-            WithStmtGuarg {
+            WithStmtGuard {
                 val: self,
                 prev_in_stmt,
             }
         }
     }
 
-    impl<F> std::ops::Deref for WithStmtGuarg<'_, F> {
+    impl<F> std::ops::Deref for WithStmtGuard<'_, F> {
         type Target = RetFinder<F>;
 
         fn deref(&self) -> &Self::Target {
@@ -193,13 +193,13 @@ where
         }
     }
 
-    impl<F> std::ops::DerefMut for WithStmtGuarg<'_, F> {
+    impl<F> std::ops::DerefMut for WithStmtGuard<'_, F> {
         fn deref_mut(&mut self) -> &mut Self::Target {
             self.val
         }
     }
 
-    impl<F> Drop for WithStmtGuarg<'_, F> {
+    impl<F> Drop for WithStmtGuard<'_, F> {
         fn drop(&mut self) {
             self.val.in_stmt = self.prev_in_stmt;
         }
diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs
index ee8ab7c1d7c..bd49f096072 100644
--- a/lintcheck/src/main.rs
+++ b/lintcheck/src/main.rs
@@ -120,8 +120,8 @@ impl ClippyWarning {
             format!("$CARGO_HOME/{}", stripped.display())
         } else {
             format!(
-                "target/lintcheck/sources/{}-{}/{}",
-                crate_name, crate_version, span.file_name
+                "target/lintcheck/sources/{crate_name}-{crate_version}/{}",
+                span.file_name
             )
         };
 
@@ -322,13 +322,13 @@ impl Crate {
 
         if config.max_jobs == 1 {
             println!(
-                "{}/{} {}% Linting {} {}",
-                index, total_crates_to_lint, perc, &self.name, &self.version
+                "{index}/{total_crates_to_lint} {perc}% Linting {} {}",
+                &self.name, &self.version
             );
         } else {
             println!(
-                "{}/{} {}% Linting {} {} in target dir {:?}",
-                index, total_crates_to_lint, perc, &self.name, &self.version, thread_index
+                "{index}/{total_crates_to_lint} {perc}% Linting {} {} in target dir {thread_index:?}",
+                &self.name, &self.version
             );
         }
 
@@ -398,8 +398,7 @@ impl Crate {
             .output()
             .unwrap_or_else(|error| {
                 panic!(
-                    "Encountered error:\n{:?}\ncargo_clippy_path: {}\ncrate path:{}\n",
-                    error,
+                    "Encountered error:\n{error:?}\ncargo_clippy_path: {}\ncrate path:{}\n",
                     &cargo_clippy_path.display(),
                     &self.path.display()
                 );
diff --git a/src/driver.rs b/src/driver.rs
index ad6132a49ba..9ec4df8e651 100644
--- a/src/driver.rs
+++ b/src/driver.rs
@@ -1,6 +1,7 @@
 #![feature(rustc_private)]
 #![feature(let_chains)]
 #![feature(once_cell)]
+#![feature(lint_reasons)]
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
 // warn on lints, that are included in `rust-lang/rust`s bootstrap
 #![warn(rust_2018_idioms, unused_lifetimes)]
@@ -90,6 +91,10 @@ fn track_files(parse_sess: &mut ParseSess, conf_path_string: Option<String>) {
 
     // During development track the `clippy-driver` executable so that cargo will re-run clippy whenever
     // it is rebuilt
+    #[expect(
+        clippy::collapsible_if,
+        reason = "Due to a bug in let_chains this if statement can't be collapsed"
+    )]
     if cfg!(debug_assertions) {
         if let Ok(current_exe) = env::current_exe()
             && let Some(current_exe) = current_exe.to_str()
diff --git a/tests/ui-internal/invalid_msrv_attr_impl.fixed b/tests/ui-internal/invalid_msrv_attr_impl.fixed
index 900a8fffd40..08634063a57 100644
--- a/tests/ui-internal/invalid_msrv_attr_impl.fixed
+++ b/tests/ui-internal/invalid_msrv_attr_impl.fixed
@@ -11,9 +11,9 @@ extern crate rustc_middle;
 #[macro_use]
 extern crate rustc_session;
 use clippy_utils::extract_msrv_attr;
+use clippy_utils::msrvs::Msrv;
 use rustc_hir::Expr;
 use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
-use rustc_semver::RustcVersion;
 
 declare_lint! {
     pub TEST_LINT,
@@ -22,7 +22,7 @@ declare_lint! {
 }
 
 struct Pass {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl_lint_pass!(Pass => [TEST_LINT]);
diff --git a/tests/ui-internal/invalid_msrv_attr_impl.rs b/tests/ui-internal/invalid_msrv_attr_impl.rs
index 4bc8164db67..f8af77e6d39 100644
--- a/tests/ui-internal/invalid_msrv_attr_impl.rs
+++ b/tests/ui-internal/invalid_msrv_attr_impl.rs
@@ -11,9 +11,9 @@ extern crate rustc_middle;
 #[macro_use]
 extern crate rustc_session;
 use clippy_utils::extract_msrv_attr;
+use clippy_utils::msrvs::Msrv;
 use rustc_hir::Expr;
 use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
-use rustc_semver::RustcVersion;
 
 declare_lint! {
     pub TEST_LINT,
@@ -22,7 +22,7 @@ declare_lint! {
 }
 
 struct Pass {
-    msrv: Option<RustcVersion>,
+    msrv: Msrv,
 }
 
 impl_lint_pass!(Pass => [TEST_LINT]);
diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr
index 8bfc060e991..2a240cc249b 100644
--- a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr
+++ b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr
@@ -1,12 +1,3 @@
-error: hardcoded path to a language item
-  --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40
-   |
-LL |     const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"];
-   |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: convert all references to use `LangItem::DerefMut`
-   = note: `-D clippy::unnecessary-def-path` implied by `-D warnings`
-
 error: hardcoded path to a diagnostic item
   --> $DIR/unnecessary_def_path_hardcoded_path.rs:10:36
    |
@@ -14,6 +5,7 @@ LL |     const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"];
    |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: convert all references to use `sym::Deref`
+   = note: `-D clippy::unnecessary-def-path` implied by `-D warnings`
 
 error: hardcoded path to a diagnostic item
   --> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43
@@ -23,5 +15,13 @@ LL |     const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref",
    |
    = help: convert all references to use `sym::deref_method`
 
+error: hardcoded path to a language item
+  --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40
+   |
+LL |     const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"];
+   |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: convert all references to use `LangItem::DerefMut`
+
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui-toml/allow_mixed_uninlined_format_args/clippy.toml b/tests/ui-toml/allow_mixed_uninlined_format_args/clippy.toml
new file mode 100644
index 00000000000..b95e806aae2
--- /dev/null
+++ b/tests/ui-toml/allow_mixed_uninlined_format_args/clippy.toml
@@ -0,0 +1 @@
+allow-mixed-uninlined-format-args = false
diff --git a/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.fixed b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.fixed
new file mode 100644
index 00000000000..aa8b45b5fe7
--- /dev/null
+++ b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.fixed
@@ -0,0 +1,14 @@
+// run-rustfix
+#![warn(clippy::uninlined_format_args)]
+
+fn main() {
+    let local_i32 = 1;
+    let local_f64 = 2.0;
+    let local_opt: Option<i32> = Some(3);
+
+    println!("val='{local_i32}'");
+    println!("Hello x is {local_f64:.local_i32$}");
+    println!("Hello {local_i32} is {local_f64:.*}", 5);
+    println!("Hello {local_i32} is {local_f64:.*}", 5);
+    println!("{local_i32}, {}", local_opt.unwrap());
+}
diff --git a/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs
new file mode 100644
index 00000000000..ad2e4863ee8
--- /dev/null
+++ b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs
@@ -0,0 +1,14 @@
+// run-rustfix
+#![warn(clippy::uninlined_format_args)]
+
+fn main() {
+    let local_i32 = 1;
+    let local_f64 = 2.0;
+    let local_opt: Option<i32> = Some(3);
+
+    println!("val='{}'", local_i32);
+    println!("Hello {} is {:.*}", "x", local_i32, local_f64);
+    println!("Hello {} is {:.*}", local_i32, 5, local_f64);
+    println!("Hello {} is {2:.*}", local_i32, 5, local_f64);
+    println!("{}, {}", local_i32, local_opt.unwrap());
+}
diff --git a/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr
new file mode 100644
index 00000000000..ee941762196
--- /dev/null
+++ b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr
@@ -0,0 +1,76 @@
+error: variables can be used directly in the `format!` string
+  --> $DIR/uninlined_format_args.rs:9:5
+   |
+LL |     println!("val='{}'", local_i32);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::uninlined-format-args` implied by `-D warnings`
+help: change this to
+   |
+LL -     println!("val='{}'", local_i32);
+LL +     println!("val='{local_i32}'");
+   |
+
+error: literal with an empty format string
+  --> $DIR/uninlined_format_args.rs:10:35
+   |
+LL |     println!("Hello {} is {:.*}", "x", local_i32, local_f64);
+   |                                   ^^^
+   |
+   = note: `-D clippy::print-literal` implied by `-D warnings`
+help: try this
+   |
+LL -     println!("Hello {} is {:.*}", "x", local_i32, local_f64);
+LL +     println!("Hello x is {:.*}", local_i32, local_f64);
+   |
+
+error: variables can be used directly in the `format!` string
+  --> $DIR/uninlined_format_args.rs:10:5
+   |
+LL |     println!("Hello {} is {:.*}", "x", local_i32, local_f64);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: change this to
+   |
+LL -     println!("Hello {} is {:.*}", "x", local_i32, local_f64);
+LL +     println!("Hello {} is {local_f64:.local_i32$}", "x");
+   |
+
+error: variables can be used directly in the `format!` string
+  --> $DIR/uninlined_format_args.rs:11:5
+   |
+LL |     println!("Hello {} is {:.*}", local_i32, 5, local_f64);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: change this to
+   |
+LL -     println!("Hello {} is {:.*}", local_i32, 5, local_f64);
+LL +     println!("Hello {local_i32} is {local_f64:.*}", 5);
+   |
+
+error: variables can be used directly in the `format!` string
+  --> $DIR/uninlined_format_args.rs:12:5
+   |
+LL |     println!("Hello {} is {2:.*}", local_i32, 5, local_f64);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: change this to
+   |
+LL -     println!("Hello {} is {2:.*}", local_i32, 5, local_f64);
+LL +     println!("Hello {local_i32} is {local_f64:.*}", 5);
+   |
+
+error: variables can be used directly in the `format!` string
+  --> $DIR/uninlined_format_args.rs:13:5
+   |
+LL |     println!("{}, {}", local_i32, local_opt.unwrap());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: change this to
+   |
+LL -     println!("{}, {}", local_i32, local_opt.unwrap());
+LL +     println!("{local_i32}, {}", local_opt.unwrap());
+   |
+
+error: aborting due to 6 previous errors
+
diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
index f91d285c2e0..01a5e962c94 100644
--- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
+++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
@@ -1,6 +1,7 @@
 error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of
            allow-dbg-in-tests
            allow-expect-in-tests
+           allow-mixed-uninlined-format-args
            allow-print-in-tests
            allow-unwrap-in-tests
            allowed-scripts
diff --git a/tests/ui/almost_complete_letter_range.fixed b/tests/ui/almost_complete_letter_range.fixed
index 079b7c000dc..adcbd4d5134 100644
--- a/tests/ui/almost_complete_letter_range.fixed
+++ b/tests/ui/almost_complete_letter_range.fixed
@@ -2,7 +2,6 @@
 // edition:2018
 // aux-build:macro_rules.rs
 
-#![feature(custom_inner_attributes)]
 #![feature(exclusive_range_pattern)]
 #![feature(stmt_expr_attributes)]
 #![warn(clippy::almost_complete_letter_range)]
@@ -62,16 +61,16 @@ fn main() {
     b!();
 }
 
+#[clippy::msrv = "1.25"]
 fn _under_msrv() {
-    #![clippy::msrv = "1.25"]
     let _ = match 'a' {
         'a'...'z' => 1,
         _ => 2,
     };
 }
 
+#[clippy::msrv = "1.26"]
 fn _meets_msrv() {
-    #![clippy::msrv = "1.26"]
     let _ = 'a'..='z';
     let _ = match 'a' {
         'a'..='z' => 1,
diff --git a/tests/ui/almost_complete_letter_range.rs b/tests/ui/almost_complete_letter_range.rs
index a66900a976e..9979316eca4 100644
--- a/tests/ui/almost_complete_letter_range.rs
+++ b/tests/ui/almost_complete_letter_range.rs
@@ -2,7 +2,6 @@
 // edition:2018
 // aux-build:macro_rules.rs
 
-#![feature(custom_inner_attributes)]
 #![feature(exclusive_range_pattern)]
 #![feature(stmt_expr_attributes)]
 #![warn(clippy::almost_complete_letter_range)]
@@ -62,16 +61,16 @@ fn main() {
     b!();
 }
 
+#[clippy::msrv = "1.25"]
 fn _under_msrv() {
-    #![clippy::msrv = "1.25"]
     let _ = match 'a' {
         'a'..'z' => 1,
         _ => 2,
     };
 }
 
+#[clippy::msrv = "1.26"]
 fn _meets_msrv() {
-    #![clippy::msrv = "1.26"]
     let _ = 'a'..'z';
     let _ = match 'a' {
         'a'..'z' => 1,
diff --git a/tests/ui/almost_complete_letter_range.stderr b/tests/ui/almost_complete_letter_range.stderr
index 3de44c72c1b..9abf6d6c5e7 100644
--- a/tests/ui/almost_complete_letter_range.stderr
+++ b/tests/ui/almost_complete_letter_range.stderr
@@ -1,5 +1,5 @@
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:30:17
+  --> $DIR/almost_complete_letter_range.rs:29:17
    |
 LL |         let _ = ('a') ..'z';
    |                 ^^^^^^--^^^
@@ -9,7 +9,7 @@ LL |         let _ = ('a') ..'z';
    = note: `-D clippy::almost-complete-letter-range` implied by `-D warnings`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:31:17
+  --> $DIR/almost_complete_letter_range.rs:30:17
    |
 LL |         let _ = 'A' .. ('Z');
    |                 ^^^^--^^^^^^
@@ -17,7 +17,7 @@ LL |         let _ = 'A' .. ('Z');
    |                     help: use an inclusive range: `..=`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:37:13
+  --> $DIR/almost_complete_letter_range.rs:36:13
    |
 LL |     let _ = (b'a')..(b'z');
    |             ^^^^^^--^^^^^^
@@ -25,7 +25,7 @@ LL |     let _ = (b'a')..(b'z');
    |                   help: use an inclusive range: `..=`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:38:13
+  --> $DIR/almost_complete_letter_range.rs:37:13
    |
 LL |     let _ = b'A'..b'Z';
    |             ^^^^--^^^^
@@ -33,7 +33,7 @@ LL |     let _ = b'A'..b'Z';
    |                 help: use an inclusive range: `..=`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:43:13
+  --> $DIR/almost_complete_letter_range.rs:42:13
    |
 LL |     let _ = a!()..'z';
    |             ^^^^--^^^
@@ -41,7 +41,7 @@ LL |     let _ = a!()..'z';
    |                 help: use an inclusive range: `..=`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:46:9
+  --> $DIR/almost_complete_letter_range.rs:45:9
    |
 LL |         b'a'..b'z' if true => 1,
    |         ^^^^--^^^^
@@ -49,7 +49,7 @@ LL |         b'a'..b'z' if true => 1,
    |             help: use an inclusive range: `..=`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:47:9
+  --> $DIR/almost_complete_letter_range.rs:46:9
    |
 LL |         b'A'..b'Z' if true => 2,
    |         ^^^^--^^^^
@@ -57,7 +57,7 @@ LL |         b'A'..b'Z' if true => 2,
    |             help: use an inclusive range: `..=`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:54:9
+  --> $DIR/almost_complete_letter_range.rs:53:9
    |
 LL |         'a'..'z' if true => 1,
    |         ^^^--^^^
@@ -65,7 +65,7 @@ LL |         'a'..'z' if true => 1,
    |            help: use an inclusive range: `..=`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:55:9
+  --> $DIR/almost_complete_letter_range.rs:54:9
    |
 LL |         'A'..'Z' if true => 2,
    |         ^^^--^^^
@@ -73,7 +73,7 @@ LL |         'A'..'Z' if true => 2,
    |            help: use an inclusive range: `..=`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:23:17
+  --> $DIR/almost_complete_letter_range.rs:22:17
    |
 LL |         let _ = 'a'..'z';
    |                 ^^^--^^^
@@ -86,7 +86,7 @@ LL |     b!();
    = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:68:9
+  --> $DIR/almost_complete_letter_range.rs:67:9
    |
 LL |         'a'..'z' => 1,
    |         ^^^--^^^
@@ -94,7 +94,7 @@ LL |         'a'..'z' => 1,
    |            help: use an inclusive range: `...`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:75:13
+  --> $DIR/almost_complete_letter_range.rs:74:13
    |
 LL |     let _ = 'a'..'z';
    |             ^^^--^^^
@@ -102,7 +102,7 @@ LL |     let _ = 'a'..'z';
    |                help: use an inclusive range: `..=`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:77:9
+  --> $DIR/almost_complete_letter_range.rs:76:9
    |
 LL |         'a'..'z' => 1,
    |         ^^^--^^^
diff --git a/tests/ui/cast_abs_to_unsigned.fixed b/tests/ui/cast_abs_to_unsigned.fixed
index e6bf944c7a5..8676b562b4f 100644
--- a/tests/ui/cast_abs_to_unsigned.fixed
+++ b/tests/ui/cast_abs_to_unsigned.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::cast_abs_to_unsigned)]
 #![allow(clippy::uninlined_format_args, unused)]
 
@@ -33,16 +32,14 @@ fn main() {
     let _ = (x as i64 - y as i64).unsigned_abs() as u32;
 }
 
+#[clippy::msrv = "1.50"]
 fn msrv_1_50() {
-    #![clippy::msrv = "1.50"]
-
     let x: i32 = 10;
     assert_eq!(10u32, x.abs() as u32);
 }
 
+#[clippy::msrv = "1.51"]
 fn msrv_1_51() {
-    #![clippy::msrv = "1.51"]
-
     let x: i32 = 10;
     assert_eq!(10u32, x.unsigned_abs());
 }
diff --git a/tests/ui/cast_abs_to_unsigned.rs b/tests/ui/cast_abs_to_unsigned.rs
index c87320b5209..5775af874f8 100644
--- a/tests/ui/cast_abs_to_unsigned.rs
+++ b/tests/ui/cast_abs_to_unsigned.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::cast_abs_to_unsigned)]
 #![allow(clippy::uninlined_format_args, unused)]
 
@@ -33,16 +32,14 @@ fn main() {
     let _ = (x as i64 - y as i64).abs() as u32;
 }
 
+#[clippy::msrv = "1.50"]
 fn msrv_1_50() {
-    #![clippy::msrv = "1.50"]
-
     let x: i32 = 10;
     assert_eq!(10u32, x.abs() as u32);
 }
 
+#[clippy::msrv = "1.51"]
 fn msrv_1_51() {
-    #![clippy::msrv = "1.51"]
-
     let x: i32 = 10;
     assert_eq!(10u32, x.abs() as u32);
 }
diff --git a/tests/ui/cast_abs_to_unsigned.stderr b/tests/ui/cast_abs_to_unsigned.stderr
index 1b39c554b03..4668554f451 100644
--- a/tests/ui/cast_abs_to_unsigned.stderr
+++ b/tests/ui/cast_abs_to_unsigned.stderr
@@ -1,5 +1,5 @@
 error: casting the result of `i32::abs()` to u32
-  --> $DIR/cast_abs_to_unsigned.rs:9:18
+  --> $DIR/cast_abs_to_unsigned.rs:8:18
    |
 LL |     let y: u32 = x.abs() as u32;
    |                  ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()`
@@ -7,103 +7,103 @@ LL |     let y: u32 = x.abs() as u32;
    = note: `-D clippy::cast-abs-to-unsigned` implied by `-D warnings`
 
 error: casting the result of `i32::abs()` to usize
-  --> $DIR/cast_abs_to_unsigned.rs:13:20
+  --> $DIR/cast_abs_to_unsigned.rs:12:20
    |
 LL |     let _: usize = a.abs() as usize;
    |                    ^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `i32::abs()` to usize
-  --> $DIR/cast_abs_to_unsigned.rs:14:20
+  --> $DIR/cast_abs_to_unsigned.rs:13:20
    |
 LL |     let _: usize = a.abs() as _;
    |                    ^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `i32::abs()` to usize
-  --> $DIR/cast_abs_to_unsigned.rs:15:13
+  --> $DIR/cast_abs_to_unsigned.rs:14:13
    |
 LL |     let _ = a.abs() as usize;
    |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `i64::abs()` to usize
-  --> $DIR/cast_abs_to_unsigned.rs:18:13
+  --> $DIR/cast_abs_to_unsigned.rs:17:13
    |
 LL |     let _ = a.abs() as usize;
    |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `i64::abs()` to u8
-  --> $DIR/cast_abs_to_unsigned.rs:19:13
+  --> $DIR/cast_abs_to_unsigned.rs:18:13
    |
 LL |     let _ = a.abs() as u8;
    |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `i64::abs()` to u16
-  --> $DIR/cast_abs_to_unsigned.rs:20:13
+  --> $DIR/cast_abs_to_unsigned.rs:19:13
    |
 LL |     let _ = a.abs() as u16;
    |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `i64::abs()` to u32
-  --> $DIR/cast_abs_to_unsigned.rs:21:13
+  --> $DIR/cast_abs_to_unsigned.rs:20:13
    |
 LL |     let _ = a.abs() as u32;
    |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `i64::abs()` to u64
-  --> $DIR/cast_abs_to_unsigned.rs:22:13
+  --> $DIR/cast_abs_to_unsigned.rs:21:13
    |
 LL |     let _ = a.abs() as u64;
    |             ^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `i64::abs()` to u128
-  --> $DIR/cast_abs_to_unsigned.rs:23:13
+  --> $DIR/cast_abs_to_unsigned.rs:22:13
    |
 LL |     let _ = a.abs() as u128;
    |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `isize::abs()` to usize
-  --> $DIR/cast_abs_to_unsigned.rs:26:13
+  --> $DIR/cast_abs_to_unsigned.rs:25:13
    |
 LL |     let _ = a.abs() as usize;
    |             ^^^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `isize::abs()` to u8
-  --> $DIR/cast_abs_to_unsigned.rs:27:13
+  --> $DIR/cast_abs_to_unsigned.rs:26:13
    |
 LL |     let _ = a.abs() as u8;
    |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `isize::abs()` to u16
-  --> $DIR/cast_abs_to_unsigned.rs:28:13
+  --> $DIR/cast_abs_to_unsigned.rs:27:13
    |
 LL |     let _ = a.abs() as u16;
    |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `isize::abs()` to u32
-  --> $DIR/cast_abs_to_unsigned.rs:29:13
+  --> $DIR/cast_abs_to_unsigned.rs:28:13
    |
 LL |     let _ = a.abs() as u32;
    |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `isize::abs()` to u64
-  --> $DIR/cast_abs_to_unsigned.rs:30:13
+  --> $DIR/cast_abs_to_unsigned.rs:29:13
    |
 LL |     let _ = a.abs() as u64;
    |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `isize::abs()` to u128
-  --> $DIR/cast_abs_to_unsigned.rs:31:13
+  --> $DIR/cast_abs_to_unsigned.rs:30:13
    |
 LL |     let _ = a.abs() as u128;
    |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
 
 error: casting the result of `i64::abs()` to u32
-  --> $DIR/cast_abs_to_unsigned.rs:33:13
+  --> $DIR/cast_abs_to_unsigned.rs:32:13
    |
 LL |     let _ = (x as i64 - y as i64).abs() as u32;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `(x as i64 - y as i64).unsigned_abs()`
 
 error: casting the result of `i32::abs()` to u32
-  --> $DIR/cast_abs_to_unsigned.rs:47:23
+  --> $DIR/cast_abs_to_unsigned.rs:44:23
    |
 LL |     assert_eq!(10u32, x.abs() as u32);
    |                       ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()`
diff --git a/tests/ui/cast_lossless_bool.fixed b/tests/ui/cast_lossless_bool.fixed
index af13b755e31..13b3cf838c9 100644
--- a/tests/ui/cast_lossless_bool.fixed
+++ b/tests/ui/cast_lossless_bool.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![allow(dead_code)]
 #![warn(clippy::cast_lossless)]
 
@@ -42,14 +41,12 @@ mod cast_lossless_in_impl {
     }
 }
 
+#[clippy::msrv = "1.27"]
 fn msrv_1_27() {
-    #![clippy::msrv = "1.27"]
-
     let _ = true as u8;
 }
 
+#[clippy::msrv = "1.28"]
 fn msrv_1_28() {
-    #![clippy::msrv = "1.28"]
-
     let _ = u8::from(true);
 }
diff --git a/tests/ui/cast_lossless_bool.rs b/tests/ui/cast_lossless_bool.rs
index 3b06af899c6..3eed2135562 100644
--- a/tests/ui/cast_lossless_bool.rs
+++ b/tests/ui/cast_lossless_bool.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![allow(dead_code)]
 #![warn(clippy::cast_lossless)]
 
@@ -42,14 +41,12 @@ mod cast_lossless_in_impl {
     }
 }
 
+#[clippy::msrv = "1.27"]
 fn msrv_1_27() {
-    #![clippy::msrv = "1.27"]
-
     let _ = true as u8;
 }
 
+#[clippy::msrv = "1.28"]
 fn msrv_1_28() {
-    #![clippy::msrv = "1.28"]
-
     let _ = true as u8;
 }
diff --git a/tests/ui/cast_lossless_bool.stderr b/tests/ui/cast_lossless_bool.stderr
index 768b033d10a..ce240b70f89 100644
--- a/tests/ui/cast_lossless_bool.stderr
+++ b/tests/ui/cast_lossless_bool.stderr
@@ -1,5 +1,5 @@
 error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)`
-  --> $DIR/cast_lossless_bool.rs:9:13
+  --> $DIR/cast_lossless_bool.rs:8:13
    |
 LL |     let _ = true as u8;
    |             ^^^^^^^^^^ help: try: `u8::from(true)`
@@ -7,79 +7,79 @@ LL |     let _ = true as u8;
    = note: `-D clippy::cast-lossless` implied by `-D warnings`
 
 error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)`
-  --> $DIR/cast_lossless_bool.rs:10:13
+  --> $DIR/cast_lossless_bool.rs:9:13
    |
 LL |     let _ = true as u16;
    |             ^^^^^^^^^^^ help: try: `u16::from(true)`
 
 error: casting `bool` to `u32` is more cleanly stated with `u32::from(_)`
-  --> $DIR/cast_lossless_bool.rs:11:13
+  --> $DIR/cast_lossless_bool.rs:10:13
    |
 LL |     let _ = true as u32;
    |             ^^^^^^^^^^^ help: try: `u32::from(true)`
 
 error: casting `bool` to `u64` is more cleanly stated with `u64::from(_)`
-  --> $DIR/cast_lossless_bool.rs:12:13
+  --> $DIR/cast_lossless_bool.rs:11:13
    |
 LL |     let _ = true as u64;
    |             ^^^^^^^^^^^ help: try: `u64::from(true)`
 
 error: casting `bool` to `u128` is more cleanly stated with `u128::from(_)`
-  --> $DIR/cast_lossless_bool.rs:13:13
+  --> $DIR/cast_lossless_bool.rs:12:13
    |
 LL |     let _ = true as u128;
    |             ^^^^^^^^^^^^ help: try: `u128::from(true)`
 
 error: casting `bool` to `usize` is more cleanly stated with `usize::from(_)`
-  --> $DIR/cast_lossless_bool.rs:14:13
+  --> $DIR/cast_lossless_bool.rs:13:13
    |
 LL |     let _ = true as usize;
    |             ^^^^^^^^^^^^^ help: try: `usize::from(true)`
 
 error: casting `bool` to `i8` is more cleanly stated with `i8::from(_)`
-  --> $DIR/cast_lossless_bool.rs:16:13
+  --> $DIR/cast_lossless_bool.rs:15:13
    |
 LL |     let _ = true as i8;
    |             ^^^^^^^^^^ help: try: `i8::from(true)`
 
 error: casting `bool` to `i16` is more cleanly stated with `i16::from(_)`
-  --> $DIR/cast_lossless_bool.rs:17:13
+  --> $DIR/cast_lossless_bool.rs:16:13
    |
 LL |     let _ = true as i16;
    |             ^^^^^^^^^^^ help: try: `i16::from(true)`
 
 error: casting `bool` to `i32` is more cleanly stated with `i32::from(_)`
-  --> $DIR/cast_lossless_bool.rs:18:13
+  --> $DIR/cast_lossless_bool.rs:17:13
    |
 LL |     let _ = true as i32;
    |             ^^^^^^^^^^^ help: try: `i32::from(true)`
 
 error: casting `bool` to `i64` is more cleanly stated with `i64::from(_)`
-  --> $DIR/cast_lossless_bool.rs:19:13
+  --> $DIR/cast_lossless_bool.rs:18:13
    |
 LL |     let _ = true as i64;
    |             ^^^^^^^^^^^ help: try: `i64::from(true)`
 
 error: casting `bool` to `i128` is more cleanly stated with `i128::from(_)`
-  --> $DIR/cast_lossless_bool.rs:20:13
+  --> $DIR/cast_lossless_bool.rs:19:13
    |
 LL |     let _ = true as i128;
    |             ^^^^^^^^^^^^ help: try: `i128::from(true)`
 
 error: casting `bool` to `isize` is more cleanly stated with `isize::from(_)`
-  --> $DIR/cast_lossless_bool.rs:21:13
+  --> $DIR/cast_lossless_bool.rs:20:13
    |
 LL |     let _ = true as isize;
    |             ^^^^^^^^^^^^^ help: try: `isize::from(true)`
 
 error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)`
-  --> $DIR/cast_lossless_bool.rs:24:13
+  --> $DIR/cast_lossless_bool.rs:23:13
    |
 LL |     let _ = (true | false) as u16;
    |             ^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::from(true | false)`
 
 error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)`
-  --> $DIR/cast_lossless_bool.rs:54:13
+  --> $DIR/cast_lossless_bool.rs:51:13
    |
 LL |     let _ = true as u8;
    |             ^^^^^^^^^^ help: try: `u8::from(true)`
diff --git a/tests/ui/cfg_attr_rustfmt.fixed b/tests/ui/cfg_attr_rustfmt.fixed
index 8a5645b22ed..b970b1209b6 100644
--- a/tests/ui/cfg_attr_rustfmt.fixed
+++ b/tests/ui/cfg_attr_rustfmt.fixed
@@ -1,5 +1,5 @@
 // run-rustfix
-#![feature(stmt_expr_attributes, custom_inner_attributes)]
+#![feature(stmt_expr_attributes)]
 
 #![allow(unused, clippy::no_effect, clippy::unnecessary_operation)]
 #![warn(clippy::deprecated_cfg_attr)]
@@ -30,16 +30,14 @@ mod foo {
     pub fn f() {}
 }
 
+#[clippy::msrv = "1.29"]
 fn msrv_1_29() {
-    #![clippy::msrv = "1.29"]
-
     #[cfg_attr(rustfmt, rustfmt::skip)]
     1+29;
 }
 
+#[clippy::msrv = "1.30"]
 fn msrv_1_30() {
-    #![clippy::msrv = "1.30"]
-
     #[rustfmt::skip]
     1+30;
 }
diff --git a/tests/ui/cfg_attr_rustfmt.rs b/tests/ui/cfg_attr_rustfmt.rs
index 2fb140efae7..0a8e6a89d8a 100644
--- a/tests/ui/cfg_attr_rustfmt.rs
+++ b/tests/ui/cfg_attr_rustfmt.rs
@@ -1,5 +1,5 @@
 // run-rustfix
-#![feature(stmt_expr_attributes, custom_inner_attributes)]
+#![feature(stmt_expr_attributes)]
 
 #![allow(unused, clippy::no_effect, clippy::unnecessary_operation)]
 #![warn(clippy::deprecated_cfg_attr)]
@@ -30,16 +30,14 @@ mod foo {
     pub fn f() {}
 }
 
+#[clippy::msrv = "1.29"]
 fn msrv_1_29() {
-    #![clippy::msrv = "1.29"]
-
     #[cfg_attr(rustfmt, rustfmt::skip)]
     1+29;
 }
 
+#[clippy::msrv = "1.30"]
 fn msrv_1_30() {
-    #![clippy::msrv = "1.30"]
-
     #[cfg_attr(rustfmt, rustfmt::skip)]
     1+30;
 }
diff --git a/tests/ui/cfg_attr_rustfmt.stderr b/tests/ui/cfg_attr_rustfmt.stderr
index 08df7b2b39a..524a2bf7248 100644
--- a/tests/ui/cfg_attr_rustfmt.stderr
+++ b/tests/ui/cfg_attr_rustfmt.stderr
@@ -13,7 +13,7 @@ LL | #[cfg_attr(rustfmt, rustfmt_skip)]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]`
 
 error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes
-  --> $DIR/cfg_attr_rustfmt.rs:43:5
+  --> $DIR/cfg_attr_rustfmt.rs:41:5
    |
 LL |     #[cfg_attr(rustfmt, rustfmt::skip)]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]`
diff --git a/tests/ui/checked_conversions.fixed b/tests/ui/checked_conversions.fixed
index f936957cb40..e279ba3147b 100644
--- a/tests/ui/checked_conversions.fixed
+++ b/tests/ui/checked_conversions.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![allow(
     clippy::cast_lossless,
     unused,
@@ -78,16 +77,14 @@ pub const fn issue_8898(i: u32) -> bool {
     i <= i32::MAX as u32
 }
 
+#[clippy::msrv = "1.33"]
 fn msrv_1_33() {
-    #![clippy::msrv = "1.33"]
-
     let value: i64 = 33;
     let _ = value <= (u32::MAX as i64) && value >= 0;
 }
 
+#[clippy::msrv = "1.34"]
 fn msrv_1_34() {
-    #![clippy::msrv = "1.34"]
-
     let value: i64 = 34;
     let _ = u32::try_from(value).is_ok();
 }
diff --git a/tests/ui/checked_conversions.rs b/tests/ui/checked_conversions.rs
index 77aec713ff3..9d7a40995c3 100644
--- a/tests/ui/checked_conversions.rs
+++ b/tests/ui/checked_conversions.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![allow(
     clippy::cast_lossless,
     unused,
@@ -78,16 +77,14 @@ pub const fn issue_8898(i: u32) -> bool {
     i <= i32::MAX as u32
 }
 
+#[clippy::msrv = "1.33"]
 fn msrv_1_33() {
-    #![clippy::msrv = "1.33"]
-
     let value: i64 = 33;
     let _ = value <= (u32::MAX as i64) && value >= 0;
 }
 
+#[clippy::msrv = "1.34"]
 fn msrv_1_34() {
-    #![clippy::msrv = "1.34"]
-
     let value: i64 = 34;
     let _ = value <= (u32::MAX as i64) && value >= 0;
 }
diff --git a/tests/ui/checked_conversions.stderr b/tests/ui/checked_conversions.stderr
index b2bf7af8daf..273ead73bda 100644
--- a/tests/ui/checked_conversions.stderr
+++ b/tests/ui/checked_conversions.stderr
@@ -1,5 +1,5 @@
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:17:13
+  --> $DIR/checked_conversions.rs:16:13
    |
 LL |     let _ = value <= (u32::max_value() as i64) && value >= 0;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()`
@@ -7,97 +7,97 @@ LL |     let _ = value <= (u32::max_value() as i64) && value >= 0;
    = note: `-D clippy::checked-conversions` implied by `-D warnings`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:18:13
+  --> $DIR/checked_conversions.rs:17:13
    |
 LL |     let _ = value <= (u32::MAX as i64) && value >= 0;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:22:13
+  --> $DIR/checked_conversions.rs:21:13
    |
 LL |     let _ = value <= i64::from(u16::max_value()) && value >= 0;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:23:13
+  --> $DIR/checked_conversions.rs:22:13
    |
 LL |     let _ = value <= i64::from(u16::MAX) && value >= 0;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:27:13
+  --> $DIR/checked_conversions.rs:26:13
    |
 LL |     let _ = value <= (u8::max_value() as isize) && value >= 0;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:28:13
+  --> $DIR/checked_conversions.rs:27:13
    |
 LL |     let _ = value <= (u8::MAX as isize) && value >= 0;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:34:13
+  --> $DIR/checked_conversions.rs:33:13
    |
 LL |     let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:35:13
+  --> $DIR/checked_conversions.rs:34:13
    |
 LL |     let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:39:13
+  --> $DIR/checked_conversions.rs:38:13
    |
 LL |     let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:40:13
+  --> $DIR/checked_conversions.rs:39:13
    |
 LL |     let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:46:13
+  --> $DIR/checked_conversions.rs:45:13
    |
 LL |     let _ = value <= i32::max_value() as u32;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:47:13
+  --> $DIR/checked_conversions.rs:46:13
    |
 LL |     let _ = value <= i32::MAX as u32;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:51:13
+  --> $DIR/checked_conversions.rs:50:13
    |
 LL |     let _ = value <= isize::max_value() as usize && value as i32 == 5;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:52:13
+  --> $DIR/checked_conversions.rs:51:13
    |
 LL |     let _ = value <= isize::MAX as usize && value as i32 == 5;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:56:13
+  --> $DIR/checked_conversions.rs:55:13
    |
 LL |     let _ = value <= u16::max_value() as u32 && value as i32 == 5;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:57:13
+  --> $DIR/checked_conversions.rs:56:13
    |
 LL |     let _ = value <= u16::MAX as u32 && value as i32 == 5;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> $DIR/checked_conversions.rs:92:13
+  --> $DIR/checked_conversions.rs:89:13
    |
 LL |     let _ = value <= (u32::MAX as i64) && value >= 0;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()`
diff --git a/tests/ui/cloned_instead_of_copied.fixed b/tests/ui/cloned_instead_of_copied.fixed
index 42ed232d100..ecbfc1feedf 100644
--- a/tests/ui/cloned_instead_of_copied.fixed
+++ b/tests/ui/cloned_instead_of_copied.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::cloned_instead_of_copied)]
 #![allow(unused)]
 
@@ -17,23 +16,20 @@ fn main() {
     let _ = Some(&String::new()).cloned();
 }
 
+#[clippy::msrv = "1.34"]
 fn msrv_1_34() {
-    #![clippy::msrv = "1.34"]
-
     let _ = [1].iter().cloned();
     let _ = Some(&1).cloned();
 }
 
+#[clippy::msrv = "1.35"]
 fn msrv_1_35() {
-    #![clippy::msrv = "1.35"]
-
     let _ = [1].iter().cloned();
     let _ = Some(&1).copied(); // Option::copied needs 1.35
 }
 
+#[clippy::msrv = "1.36"]
 fn msrv_1_36() {
-    #![clippy::msrv = "1.36"]
-
     let _ = [1].iter().copied(); // Iterator::copied needs 1.36
     let _ = Some(&1).copied();
 }
diff --git a/tests/ui/cloned_instead_of_copied.rs b/tests/ui/cloned_instead_of_copied.rs
index 471bd9654cc..163dc3dddce 100644
--- a/tests/ui/cloned_instead_of_copied.rs
+++ b/tests/ui/cloned_instead_of_copied.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::cloned_instead_of_copied)]
 #![allow(unused)]
 
@@ -17,23 +16,20 @@ fn main() {
     let _ = Some(&String::new()).cloned();
 }
 
+#[clippy::msrv = "1.34"]
 fn msrv_1_34() {
-    #![clippy::msrv = "1.34"]
-
     let _ = [1].iter().cloned();
     let _ = Some(&1).cloned();
 }
 
+#[clippy::msrv = "1.35"]
 fn msrv_1_35() {
-    #![clippy::msrv = "1.35"]
-
     let _ = [1].iter().cloned();
     let _ = Some(&1).cloned(); // Option::copied needs 1.35
 }
 
+#[clippy::msrv = "1.36"]
 fn msrv_1_36() {
-    #![clippy::msrv = "1.36"]
-
     let _ = [1].iter().cloned(); // Iterator::copied needs 1.36
     let _ = Some(&1).cloned();
 }
diff --git a/tests/ui/cloned_instead_of_copied.stderr b/tests/ui/cloned_instead_of_copied.stderr
index 914c9a91e83..e0361acd956 100644
--- a/tests/ui/cloned_instead_of_copied.stderr
+++ b/tests/ui/cloned_instead_of_copied.stderr
@@ -1,5 +1,5 @@
 error: used `cloned` where `copied` could be used instead
-  --> $DIR/cloned_instead_of_copied.rs:9:24
+  --> $DIR/cloned_instead_of_copied.rs:8:24
    |
 LL |     let _ = [1].iter().cloned();
    |                        ^^^^^^ help: try: `copied`
@@ -7,43 +7,43 @@ LL |     let _ = [1].iter().cloned();
    = note: `-D clippy::cloned-instead-of-copied` implied by `-D warnings`
 
 error: used `cloned` where `copied` could be used instead
-  --> $DIR/cloned_instead_of_copied.rs:10:31
+  --> $DIR/cloned_instead_of_copied.rs:9:31
    |
 LL |     let _ = vec!["hi"].iter().cloned();
    |                               ^^^^^^ help: try: `copied`
 
 error: used `cloned` where `copied` could be used instead
-  --> $DIR/cloned_instead_of_copied.rs:11:22
+  --> $DIR/cloned_instead_of_copied.rs:10:22
    |
 LL |     let _ = Some(&1).cloned();
    |                      ^^^^^^ help: try: `copied`
 
 error: used `cloned` where `copied` could be used instead
-  --> $DIR/cloned_instead_of_copied.rs:12:34
+  --> $DIR/cloned_instead_of_copied.rs:11:34
    |
 LL |     let _ = Box::new([1].iter()).cloned();
    |                                  ^^^^^^ help: try: `copied`
 
 error: used `cloned` where `copied` could be used instead
-  --> $DIR/cloned_instead_of_copied.rs:13:32
+  --> $DIR/cloned_instead_of_copied.rs:12:32
    |
 LL |     let _ = Box::new(Some(&1)).cloned();
    |                                ^^^^^^ help: try: `copied`
 
 error: used `cloned` where `copied` could be used instead
-  --> $DIR/cloned_instead_of_copied.rs:31:22
+  --> $DIR/cloned_instead_of_copied.rs:28:22
    |
 LL |     let _ = Some(&1).cloned(); // Option::copied needs 1.35
    |                      ^^^^^^ help: try: `copied`
 
 error: used `cloned` where `copied` could be used instead
-  --> $DIR/cloned_instead_of_copied.rs:37:24
+  --> $DIR/cloned_instead_of_copied.rs:33:24
    |
 LL |     let _ = [1].iter().cloned(); // Iterator::copied needs 1.36
    |                        ^^^^^^ help: try: `copied`
 
 error: used `cloned` where `copied` could be used instead
-  --> $DIR/cloned_instead_of_copied.rs:38:22
+  --> $DIR/cloned_instead_of_copied.rs:34:22
    |
 LL |     let _ = Some(&1).cloned();
    |                      ^^^^^^ help: try: `copied`
diff --git a/tests/ui/derive.rs b/tests/ui/derive.rs
index b276c384c04..c629c0e5353 100644
--- a/tests/ui/derive.rs
+++ b/tests/ui/derive.rs
@@ -1,7 +1,6 @@
 #![allow(dead_code)]
 #![warn(clippy::expl_impl_clone_on_copy)]
 
-
 #[derive(Copy)]
 struct Qux;
 
diff --git a/tests/ui/derive.stderr b/tests/ui/derive.stderr
index e1fbb8dcd1e..d37f7fa7331 100644
--- a/tests/ui/derive.stderr
+++ b/tests/ui/derive.stderr
@@ -1,5 +1,5 @@
 error: you are implementing `Clone` explicitly on a `Copy` type
-  --> $DIR/derive.rs:8:1
+  --> $DIR/derive.rs:7:1
    |
 LL | / impl Clone for Qux {
 LL | |     fn clone(&self) -> Self {
@@ -9,7 +9,7 @@ LL | | }
    | |_^
    |
 note: consider deriving `Clone` or removing `Copy`
-  --> $DIR/derive.rs:8:1
+  --> $DIR/derive.rs:7:1
    |
 LL | / impl Clone for Qux {
 LL | |     fn clone(&self) -> Self {
@@ -20,7 +20,7 @@ LL | | }
    = note: `-D clippy::expl-impl-clone-on-copy` implied by `-D warnings`
 
 error: you are implementing `Clone` explicitly on a `Copy` type
-  --> $DIR/derive.rs:32:1
+  --> $DIR/derive.rs:31:1
    |
 LL | / impl<'a> Clone for Lt<'a> {
 LL | |     fn clone(&self) -> Self {
@@ -30,7 +30,7 @@ LL | | }
    | |_^
    |
 note: consider deriving `Clone` or removing `Copy`
-  --> $DIR/derive.rs:32:1
+  --> $DIR/derive.rs:31:1
    |
 LL | / impl<'a> Clone for Lt<'a> {
 LL | |     fn clone(&self) -> Self {
@@ -40,7 +40,7 @@ LL | | }
    | |_^
 
 error: you are implementing `Clone` explicitly on a `Copy` type
-  --> $DIR/derive.rs:43:1
+  --> $DIR/derive.rs:42:1
    |
 LL | / impl Clone for BigArray {
 LL | |     fn clone(&self) -> Self {
@@ -50,7 +50,7 @@ LL | | }
    | |_^
    |
 note: consider deriving `Clone` or removing `Copy`
-  --> $DIR/derive.rs:43:1
+  --> $DIR/derive.rs:42:1
    |
 LL | / impl Clone for BigArray {
 LL | |     fn clone(&self) -> Self {
@@ -60,7 +60,7 @@ LL | | }
    | |_^
 
 error: you are implementing `Clone` explicitly on a `Copy` type
-  --> $DIR/derive.rs:54:1
+  --> $DIR/derive.rs:53:1
    |
 LL | / impl Clone for FnPtr {
 LL | |     fn clone(&self) -> Self {
@@ -70,7 +70,7 @@ LL | | }
    | |_^
    |
 note: consider deriving `Clone` or removing `Copy`
-  --> $DIR/derive.rs:54:1
+  --> $DIR/derive.rs:53:1
    |
 LL | / impl Clone for FnPtr {
 LL | |     fn clone(&self) -> Self {
@@ -80,7 +80,7 @@ LL | | }
    | |_^
 
 error: you are implementing `Clone` explicitly on a `Copy` type
-  --> $DIR/derive.rs:74:1
+  --> $DIR/derive.rs:73:1
    |
 LL | / impl<T: Clone> Clone for Generic2<T> {
 LL | |     fn clone(&self) -> Self {
@@ -90,7 +90,7 @@ LL | | }
    | |_^
    |
 note: consider deriving `Clone` or removing `Copy`
-  --> $DIR/derive.rs:74:1
+  --> $DIR/derive.rs:73:1
    |
 LL | / impl<T: Clone> Clone for Generic2<T> {
 LL | |     fn clone(&self) -> Self {
diff --git a/tests/ui/err_expect.fixed b/tests/ui/err_expect.fixed
index 3bac738acd6..b63cbd8a8e6 100644
--- a/tests/ui/err_expect.fixed
+++ b/tests/ui/err_expect.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![allow(unused)]
 
 struct MyTypeNonDebug;
@@ -16,16 +15,14 @@ fn main() {
     test_non_debug.err().expect("Testing non debug type");
 }
 
+#[clippy::msrv = "1.16"]
 fn msrv_1_16() {
-    #![clippy::msrv = "1.16"]
-
     let x: Result<u32, &str> = Ok(16);
     x.err().expect("16");
 }
 
+#[clippy::msrv = "1.17"]
 fn msrv_1_17() {
-    #![clippy::msrv = "1.17"]
-
     let x: Result<u32, &str> = Ok(17);
     x.expect_err("17");
 }
diff --git a/tests/ui/err_expect.rs b/tests/ui/err_expect.rs
index 6e7c47d9ad3..c081a745fb4 100644
--- a/tests/ui/err_expect.rs
+++ b/tests/ui/err_expect.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![allow(unused)]
 
 struct MyTypeNonDebug;
@@ -16,16 +15,14 @@ fn main() {
     test_non_debug.err().expect("Testing non debug type");
 }
 
+#[clippy::msrv = "1.16"]
 fn msrv_1_16() {
-    #![clippy::msrv = "1.16"]
-
     let x: Result<u32, &str> = Ok(16);
     x.err().expect("16");
 }
 
+#[clippy::msrv = "1.17"]
 fn msrv_1_17() {
-    #![clippy::msrv = "1.17"]
-
     let x: Result<u32, &str> = Ok(17);
     x.err().expect("17");
 }
diff --git a/tests/ui/err_expect.stderr b/tests/ui/err_expect.stderr
index 91a6cf8de65..82c0754cfb0 100644
--- a/tests/ui/err_expect.stderr
+++ b/tests/ui/err_expect.stderr
@@ -1,5 +1,5 @@
 error: called `.err().expect()` on a `Result` value
-  --> $DIR/err_expect.rs:13:16
+  --> $DIR/err_expect.rs:12:16
    |
 LL |     test_debug.err().expect("Testing debug type");
    |                ^^^^^^^^^^^^ help: try: `expect_err`
@@ -7,7 +7,7 @@ LL |     test_debug.err().expect("Testing debug type");
    = note: `-D clippy::err-expect` implied by `-D warnings`
 
 error: called `.err().expect()` on a `Result` value
-  --> $DIR/err_expect.rs:30:7
+  --> $DIR/err_expect.rs:27:7
    |
 LL |     x.err().expect("17");
    |       ^^^^^^^^^^^^ help: try: `expect_err`
diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed
index a9cc80aaaf6..dc129591eac 100644
--- a/tests/ui/eta.fixed
+++ b/tests/ui/eta.fixed
@@ -316,3 +316,25 @@ pub fn mutable_impl_fn_mut(mut f: impl FnMut(), mut f_used_once: impl FnMut()) -
 
     move || takes_fn_mut(&mut f_used_once)
 }
+
+impl dyn TestTrait + '_ {
+    fn method_on_dyn(&self) -> bool {
+        false
+    }
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/7746
+fn angle_brackets_and_substs() {
+    let array_opt: Option<&[u8; 3]> = Some(&[4, 8, 7]);
+    array_opt.map(<[u8; 3]>::as_slice);
+
+    let slice_opt: Option<&[u8]> = Some(b"slice");
+    slice_opt.map(<[u8]>::len);
+
+    let ptr_opt: Option<*const usize> = Some(&487);
+    ptr_opt.map(<*const usize>::is_null);
+
+    let test_struct = TestStruct { some_ref: &487 };
+    let dyn_opt: Option<&dyn TestTrait> = Some(&test_struct);
+    dyn_opt.map(<dyn TestTrait>::method_on_dyn);
+}
diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs
index cc99906ccd6..025fd6a0b7a 100644
--- a/tests/ui/eta.rs
+++ b/tests/ui/eta.rs
@@ -316,3 +316,25 @@ pub fn mutable_impl_fn_mut(mut f: impl FnMut(), mut f_used_once: impl FnMut()) -
 
     move || takes_fn_mut(|| f_used_once())
 }
+
+impl dyn TestTrait + '_ {
+    fn method_on_dyn(&self) -> bool {
+        false
+    }
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/7746
+fn angle_brackets_and_substs() {
+    let array_opt: Option<&[u8; 3]> = Some(&[4, 8, 7]);
+    array_opt.map(|a| a.as_slice());
+
+    let slice_opt: Option<&[u8]> = Some(b"slice");
+    slice_opt.map(|s| s.len());
+
+    let ptr_opt: Option<*const usize> = Some(&487);
+    ptr_opt.map(|p| p.is_null());
+
+    let test_struct = TestStruct { some_ref: &487 };
+    let dyn_opt: Option<&dyn TestTrait> = Some(&test_struct);
+    dyn_opt.map(|d| d.method_on_dyn());
+}
diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr
index 434706b7e25..a521fb86860 100644
--- a/tests/ui/eta.stderr
+++ b/tests/ui/eta.stderr
@@ -134,5 +134,29 @@ error: redundant closure
 LL |     move || takes_fn_mut(|| f_used_once())
    |                          ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut f_used_once`
 
-error: aborting due to 22 previous errors
+error: redundant closure
+  --> $DIR/eta.rs:329:19
+   |
+LL |     array_opt.map(|a| a.as_slice());
+   |                   ^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8; 3]>::as_slice`
+
+error: redundant closure
+  --> $DIR/eta.rs:332:19
+   |
+LL |     slice_opt.map(|s| s.len());
+   |                   ^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8]>::len`
+
+error: redundant closure
+  --> $DIR/eta.rs:335:17
+   |
+LL |     ptr_opt.map(|p| p.is_null());
+   |                 ^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<*const usize>::is_null`
+
+error: redundant closure
+  --> $DIR/eta.rs:339:17
+   |
+LL |     dyn_opt.map(|d| d.method_on_dyn());
+   |                 ^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<dyn TestTrait>::method_on_dyn`
+
+error: aborting due to 26 previous errors
 
diff --git a/tests/ui/expect_tool_lint_rfc_2383.rs b/tests/ui/expect_tool_lint_rfc_2383.rs
index 0415e33b3fa..018f875d60b 100644
--- a/tests/ui/expect_tool_lint_rfc_2383.rs
+++ b/tests/ui/expect_tool_lint_rfc_2383.rs
@@ -22,9 +22,9 @@ mod rustc_ok {
 
         #[expect(illegal_floating_point_literal_pattern)]
         match x {
-            5.0 => {}
-            6.0 => {}
-            _ => {}
+            5.0 => {},
+            6.0 => {},
+            _ => {},
         }
     }
 }
@@ -38,9 +38,9 @@ mod rustc_warn {
 
         #[expect(illegal_floating_point_literal_pattern)]
         match x {
-            5 => {}
-            6 => {}
-            _ => {}
+            5 => {},
+            6 => {},
+            _ => {},
         }
     }
 }
diff --git a/tests/ui/explicit_auto_deref.fixed b/tests/ui/explicit_auto_deref.fixed
index 59ff5e4040a..475fae5e823 100644
--- a/tests/ui/explicit_auto_deref.fixed
+++ b/tests/ui/explicit_auto_deref.fixed
@@ -277,4 +277,8 @@ fn main() {
         unimplemented!()
     }
     let _: String = takes_assoc(&*String::new());
+
+    // Issue #9901
+    fn takes_ref(_: &i32) {}
+    takes_ref(*Box::new(&0i32));
 }
diff --git a/tests/ui/explicit_auto_deref.rs b/tests/ui/explicit_auto_deref.rs
index bcfb60c3278..c1894258f4d 100644
--- a/tests/ui/explicit_auto_deref.rs
+++ b/tests/ui/explicit_auto_deref.rs
@@ -277,4 +277,8 @@ fn main() {
         unimplemented!()
     }
     let _: String = takes_assoc(&*String::new());
+
+    // Issue #9901
+    fn takes_ref(_: &i32) {}
+    takes_ref(*Box::new(&0i32));
 }
diff --git a/tests/ui/filter_map_next_fixable.fixed b/tests/ui/filter_map_next_fixable.fixed
index 41828ddd7ac..462d46169fc 100644
--- a/tests/ui/filter_map_next_fixable.fixed
+++ b/tests/ui/filter_map_next_fixable.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::all, clippy::pedantic)]
 #![allow(unused)]
 
@@ -11,16 +10,14 @@ fn main() {
     assert_eq!(element, Some(1));
 }
 
+#[clippy::msrv = "1.29"]
 fn msrv_1_29() {
-    #![clippy::msrv = "1.29"]
-
     let a = ["1", "lol", "3", "NaN", "5"];
     let _: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
 }
 
+#[clippy::msrv = "1.30"]
 fn msrv_1_30() {
-    #![clippy::msrv = "1.30"]
-
     let a = ["1", "lol", "3", "NaN", "5"];
     let _: Option<i32> = a.iter().find_map(|s| s.parse().ok());
 }
diff --git a/tests/ui/filter_map_next_fixable.rs b/tests/ui/filter_map_next_fixable.rs
index be492a81b45..2ea00cf7307 100644
--- a/tests/ui/filter_map_next_fixable.rs
+++ b/tests/ui/filter_map_next_fixable.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::all, clippy::pedantic)]
 #![allow(unused)]
 
@@ -11,16 +10,14 @@ fn main() {
     assert_eq!(element, Some(1));
 }
 
+#[clippy::msrv = "1.29"]
 fn msrv_1_29() {
-    #![clippy::msrv = "1.29"]
-
     let a = ["1", "lol", "3", "NaN", "5"];
     let _: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
 }
 
+#[clippy::msrv = "1.30"]
 fn msrv_1_30() {
-    #![clippy::msrv = "1.30"]
-
     let a = ["1", "lol", "3", "NaN", "5"];
     let _: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
 }
diff --git a/tests/ui/filter_map_next_fixable.stderr b/tests/ui/filter_map_next_fixable.stderr
index e789efeabd5..a9fc6abe88f 100644
--- a/tests/ui/filter_map_next_fixable.stderr
+++ b/tests/ui/filter_map_next_fixable.stderr
@@ -1,5 +1,5 @@
 error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead
-  --> $DIR/filter_map_next_fixable.rs:10:32
+  --> $DIR/filter_map_next_fixable.rs:9:32
    |
 LL |     let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
    |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())`
@@ -7,7 +7,7 @@ LL |     let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next
    = note: `-D clippy::filter-map-next` implied by `-D warnings`
 
 error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead
-  --> $DIR/filter_map_next_fixable.rs:25:26
+  --> $DIR/filter_map_next_fixable.rs:22:26
    |
 LL |     let _: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())`
diff --git a/tests/ui/from_over_into.fixed b/tests/ui/from_over_into.fixed
index 1cf49ca45f4..125c9a69cd3 100644
--- a/tests/ui/from_over_into.fixed
+++ b/tests/ui/from_over_into.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::from_over_into)]
 #![allow(unused)]
 
@@ -60,9 +59,8 @@ impl From<String> for A {
     }
 }
 
+#[clippy::msrv = "1.40"]
 fn msrv_1_40() {
-    #![clippy::msrv = "1.40"]
-
     struct FromOverInto<T>(Vec<T>);
 
     impl<T> Into<FromOverInto<T>> for Vec<T> {
@@ -72,9 +70,8 @@ fn msrv_1_40() {
     }
 }
 
+#[clippy::msrv = "1.41"]
 fn msrv_1_41() {
-    #![clippy::msrv = "1.41"]
-
     struct FromOverInto<T>(Vec<T>);
 
     impl<T> From<Vec<T>> for FromOverInto<T> {
diff --git a/tests/ui/from_over_into.rs b/tests/ui/from_over_into.rs
index d30f3c3fc92..5aa127bfabe 100644
--- a/tests/ui/from_over_into.rs
+++ b/tests/ui/from_over_into.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::from_over_into)]
 #![allow(unused)]
 
@@ -60,9 +59,8 @@ impl From<String> for A {
     }
 }
 
+#[clippy::msrv = "1.40"]
 fn msrv_1_40() {
-    #![clippy::msrv = "1.40"]
-
     struct FromOverInto<T>(Vec<T>);
 
     impl<T> Into<FromOverInto<T>> for Vec<T> {
@@ -72,9 +70,8 @@ fn msrv_1_40() {
     }
 }
 
+#[clippy::msrv = "1.41"]
 fn msrv_1_41() {
-    #![clippy::msrv = "1.41"]
-
     struct FromOverInto<T>(Vec<T>);
 
     impl<T> Into<FromOverInto<T>> for Vec<T> {
diff --git a/tests/ui/from_over_into.stderr b/tests/ui/from_over_into.stderr
index 9c2a7c04c36..a1764a5ea12 100644
--- a/tests/ui/from_over_into.stderr
+++ b/tests/ui/from_over_into.stderr
@@ -1,5 +1,5 @@
 error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
-  --> $DIR/from_over_into.rs:10:1
+  --> $DIR/from_over_into.rs:9:1
    |
 LL | impl Into<StringWrapper> for String {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -13,7 +13,7 @@ LL ~         StringWrapper(val)
    |
 
 error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
-  --> $DIR/from_over_into.rs:18:1
+  --> $DIR/from_over_into.rs:17:1
    |
 LL | impl Into<SelfType> for String {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -26,7 +26,7 @@ LL ~         SelfType(String::new())
    |
 
 error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
-  --> $DIR/from_over_into.rs:33:1
+  --> $DIR/from_over_into.rs:32:1
    |
 LL | impl Into<SelfKeywords> for X {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -41,7 +41,7 @@ LL ~         let _: X = val;
    |
 
 error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
-  --> $DIR/from_over_into.rs:45:1
+  --> $DIR/from_over_into.rs:44:1
    |
 LL | impl core::convert::Into<bool> for crate::ExplicitPaths {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -59,7 +59,7 @@ LL ~         val.0
    |
 
 error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
-  --> $DIR/from_over_into.rs:80:5
+  --> $DIR/from_over_into.rs:77:5
    |
 LL |     impl<T> Into<FromOverInto<T>> for Vec<T> {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/if_then_some_else_none.rs b/tests/ui/if_then_some_else_none.rs
index 3bc3a039524..0e89fdb0dfa 100644
--- a/tests/ui/if_then_some_else_none.rs
+++ b/tests/ui/if_then_some_else_none.rs
@@ -1,5 +1,4 @@
 #![warn(clippy::if_then_some_else_none)]
-#![feature(custom_inner_attributes)]
 
 fn main() {
     // Should issue an error.
@@ -66,8 +65,8 @@ fn main() {
     let _ = if foo() { into_some("foo") } else { None };
 }
 
+#[clippy::msrv = "1.49"]
 fn _msrv_1_49() {
-    #![clippy::msrv = "1.49"]
     // `bool::then` was stabilized in 1.50. Do not lint this
     let _ = if foo() {
         println!("true!");
@@ -77,8 +76,8 @@ fn _msrv_1_49() {
     };
 }
 
+#[clippy::msrv = "1.50"]
 fn _msrv_1_50() {
-    #![clippy::msrv = "1.50"]
     let _ = if foo() {
         println!("true!");
         Some(150)
diff --git a/tests/ui/if_then_some_else_none.stderr b/tests/ui/if_then_some_else_none.stderr
index 24e0b5947f1..d728a3c31a3 100644
--- a/tests/ui/if_then_some_else_none.stderr
+++ b/tests/ui/if_then_some_else_none.stderr
@@ -1,5 +1,5 @@
 error: this could be simplified with `bool::then`
-  --> $DIR/if_then_some_else_none.rs:6:13
+  --> $DIR/if_then_some_else_none.rs:5:13
    |
 LL |       let _ = if foo() {
    |  _____________^
@@ -14,7 +14,7 @@ LL | |     };
    = note: `-D clippy::if-then-some-else-none` implied by `-D warnings`
 
 error: this could be simplified with `bool::then`
-  --> $DIR/if_then_some_else_none.rs:14:13
+  --> $DIR/if_then_some_else_none.rs:13:13
    |
 LL |       let _ = if matches!(true, true) {
    |  _____________^
@@ -28,7 +28,7 @@ LL | |     };
    = help: consider using `bool::then` like: `matches!(true, true).then(|| { /* snippet */ matches!(true, false) })`
 
 error: this could be simplified with `bool::then_some`
-  --> $DIR/if_then_some_else_none.rs:23:28
+  --> $DIR/if_then_some_else_none.rs:22:28
    |
 LL |     let _ = x.and_then(|o| if o < 32 { Some(o) } else { None });
    |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -36,7 +36,7 @@ LL |     let _ = x.and_then(|o| if o < 32 { Some(o) } else { None });
    = help: consider using `bool::then_some` like: `(o < 32).then_some(o)`
 
 error: this could be simplified with `bool::then_some`
-  --> $DIR/if_then_some_else_none.rs:27:13
+  --> $DIR/if_then_some_else_none.rs:26:13
    |
 LL |     let _ = if !x { Some(0) } else { None };
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -44,7 +44,7 @@ LL |     let _ = if !x { Some(0) } else { None };
    = help: consider using `bool::then_some` like: `(!x).then_some(0)`
 
 error: this could be simplified with `bool::then`
-  --> $DIR/if_then_some_else_none.rs:82:13
+  --> $DIR/if_then_some_else_none.rs:81:13
    |
 LL |       let _ = if foo() {
    |  _____________^
diff --git a/tests/ui/manual_clamp.rs b/tests/ui/manual_clamp.rs
index 331fd29b74e..f7902e6fd53 100644
--- a/tests/ui/manual_clamp.rs
+++ b/tests/ui/manual_clamp.rs
@@ -1,4 +1,3 @@
-#![feature(custom_inner_attributes)]
 #![warn(clippy::manual_clamp)]
 #![allow(
     unused,
@@ -304,9 +303,8 @@ fn cmp_min_max(input: i32) -> i32 {
     input * 3
 }
 
+#[clippy::msrv = "1.49"]
 fn msrv_1_49() {
-    #![clippy::msrv = "1.49"]
-
     let (input, min, max) = (0, -1, 2);
     let _ = if input < min {
         min
@@ -317,9 +315,8 @@ fn msrv_1_49() {
     };
 }
 
+#[clippy::msrv = "1.50"]
 fn msrv_1_50() {
-    #![clippy::msrv = "1.50"]
-
     let (input, min, max) = (0, -1, 2);
     let _ = if input < min {
         min
diff --git a/tests/ui/manual_clamp.stderr b/tests/ui/manual_clamp.stderr
index 70abe28091c..988ad1527e8 100644
--- a/tests/ui/manual_clamp.stderr
+++ b/tests/ui/manual_clamp.stderr
@@ -1,5 +1,5 @@
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:77:5
+  --> $DIR/manual_clamp.rs:76:5
    |
 LL | /     if x9 < min {
 LL | |         x9 = min;
@@ -13,7 +13,7 @@ LL | |     }
    = note: `-D clippy::manual-clamp` implied by `-D warnings`
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:92:5
+  --> $DIR/manual_clamp.rs:91:5
    |
 LL | /     if x11 > max {
 LL | |         x11 = max;
@@ -26,7 +26,7 @@ LL | |     }
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:100:5
+  --> $DIR/manual_clamp.rs:99:5
    |
 LL | /     if min > x12 {
 LL | |         x12 = min;
@@ -39,7 +39,7 @@ LL | |     }
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:108:5
+  --> $DIR/manual_clamp.rs:107:5
    |
 LL | /     if max < x13 {
 LL | |         x13 = max;
@@ -52,7 +52,7 @@ LL | |     }
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:162:5
+  --> $DIR/manual_clamp.rs:161:5
    |
 LL | /     if max < x33 {
 LL | |         x33 = max;
@@ -65,7 +65,7 @@ LL | |     }
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:22:14
+  --> $DIR/manual_clamp.rs:21:14
    |
 LL |       let x0 = if max < input {
    |  ______________^
@@ -80,7 +80,7 @@ LL | |     };
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:30:14
+  --> $DIR/manual_clamp.rs:29:14
    |
 LL |       let x1 = if input > max {
    |  ______________^
@@ -95,7 +95,7 @@ LL | |     };
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:38:14
+  --> $DIR/manual_clamp.rs:37:14
    |
 LL |       let x2 = if input < min {
    |  ______________^
@@ -110,7 +110,7 @@ LL | |     };
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:46:14
+  --> $DIR/manual_clamp.rs:45:14
    |
 LL |       let x3 = if min > input {
    |  ______________^
@@ -125,7 +125,7 @@ LL | |     };
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:54:14
+  --> $DIR/manual_clamp.rs:53:14
    |
 LL |     let x4 = input.max(min).min(max);
    |              ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)`
@@ -133,7 +133,7 @@ LL |     let x4 = input.max(min).min(max);
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:56:14
+  --> $DIR/manual_clamp.rs:55:14
    |
 LL |     let x5 = input.min(max).max(min);
    |              ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)`
@@ -141,7 +141,7 @@ LL |     let x5 = input.min(max).max(min);
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:58:14
+  --> $DIR/manual_clamp.rs:57:14
    |
 LL |       let x6 = match input {
    |  ______________^
@@ -154,7 +154,7 @@ LL | |     };
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:64:14
+  --> $DIR/manual_clamp.rs:63:14
    |
 LL |       let x7 = match input {
    |  ______________^
@@ -167,7 +167,7 @@ LL | |     };
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:70:14
+  --> $DIR/manual_clamp.rs:69:14
    |
 LL |       let x8 = match input {
    |  ______________^
@@ -180,7 +180,7 @@ LL | |     };
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:84:15
+  --> $DIR/manual_clamp.rs:83:15
    |
 LL |       let x10 = match input {
    |  _______________^
@@ -193,7 +193,7 @@ LL | |     };
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:115:15
+  --> $DIR/manual_clamp.rs:114:15
    |
 LL |       let x14 = if input > CONST_MAX {
    |  _______________^
@@ -208,7 +208,7 @@ LL | |     };
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:124:19
+  --> $DIR/manual_clamp.rs:123:19
    |
 LL |           let x15 = if input > max {
    |  ___________________^
@@ -224,7 +224,7 @@ LL | |         };
    = note: clamp returns NaN if the input is NaN
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:135:19
+  --> $DIR/manual_clamp.rs:134:19
    |
 LL |         let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN);
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
@@ -232,7 +232,7 @@ LL |         let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN);
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:136:19
+  --> $DIR/manual_clamp.rs:135:19
    |
 LL |         let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX);
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
@@ -240,7 +240,7 @@ LL |         let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX);
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:137:19
+  --> $DIR/manual_clamp.rs:136:19
    |
 LL |         let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX));
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
@@ -248,7 +248,7 @@ LL |         let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX));
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:138:19
+  --> $DIR/manual_clamp.rs:137:19
    |
 LL |         let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN));
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
@@ -256,7 +256,7 @@ LL |         let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN));
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:139:19
+  --> $DIR/manual_clamp.rs:138:19
    |
 LL |         let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN);
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
@@ -264,7 +264,7 @@ LL |         let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN);
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:140:19
+  --> $DIR/manual_clamp.rs:139:19
    |
 LL |         let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX);
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
@@ -272,7 +272,7 @@ LL |         let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX);
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:141:19
+  --> $DIR/manual_clamp.rs:140:19
    |
 LL |         let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input));
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
@@ -280,7 +280,7 @@ LL |         let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input));
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:142:19
+  --> $DIR/manual_clamp.rs:141:19
    |
 LL |         let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input));
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
@@ -288,7 +288,7 @@ LL |         let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input));
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:144:19
+  --> $DIR/manual_clamp.rs:143:19
    |
 LL |         let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN);
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
@@ -297,7 +297,7 @@ LL |         let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN);
    = note: clamp returns NaN if the input is NaN
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:145:19
+  --> $DIR/manual_clamp.rs:144:19
    |
 LL |         let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX);
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
@@ -306,7 +306,7 @@ LL |         let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX);
    = note: clamp returns NaN if the input is NaN
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:146:19
+  --> $DIR/manual_clamp.rs:145:19
    |
 LL |         let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX));
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
@@ -315,7 +315,7 @@ LL |         let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX));
    = note: clamp returns NaN if the input is NaN
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:147:19
+  --> $DIR/manual_clamp.rs:146:19
    |
 LL |         let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN));
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
@@ -324,7 +324,7 @@ LL |         let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN));
    = note: clamp returns NaN if the input is NaN
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:148:19
+  --> $DIR/manual_clamp.rs:147:19
    |
 LL |         let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN);
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
@@ -333,7 +333,7 @@ LL |         let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN);
    = note: clamp returns NaN if the input is NaN
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:149:19
+  --> $DIR/manual_clamp.rs:148:19
    |
 LL |         let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX);
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
@@ -342,7 +342,7 @@ LL |         let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX);
    = note: clamp returns NaN if the input is NaN
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:150:19
+  --> $DIR/manual_clamp.rs:149:19
    |
 LL |         let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input));
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
@@ -351,7 +351,7 @@ LL |         let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input));
    = note: clamp returns NaN if the input is NaN
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:151:19
+  --> $DIR/manual_clamp.rs:150:19
    |
 LL |         let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input));
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
@@ -360,7 +360,7 @@ LL |         let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input));
    = note: clamp returns NaN if the input is NaN
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:154:5
+  --> $DIR/manual_clamp.rs:153:5
    |
 LL | /     if x32 < min {
 LL | |         x32 = min;
@@ -372,7 +372,7 @@ LL | |     }
    = note: clamp will panic if max < min
 
 error: clamp-like pattern without using clamp function
-  --> $DIR/manual_clamp.rs:324:13
+  --> $DIR/manual_clamp.rs:321:13
    |
 LL |       let _ = if input < min {
    |  _____________^
diff --git a/tests/ui/manual_is_ascii_check.fixed b/tests/ui/manual_is_ascii_check.fixed
index 765bb785994..231ba83b142 100644
--- a/tests/ui/manual_is_ascii_check.fixed
+++ b/tests/ui/manual_is_ascii_check.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![allow(unused, dead_code)]
 #![warn(clippy::manual_is_ascii_check)]
 
@@ -18,28 +17,26 @@ fn main() {
     assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_'));
 }
 
+#[clippy::msrv = "1.23"]
 fn msrv_1_23() {
-    #![clippy::msrv = "1.23"]
-
     assert!(matches!(b'1', b'0'..=b'9'));
     assert!(matches!('X', 'A'..='Z'));
     assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
 }
 
+#[clippy::msrv = "1.24"]
 fn msrv_1_24() {
-    #![clippy::msrv = "1.24"]
-
     assert!(b'1'.is_ascii_digit());
     assert!('X'.is_ascii_uppercase());
     assert!('x'.is_ascii_alphabetic());
 }
 
+#[clippy::msrv = "1.46"]
 fn msrv_1_46() {
-    #![clippy::msrv = "1.46"]
     const FOO: bool = matches!('x', '0'..='9');
 }
 
+#[clippy::msrv = "1.47"]
 fn msrv_1_47() {
-    #![clippy::msrv = "1.47"]
     const FOO: bool = 'x'.is_ascii_digit();
 }
diff --git a/tests/ui/manual_is_ascii_check.rs b/tests/ui/manual_is_ascii_check.rs
index be133161041..39ee6151c56 100644
--- a/tests/ui/manual_is_ascii_check.rs
+++ b/tests/ui/manual_is_ascii_check.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![allow(unused, dead_code)]
 #![warn(clippy::manual_is_ascii_check)]
 
@@ -18,28 +17,26 @@ fn main() {
     assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_'));
 }
 
+#[clippy::msrv = "1.23"]
 fn msrv_1_23() {
-    #![clippy::msrv = "1.23"]
-
     assert!(matches!(b'1', b'0'..=b'9'));
     assert!(matches!('X', 'A'..='Z'));
     assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
 }
 
+#[clippy::msrv = "1.24"]
 fn msrv_1_24() {
-    #![clippy::msrv = "1.24"]
-
     assert!(matches!(b'1', b'0'..=b'9'));
     assert!(matches!('X', 'A'..='Z'));
     assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
 }
 
+#[clippy::msrv = "1.46"]
 fn msrv_1_46() {
-    #![clippy::msrv = "1.46"]
     const FOO: bool = matches!('x', '0'..='9');
 }
 
+#[clippy::msrv = "1.47"]
 fn msrv_1_47() {
-    #![clippy::msrv = "1.47"]
     const FOO: bool = matches!('x', '0'..='9');
 }
diff --git a/tests/ui/manual_is_ascii_check.stderr b/tests/ui/manual_is_ascii_check.stderr
index c0a9d4db1a1..397cbe05c82 100644
--- a/tests/ui/manual_is_ascii_check.stderr
+++ b/tests/ui/manual_is_ascii_check.stderr
@@ -1,5 +1,5 @@
 error: manual check for common ascii range
-  --> $DIR/manual_is_ascii_check.rs:8:13
+  --> $DIR/manual_is_ascii_check.rs:7:13
    |
 LL |     assert!(matches!('x', 'a'..='z'));
    |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_lowercase()`
@@ -7,61 +7,61 @@ LL |     assert!(matches!('x', 'a'..='z'));
    = note: `-D clippy::manual-is-ascii-check` implied by `-D warnings`
 
 error: manual check for common ascii range
-  --> $DIR/manual_is_ascii_check.rs:9:13
+  --> $DIR/manual_is_ascii_check.rs:8:13
    |
 LL |     assert!(matches!('X', 'A'..='Z'));
    |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'X'.is_ascii_uppercase()`
 
 error: manual check for common ascii range
-  --> $DIR/manual_is_ascii_check.rs:10:13
+  --> $DIR/manual_is_ascii_check.rs:9:13
    |
 LL |     assert!(matches!(b'x', b'a'..=b'z'));
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'x'.is_ascii_lowercase()`
 
 error: manual check for common ascii range
-  --> $DIR/manual_is_ascii_check.rs:11:13
+  --> $DIR/manual_is_ascii_check.rs:10:13
    |
 LL |     assert!(matches!(b'X', b'A'..=b'Z'));
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'X'.is_ascii_uppercase()`
 
 error: manual check for common ascii range
-  --> $DIR/manual_is_ascii_check.rs:14:13
+  --> $DIR/manual_is_ascii_check.rs:13:13
    |
 LL |     assert!(matches!(num, '0'..='9'));
    |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.is_ascii_digit()`
 
 error: manual check for common ascii range
-  --> $DIR/manual_is_ascii_check.rs:15:13
+  --> $DIR/manual_is_ascii_check.rs:14:13
    |
 LL |     assert!(matches!(b'1', b'0'..=b'9'));
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'1'.is_ascii_digit()`
 
 error: manual check for common ascii range
-  --> $DIR/manual_is_ascii_check.rs:16:13
+  --> $DIR/manual_is_ascii_check.rs:15:13
    |
 LL |     assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()`
 
 error: manual check for common ascii range
-  --> $DIR/manual_is_ascii_check.rs:32:13
+  --> $DIR/manual_is_ascii_check.rs:29:13
    |
 LL |     assert!(matches!(b'1', b'0'..=b'9'));
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'1'.is_ascii_digit()`
 
 error: manual check for common ascii range
-  --> $DIR/manual_is_ascii_check.rs:33:13
+  --> $DIR/manual_is_ascii_check.rs:30:13
    |
 LL |     assert!(matches!('X', 'A'..='Z'));
    |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'X'.is_ascii_uppercase()`
 
 error: manual check for common ascii range
-  --> $DIR/manual_is_ascii_check.rs:34:13
+  --> $DIR/manual_is_ascii_check.rs:31:13
    |
 LL |     assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()`
 
 error: manual check for common ascii range
-  --> $DIR/manual_is_ascii_check.rs:44:23
+  --> $DIR/manual_is_ascii_check.rs:41:23
    |
 LL |     const FOO: bool = matches!('x', '0'..='9');
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_digit()`
diff --git a/tests/ui/manual_let_else.rs b/tests/ui/manual_let_else.rs
index 2ef40e5911a..48a162c1360 100644
--- a/tests/ui/manual_let_else.rs
+++ b/tests/ui/manual_let_else.rs
@@ -234,4 +234,18 @@ fn not_fire() {
     // If a type annotation is present, don't lint as
     // expressing the type might be too hard
     let v: () = if let Some(v_some) = g() { v_some } else { panic!() };
+
+    // Issue 9940
+    // Suggestion should not expand macros
+    macro_rules! macro_call {
+        () => {
+            return ()
+        };
+    }
+
+    let ff = Some(1);
+    let _ = match ff {
+        Some(value) => value,
+        _ => macro_call!(),
+    };
 }
diff --git a/tests/ui/manual_let_else.stderr b/tests/ui/manual_let_else.stderr
index 453b68b8bd0..52aac6bc673 100644
--- a/tests/ui/manual_let_else.stderr
+++ b/tests/ui/manual_let_else.stderr
@@ -259,5 +259,14 @@ LL |     create_binding_if_some!(w, g());
    |
    = note: this error originates in the macro `create_binding_if_some` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to 17 previous errors
+error: this could be rewritten as `let...else`
+  --> $DIR/manual_let_else.rs:247:5
+   |
+LL | /     let _ = match ff {
+LL | |         Some(value) => value,
+LL | |         _ => macro_call!(),
+LL | |     };
+   | |______^ help: consider writing: `let Some(value) = ff else { macro_call!() };`
+
+error: aborting due to 18 previous errors
 
diff --git a/tests/ui/manual_rem_euclid.fixed b/tests/ui/manual_rem_euclid.fixed
index b942fbfe930..4cdc0546a74 100644
--- a/tests/ui/manual_rem_euclid.fixed
+++ b/tests/ui/manual_rem_euclid.fixed
@@ -1,7 +1,6 @@
 // run-rustfix
 // aux-build:macro_rules.rs
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::manual_rem_euclid)]
 
 #[macro_use]
@@ -55,31 +54,27 @@ pub const fn const_rem_euclid_4(num: i32) -> i32 {
     num.rem_euclid(4)
 }
 
+#[clippy::msrv = "1.37"]
 pub fn msrv_1_37() {
-    #![clippy::msrv = "1.37"]
-
     let x: i32 = 10;
     let _: i32 = ((x % 4) + 4) % 4;
 }
 
+#[clippy::msrv = "1.38"]
 pub fn msrv_1_38() {
-    #![clippy::msrv = "1.38"]
-
     let x: i32 = 10;
     let _: i32 = x.rem_euclid(4);
 }
 
 // For const fns:
+#[clippy::msrv = "1.51"]
 pub const fn msrv_1_51() {
-    #![clippy::msrv = "1.51"]
-
     let x: i32 = 10;
     let _: i32 = ((x % 4) + 4) % 4;
 }
 
+#[clippy::msrv = "1.52"]
 pub const fn msrv_1_52() {
-    #![clippy::msrv = "1.52"]
-
     let x: i32 = 10;
     let _: i32 = x.rem_euclid(4);
 }
diff --git a/tests/ui/manual_rem_euclid.rs b/tests/ui/manual_rem_euclid.rs
index 7462d532169..58a9e20f38b 100644
--- a/tests/ui/manual_rem_euclid.rs
+++ b/tests/ui/manual_rem_euclid.rs
@@ -1,7 +1,6 @@
 // run-rustfix
 // aux-build:macro_rules.rs
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::manual_rem_euclid)]
 
 #[macro_use]
@@ -55,31 +54,27 @@ pub const fn const_rem_euclid_4(num: i32) -> i32 {
     ((num % 4) + 4) % 4
 }
 
+#[clippy::msrv = "1.37"]
 pub fn msrv_1_37() {
-    #![clippy::msrv = "1.37"]
-
     let x: i32 = 10;
     let _: i32 = ((x % 4) + 4) % 4;
 }
 
+#[clippy::msrv = "1.38"]
 pub fn msrv_1_38() {
-    #![clippy::msrv = "1.38"]
-
     let x: i32 = 10;
     let _: i32 = ((x % 4) + 4) % 4;
 }
 
 // For const fns:
+#[clippy::msrv = "1.51"]
 pub const fn msrv_1_51() {
-    #![clippy::msrv = "1.51"]
-
     let x: i32 = 10;
     let _: i32 = ((x % 4) + 4) % 4;
 }
 
+#[clippy::msrv = "1.52"]
 pub const fn msrv_1_52() {
-    #![clippy::msrv = "1.52"]
-
     let x: i32 = 10;
     let _: i32 = ((x % 4) + 4) % 4;
 }
diff --git a/tests/ui/manual_rem_euclid.stderr b/tests/ui/manual_rem_euclid.stderr
index d51bac03b56..e3122a588b6 100644
--- a/tests/ui/manual_rem_euclid.stderr
+++ b/tests/ui/manual_rem_euclid.stderr
@@ -1,5 +1,5 @@
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:20:18
+  --> $DIR/manual_rem_euclid.rs:19: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:21:18
+  --> $DIR/manual_rem_euclid.rs:20: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:22:18
+  --> $DIR/manual_rem_euclid.rs:21: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:23:18
+  --> $DIR/manual_rem_euclid.rs:22: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:24:22
+  --> $DIR/manual_rem_euclid.rs:23: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:13:22
+  --> $DIR/manual_rem_euclid.rs:12: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:50:5
+  --> $DIR/manual_rem_euclid.rs:49:5
    |
 LL |     ((num % 4) + 4) % 4
    |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:55:5
+  --> $DIR/manual_rem_euclid.rs:54:5
    |
 LL |     ((num % 4) + 4) % 4
    |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)`
 
 error: manual `rem_euclid` implementation
-  --> $DIR/manual_rem_euclid.rs:69:18
+  --> $DIR/manual_rem_euclid.rs:66: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:84:18
+  --> $DIR/manual_rem_euclid.rs:79:18
    |
 LL |     let _: i32 = ((x % 4) + 4) % 4;
    |                  ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)`
diff --git a/tests/ui/manual_retain.fixed b/tests/ui/manual_retain.fixed
index fba503a2066..e5ae3cf3e50 100644
--- a/tests/ui/manual_retain.fixed
+++ b/tests/ui/manual_retain.fixed
@@ -1,5 +1,4 @@
 // run-rustfix
-#![feature(custom_inner_attributes)]
 #![warn(clippy::manual_retain)]
 #![allow(unused)]
 use std::collections::BTreeMap;
@@ -216,8 +215,8 @@ fn vec_deque_retain() {
     bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
 }
 
+#[clippy::msrv = "1.52"]
 fn _msrv_153() {
-    #![clippy::msrv = "1.52"]
     let mut btree_map: BTreeMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
     btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
 
@@ -225,14 +224,14 @@ fn _msrv_153() {
     btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect();
 }
 
+#[clippy::msrv = "1.25"]
 fn _msrv_126() {
-    #![clippy::msrv = "1.25"]
     let mut s = String::from("foobar");
     s = s.chars().filter(|&c| c != 'o').to_owned().collect();
 }
 
+#[clippy::msrv = "1.17"]
 fn _msrv_118() {
-    #![clippy::msrv = "1.17"]
     let mut hash_set = HashSet::from([1, 2, 3, 4, 5, 6]);
     hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect();
     let mut hash_map: HashMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
diff --git a/tests/ui/manual_retain.rs b/tests/ui/manual_retain.rs
index 81a849fe768..1021f15edd1 100644
--- a/tests/ui/manual_retain.rs
+++ b/tests/ui/manual_retain.rs
@@ -1,5 +1,4 @@
 // run-rustfix
-#![feature(custom_inner_attributes)]
 #![warn(clippy::manual_retain)]
 #![allow(unused)]
 use std::collections::BTreeMap;
@@ -222,8 +221,8 @@ fn vec_deque_retain() {
     bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
 }
 
+#[clippy::msrv = "1.52"]
 fn _msrv_153() {
-    #![clippy::msrv = "1.52"]
     let mut btree_map: BTreeMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
     btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
 
@@ -231,14 +230,14 @@ fn _msrv_153() {
     btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect();
 }
 
+#[clippy::msrv = "1.25"]
 fn _msrv_126() {
-    #![clippy::msrv = "1.25"]
     let mut s = String::from("foobar");
     s = s.chars().filter(|&c| c != 'o').to_owned().collect();
 }
 
+#[clippy::msrv = "1.17"]
 fn _msrv_118() {
-    #![clippy::msrv = "1.17"]
     let mut hash_set = HashSet::from([1, 2, 3, 4, 5, 6]);
     hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect();
     let mut hash_map: HashMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
diff --git a/tests/ui/manual_retain.stderr b/tests/ui/manual_retain.stderr
index ec635919b48..89316ce1d99 100644
--- a/tests/ui/manual_retain.stderr
+++ b/tests/ui/manual_retain.stderr
@@ -1,5 +1,5 @@
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:52:5
+  --> $DIR/manual_retain.rs:51:5
    |
 LL |     btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_map.retain(|k, _| k % 2 == 0)`
@@ -7,13 +7,13 @@ LL |     btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect()
    = note: `-D clippy::manual-retain` implied by `-D warnings`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:53:5
+  --> $DIR/manual_retain.rs:52:5
    |
 LL |     btree_map = btree_map.into_iter().filter(|(_, v)| v % 2 == 0).collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_map.retain(|_, &mut v| v % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:54:5
+  --> $DIR/manual_retain.rs:53:5
    |
 LL | /     btree_map = btree_map
 LL | |         .into_iter()
@@ -22,37 +22,37 @@ LL | |         .collect();
    | |__________________^ help: consider calling `.retain()` instead: `btree_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0))`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:76:5
+  --> $DIR/manual_retain.rs:75:5
    |
 LL |     btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:77:5
+  --> $DIR/manual_retain.rs:76:5
    |
 LL |     btree_set = btree_set.iter().filter(|&x| x % 2 == 0).cloned().collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:78:5
+  --> $DIR/manual_retain.rs:77:5
    |
 LL |     btree_set = btree_set.into_iter().filter(|x| x % 2 == 0).collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:108:5
+  --> $DIR/manual_retain.rs:107:5
    |
 LL |     hash_map = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_map.retain(|k, _| k % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:109:5
+  --> $DIR/manual_retain.rs:108:5
    |
 LL |     hash_map = hash_map.into_iter().filter(|(_, v)| v % 2 == 0).collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_map.retain(|_, &mut v| v % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:110:5
+  --> $DIR/manual_retain.rs:109:5
    |
 LL | /     hash_map = hash_map
 LL | |         .into_iter()
@@ -61,61 +61,61 @@ LL | |         .collect();
    | |__________________^ help: consider calling `.retain()` instead: `hash_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0))`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:131:5
+  --> $DIR/manual_retain.rs:130:5
    |
 LL |     hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:132:5
+  --> $DIR/manual_retain.rs:131:5
    |
 LL |     hash_set = hash_set.iter().filter(|&x| x % 2 == 0).copied().collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:133:5
+  --> $DIR/manual_retain.rs:132:5
    |
 LL |     hash_set = hash_set.iter().filter(|&x| x % 2 == 0).cloned().collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:162:5
+  --> $DIR/manual_retain.rs:161:5
    |
 LL |     s = s.chars().filter(|&c| c != 'o').to_owned().collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `s.retain(|c| c != 'o')`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:174:5
+  --> $DIR/manual_retain.rs:173:5
    |
 LL |     vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:175:5
+  --> $DIR/manual_retain.rs:174:5
    |
 LL |     vec = vec.iter().filter(|&x| x % 2 == 0).cloned().collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:176:5
+  --> $DIR/manual_retain.rs:175:5
    |
 LL |     vec = vec.into_iter().filter(|x| x % 2 == 0).collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:198:5
+  --> $DIR/manual_retain.rs:197:5
    |
 LL |     vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).copied().collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:199:5
+  --> $DIR/manual_retain.rs:198:5
    |
 LL |     vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).cloned().collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:200:5
+  --> $DIR/manual_retain.rs:199:5
    |
 LL |     vec_deque = vec_deque.into_iter().filter(|x| x % 2 == 0).collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)`
diff --git a/tests/ui/manual_split_once.fixed b/tests/ui/manual_split_once.fixed
index c7ca770434a..50b02019cc2 100644
--- a/tests/ui/manual_split_once.fixed
+++ b/tests/ui/manual_split_once.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::manual_split_once)]
 #![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)]
 
@@ -127,8 +126,8 @@ fn indirect() -> Option<()> {
     None
 }
 
+#[clippy::msrv = "1.51"]
 fn _msrv_1_51() {
-    #![clippy::msrv = "1.51"]
     // `str::split_once` was stabilized in 1.52. Do not lint this
     let _ = "key=value".splitn(2, '=').nth(1).unwrap();
 
@@ -137,8 +136,8 @@ fn _msrv_1_51() {
     let b = iter.next().unwrap();
 }
 
+#[clippy::msrv = "1.52"]
 fn _msrv_1_52() {
-    #![clippy::msrv = "1.52"]
     let _ = "key=value".split_once('=').unwrap().1;
 
     let (a, b) = "a.b.c".split_once('.').unwrap();
diff --git a/tests/ui/manual_split_once.rs b/tests/ui/manual_split_once.rs
index ee2848a251e..e1e8b71a9de 100644
--- a/tests/ui/manual_split_once.rs
+++ b/tests/ui/manual_split_once.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::manual_split_once)]
 #![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)]
 
@@ -127,8 +126,8 @@ fn indirect() -> Option<()> {
     None
 }
 
+#[clippy::msrv = "1.51"]
 fn _msrv_1_51() {
-    #![clippy::msrv = "1.51"]
     // `str::split_once` was stabilized in 1.52. Do not lint this
     let _ = "key=value".splitn(2, '=').nth(1).unwrap();
 
@@ -137,8 +136,8 @@ fn _msrv_1_51() {
     let b = iter.next().unwrap();
 }
 
+#[clippy::msrv = "1.52"]
 fn _msrv_1_52() {
-    #![clippy::msrv = "1.52"]
     let _ = "key=value".splitn(2, '=').nth(1).unwrap();
 
     let mut iter = "a.b.c".splitn(2, '.');
diff --git a/tests/ui/manual_split_once.stderr b/tests/ui/manual_split_once.stderr
index 2696694680a..78da5a16cc5 100644
--- a/tests/ui/manual_split_once.stderr
+++ b/tests/ui/manual_split_once.stderr
@@ -1,5 +1,5 @@
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:14:13
+  --> $DIR/manual_split_once.rs:13:13
    |
 LL |     let _ = "key=value".splitn(2, '=').nth(1).unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
@@ -7,79 +7,79 @@ LL |     let _ = "key=value".splitn(2, '=').nth(1).unwrap();
    = note: `-D clippy::manual-split-once` implied by `-D warnings`
 
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:15:13
+  --> $DIR/manual_split_once.rs:14:13
    |
 LL |     let _ = "key=value".splitn(2, '=').skip(1).next().unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
 
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:16:18
+  --> $DIR/manual_split_once.rs:15:18
    |
 LL |     let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap();
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=')`
 
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:19:13
+  --> $DIR/manual_split_once.rs:18:13
    |
 LL |     let _ = s.splitn(2, '=').nth(1).unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1`
 
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:22:13
+  --> $DIR/manual_split_once.rs:21:13
    |
 LL |     let _ = s.splitn(2, '=').nth(1).unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1`
 
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:25:13
+  --> $DIR/manual_split_once.rs:24:13
    |
 LL |     let _ = s.splitn(2, '=').skip(1).next().unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1`
 
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:28:17
+  --> $DIR/manual_split_once.rs:27:17
    |
 LL |         let _ = s.splitn(2, '=').nth(1)?;
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1`
 
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:29:17
+  --> $DIR/manual_split_once.rs:28:17
    |
 LL |         let _ = s.splitn(2, '=').skip(1).next()?;
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1`
 
 error: manual implementation of `rsplit_once`
-  --> $DIR/manual_split_once.rs:30:17
+  --> $DIR/manual_split_once.rs:29:17
    |
 LL |         let _ = s.rsplitn(2, '=').nth(1)?;
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0`
 
 error: manual implementation of `rsplit_once`
-  --> $DIR/manual_split_once.rs:31:17
+  --> $DIR/manual_split_once.rs:30:17
    |
 LL |         let _ = s.rsplitn(2, '=').skip(1).next()?;
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0`
 
 error: manual implementation of `rsplit_once`
-  --> $DIR/manual_split_once.rs:39:13
+  --> $DIR/manual_split_once.rs:38:13
    |
 LL |     let _ = "key=value".rsplitn(2, '=').nth(1).unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').unwrap().0`
 
 error: manual implementation of `rsplit_once`
-  --> $DIR/manual_split_once.rs:40:18
+  --> $DIR/manual_split_once.rs:39:18
    |
 LL |     let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap();
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))`
 
 error: manual implementation of `rsplit_once`
-  --> $DIR/manual_split_once.rs:41:13
+  --> $DIR/manual_split_once.rs:40:13
    |
 LL |     let _ = s.rsplitn(2, '=').nth(1);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=').map(|x| x.0)`
 
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:45:5
+  --> $DIR/manual_split_once.rs:44:5
    |
 LL |     let mut iter = "a.b.c".splitn(2, '.');
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -104,7 +104,7 @@ LL +
    |
 
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:49:5
+  --> $DIR/manual_split_once.rs:48:5
    |
 LL |     let mut iter = "a.b.c".splitn(2, '.');
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -129,7 +129,7 @@ LL +
    |
 
 error: manual implementation of `rsplit_once`
-  --> $DIR/manual_split_once.rs:53:5
+  --> $DIR/manual_split_once.rs:52:5
    |
 LL |     let mut iter = "a.b.c".rsplitn(2, '.');
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -154,7 +154,7 @@ LL +
    |
 
 error: manual implementation of `rsplit_once`
-  --> $DIR/manual_split_once.rs:57:5
+  --> $DIR/manual_split_once.rs:56:5
    |
 LL |     let mut iter = "a.b.c".rsplitn(2, '.');
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -179,13 +179,13 @@ LL +
    |
 
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:142:13
+  --> $DIR/manual_split_once.rs:141:13
    |
 LL |     let _ = "key=value".splitn(2, '=').nth(1).unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
 
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:144:5
+  --> $DIR/manual_split_once.rs:143:5
    |
 LL |     let mut iter = "a.b.c".splitn(2, '.');
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/manual_str_repeat.fixed b/tests/ui/manual_str_repeat.fixed
index 0704ba2f933..3d56f2a0ded 100644
--- a/tests/ui/manual_str_repeat.fixed
+++ b/tests/ui/manual_str_repeat.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::manual_str_repeat)]
 
 use std::borrow::Cow;
@@ -54,13 +53,13 @@ fn main() {
     let _: String = repeat(x).take(count).collect();
 }
 
+#[clippy::msrv = "1.15"]
 fn _msrv_1_15() {
-    #![clippy::msrv = "1.15"]
     // `str::repeat` was stabilized in 1.16. Do not lint this
     let _: String = std::iter::repeat("test").take(10).collect();
 }
 
+#[clippy::msrv = "1.16"]
 fn _msrv_1_16() {
-    #![clippy::msrv = "1.16"]
     let _: String = "test".repeat(10);
 }
diff --git a/tests/ui/manual_str_repeat.rs b/tests/ui/manual_str_repeat.rs
index f522be439aa..e8240a949db 100644
--- a/tests/ui/manual_str_repeat.rs
+++ b/tests/ui/manual_str_repeat.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::manual_str_repeat)]
 
 use std::borrow::Cow;
@@ -54,13 +53,13 @@ fn main() {
     let _: String = repeat(x).take(count).collect();
 }
 
+#[clippy::msrv = "1.15"]
 fn _msrv_1_15() {
-    #![clippy::msrv = "1.15"]
     // `str::repeat` was stabilized in 1.16. Do not lint this
     let _: String = std::iter::repeat("test").take(10).collect();
 }
 
+#[clippy::msrv = "1.16"]
 fn _msrv_1_16() {
-    #![clippy::msrv = "1.16"]
     let _: String = std::iter::repeat("test").take(10).collect();
 }
diff --git a/tests/ui/manual_str_repeat.stderr b/tests/ui/manual_str_repeat.stderr
index c6511689716..bdfee7cab26 100644
--- a/tests/ui/manual_str_repeat.stderr
+++ b/tests/ui/manual_str_repeat.stderr
@@ -1,5 +1,5 @@
 error: manual implementation of `str::repeat` using iterators
-  --> $DIR/manual_str_repeat.rs:10:21
+  --> $DIR/manual_str_repeat.rs:9:21
    |
 LL |     let _: String = std::iter::repeat("test").take(10).collect();
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"test".repeat(10)`
@@ -7,55 +7,55 @@ LL |     let _: String = std::iter::repeat("test").take(10).collect();
    = note: `-D clippy::manual-str-repeat` implied by `-D warnings`
 
 error: manual implementation of `str::repeat` using iterators
-  --> $DIR/manual_str_repeat.rs:11:21
+  --> $DIR/manual_str_repeat.rs:10:21
    |
 LL |     let _: String = std::iter::repeat('x').take(10).collect();
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"x".repeat(10)`
 
 error: manual implementation of `str::repeat` using iterators
-  --> $DIR/manual_str_repeat.rs:12:21
+  --> $DIR/manual_str_repeat.rs:11:21
    |
 LL |     let _: String = std::iter::repeat('/'').take(10).collect();
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"'".repeat(10)`
 
 error: manual implementation of `str::repeat` using iterators
-  --> $DIR/manual_str_repeat.rs:13:21
+  --> $DIR/manual_str_repeat.rs:12:21
    |
 LL |     let _: String = std::iter::repeat('"').take(10).collect();
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"/"".repeat(10)`
 
 error: manual implementation of `str::repeat` using iterators
-  --> $DIR/manual_str_repeat.rs:17:13
+  --> $DIR/manual_str_repeat.rs:16:13
    |
 LL |     let _ = repeat(x).take(count + 2).collect::<String>();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count + 2)`
 
 error: manual implementation of `str::repeat` using iterators
-  --> $DIR/manual_str_repeat.rs:26:21
+  --> $DIR/manual_str_repeat.rs:25:21
    |
 LL |     let _: String = repeat(*x).take(count).collect();
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(*x).repeat(count)`
 
 error: manual implementation of `str::repeat` using iterators
-  --> $DIR/manual_str_repeat.rs:35:21
+  --> $DIR/manual_str_repeat.rs:34:21
    |
 LL |     let _: String = repeat(x).take(count).collect();
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count)`
 
 error: manual implementation of `str::repeat` using iterators
-  --> $DIR/manual_str_repeat.rs:47:21
+  --> $DIR/manual_str_repeat.rs:46:21
    |
 LL |     let _: String = repeat(Cow::Borrowed("test")).take(count).collect();
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Cow::Borrowed("test").repeat(count)`
 
 error: manual implementation of `str::repeat` using iterators
-  --> $DIR/manual_str_repeat.rs:50:21
+  --> $DIR/manual_str_repeat.rs:49:21
    |
 LL |     let _: String = repeat(x).take(count).collect();
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count)`
 
 error: manual implementation of `str::repeat` using iterators
-  --> $DIR/manual_str_repeat.rs:65:21
+  --> $DIR/manual_str_repeat.rs:64:21
    |
 LL |     let _: String = std::iter::repeat("test").take(10).collect();
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"test".repeat(10)`
diff --git a/tests/ui/manual_strip.rs b/tests/ui/manual_strip.rs
index 85009d78558..b0b1c262aee 100644
--- a/tests/ui/manual_strip.rs
+++ b/tests/ui/manual_strip.rs
@@ -1,4 +1,3 @@
-#![feature(custom_inner_attributes)]
 #![warn(clippy::manual_strip)]
 
 fn main() {
@@ -66,18 +65,16 @@ fn main() {
     }
 }
 
+#[clippy::msrv = "1.44"]
 fn msrv_1_44() {
-    #![clippy::msrv = "1.44"]
-
     let s = "abc";
     if s.starts_with('a') {
         s[1..].to_string();
     }
 }
 
+#[clippy::msrv = "1.45"]
 fn msrv_1_45() {
-    #![clippy::msrv = "1.45"]
-
     let s = "abc";
     if s.starts_with('a') {
         s[1..].to_string();
diff --git a/tests/ui/manual_strip.stderr b/tests/ui/manual_strip.stderr
index ad2a362f3e7..f592e898fc9 100644
--- a/tests/ui/manual_strip.stderr
+++ b/tests/ui/manual_strip.stderr
@@ -1,11 +1,11 @@
 error: stripping a prefix manually
-  --> $DIR/manual_strip.rs:8:24
+  --> $DIR/manual_strip.rs:7:24
    |
 LL |         str::to_string(&s["ab".len()..]);
    |                        ^^^^^^^^^^^^^^^^
    |
 note: the prefix was tested here
-  --> $DIR/manual_strip.rs:7:5
+  --> $DIR/manual_strip.rs:6:5
    |
 LL |     if s.starts_with("ab") {
    |     ^^^^^^^^^^^^^^^^^^^^^^^
@@ -21,13 +21,13 @@ LL ~         <stripped>.to_string();
    |
 
 error: stripping a suffix manually
-  --> $DIR/manual_strip.rs:16:24
+  --> $DIR/manual_strip.rs:15:24
    |
 LL |         str::to_string(&s[..s.len() - "bc".len()]);
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: the suffix was tested here
-  --> $DIR/manual_strip.rs:15:5
+  --> $DIR/manual_strip.rs:14:5
    |
 LL |     if s.ends_with("bc") {
    |     ^^^^^^^^^^^^^^^^^^^^^
@@ -42,13 +42,13 @@ LL ~         <stripped>.to_string();
    |
 
 error: stripping a prefix manually
-  --> $DIR/manual_strip.rs:25:24
+  --> $DIR/manual_strip.rs:24:24
    |
 LL |         str::to_string(&s[1..]);
    |                        ^^^^^^^
    |
 note: the prefix was tested here
-  --> $DIR/manual_strip.rs:24:5
+  --> $DIR/manual_strip.rs:23:5
    |
 LL |     if s.starts_with('a') {
    |     ^^^^^^^^^^^^^^^^^^^^^^
@@ -60,13 +60,13 @@ LL ~         <stripped>.to_string();
    |
 
 error: stripping a prefix manually
-  --> $DIR/manual_strip.rs:32:24
+  --> $DIR/manual_strip.rs:31:24
    |
 LL |         str::to_string(&s[prefix.len()..]);
    |                        ^^^^^^^^^^^^^^^^^^
    |
 note: the prefix was tested here
-  --> $DIR/manual_strip.rs:31:5
+  --> $DIR/manual_strip.rs:30:5
    |
 LL |     if s.starts_with(prefix) {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -77,13 +77,13 @@ LL ~         str::to_string(<stripped>);
    |
 
 error: stripping a prefix manually
-  --> $DIR/manual_strip.rs:38:24
+  --> $DIR/manual_strip.rs:37:24
    |
 LL |         str::to_string(&s[PREFIX.len()..]);
    |                        ^^^^^^^^^^^^^^^^^^
    |
 note: the prefix was tested here
-  --> $DIR/manual_strip.rs:37:5
+  --> $DIR/manual_strip.rs:36:5
    |
 LL |     if s.starts_with(PREFIX) {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -95,13 +95,13 @@ LL ~         str::to_string(<stripped>);
    |
 
 error: stripping a prefix manually
-  --> $DIR/manual_strip.rs:45:24
+  --> $DIR/manual_strip.rs:44:24
    |
 LL |         str::to_string(&TARGET[prefix.len()..]);
    |                        ^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: the prefix was tested here
-  --> $DIR/manual_strip.rs:44:5
+  --> $DIR/manual_strip.rs:43:5
    |
 LL |     if TARGET.starts_with(prefix) {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -112,13 +112,13 @@ LL ~         str::to_string(<stripped>);
    |
 
 error: stripping a prefix manually
-  --> $DIR/manual_strip.rs:51:9
+  --> $DIR/manual_strip.rs:50:9
    |
 LL |         s1[2..].to_uppercase();
    |         ^^^^^^^
    |
 note: the prefix was tested here
-  --> $DIR/manual_strip.rs:50:5
+  --> $DIR/manual_strip.rs:49:5
    |
 LL |     if s1.starts_with("ab") {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -129,13 +129,13 @@ LL ~         <stripped>.to_uppercase();
    |
 
 error: stripping a prefix manually
-  --> $DIR/manual_strip.rs:83:9
+  --> $DIR/manual_strip.rs:80:9
    |
 LL |         s[1..].to_string();
    |         ^^^^^^
    |
 note: the prefix was tested here
-  --> $DIR/manual_strip.rs:82:5
+  --> $DIR/manual_strip.rs:79:5
    |
 LL |     if s.starts_with('a') {
    |     ^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/map_unwrap_or.rs b/tests/ui/map_unwrap_or.rs
index 396b22a9abb..32631024ca5 100644
--- a/tests/ui/map_unwrap_or.rs
+++ b/tests/ui/map_unwrap_or.rs
@@ -1,6 +1,5 @@
 // aux-build:option_helpers.rs
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::map_unwrap_or)]
 #![allow(clippy::uninlined_format_args, clippy::unnecessary_lazy_evaluations)]
 
@@ -82,17 +81,15 @@ fn main() {
     result_methods();
 }
 
+#[clippy::msrv = "1.40"]
 fn msrv_1_40() {
-    #![clippy::msrv = "1.40"]
-
     let res: Result<i32, ()> = Ok(1);
 
     let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0);
 }
 
+#[clippy::msrv = "1.41"]
 fn msrv_1_41() {
-    #![clippy::msrv = "1.41"]
-
     let res: Result<i32, ()> = Ok(1);
 
     let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0);
diff --git a/tests/ui/map_unwrap_or.stderr b/tests/ui/map_unwrap_or.stderr
index d17d24a403e..41781b050fa 100644
--- a/tests/ui/map_unwrap_or.stderr
+++ b/tests/ui/map_unwrap_or.stderr
@@ -1,5 +1,5 @@
 error: called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead
-  --> $DIR/map_unwrap_or.rs:18:13
+  --> $DIR/map_unwrap_or.rs:17:13
    |
 LL |       let _ = opt.map(|x| x + 1)
    |  _____________^
@@ -15,7 +15,7 @@ LL +     let _ = opt.map_or(0, |x| x + 1);
    |
 
 error: called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead
-  --> $DIR/map_unwrap_or.rs:22:13
+  --> $DIR/map_unwrap_or.rs:21:13
    |
 LL |       let _ = opt.map(|x| {
    |  _____________^
@@ -33,7 +33,7 @@ LL ~     );
    |
 
 error: called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead
-  --> $DIR/map_unwrap_or.rs:26:13
+  --> $DIR/map_unwrap_or.rs:25:13
    |
 LL |       let _ = opt.map(|x| x + 1)
    |  _____________^
@@ -50,7 +50,7 @@ LL ~         }, |x| x + 1);
    |
 
 error: called `map(<f>).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(<f>)` instead
-  --> $DIR/map_unwrap_or.rs:31:13
+  --> $DIR/map_unwrap_or.rs:30:13
    |
 LL |     let _ = opt.map(|x| Some(x + 1)).unwrap_or(None);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -62,7 +62,7 @@ LL +     let _ = opt.and_then(|x| Some(x + 1));
    |
 
 error: called `map(<f>).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(<f>)` instead
-  --> $DIR/map_unwrap_or.rs:33:13
+  --> $DIR/map_unwrap_or.rs:32:13
    |
 LL |       let _ = opt.map(|x| {
    |  _____________^
@@ -80,7 +80,7 @@ LL ~     );
    |
 
 error: called `map(<f>).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(<f>)` instead
-  --> $DIR/map_unwrap_or.rs:37:13
+  --> $DIR/map_unwrap_or.rs:36:13
    |
 LL |       let _ = opt
    |  _____________^
@@ -95,7 +95,7 @@ LL +         .and_then(|x| Some(x + 1));
    |
 
 error: called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead
-  --> $DIR/map_unwrap_or.rs:48:13
+  --> $DIR/map_unwrap_or.rs:47:13
    |
 LL |     let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -107,7 +107,7 @@ LL +     let _ = Some("prefix").map_or(id, |p| format!("{}.", p));
    |
 
 error: called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead
-  --> $DIR/map_unwrap_or.rs:52:13
+  --> $DIR/map_unwrap_or.rs:51:13
    |
 LL |       let _ = opt.map(|x| {
    |  _____________^
@@ -117,7 +117,7 @@ LL | |     ).unwrap_or_else(|| 0);
    | |__________________________^
 
 error: called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead
-  --> $DIR/map_unwrap_or.rs:56:13
+  --> $DIR/map_unwrap_or.rs:55:13
    |
 LL |       let _ = opt.map(|x| x + 1)
    |  _____________^
@@ -127,7 +127,7 @@ LL | |         );
    | |_________^
 
 error: called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling `.map_or_else(<g>, <f>)` instead
-  --> $DIR/map_unwrap_or.rs:68:13
+  --> $DIR/map_unwrap_or.rs:67:13
    |
 LL |       let _ = res.map(|x| {
    |  _____________^
@@ -137,7 +137,7 @@ LL | |     ).unwrap_or_else(|_e| 0);
    | |____________________________^
 
 error: called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling `.map_or_else(<g>, <f>)` instead
-  --> $DIR/map_unwrap_or.rs:72:13
+  --> $DIR/map_unwrap_or.rs:71:13
    |
 LL |       let _ = res.map(|x| x + 1)
    |  _____________^
@@ -147,7 +147,7 @@ LL | |         });
    | |__________^
 
 error: called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling `.map_or_else(<g>, <f>)` instead
-  --> $DIR/map_unwrap_or.rs:98:13
+  --> $DIR/map_unwrap_or.rs:95:13
    |
 LL |     let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)`
diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_expr_like_matches_macro.fixed
index 968f462f8a0..55cd15bd5c3 100644
--- a/tests/ui/match_expr_like_matches_macro.fixed
+++ b/tests/ui/match_expr_like_matches_macro.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::match_like_matches_macro)]
 #![allow(
     unreachable_patterns,
@@ -200,17 +199,15 @@ fn main() {
     };
 }
 
+#[clippy::msrv = "1.41"]
 fn msrv_1_41() {
-    #![clippy::msrv = "1.41"]
-
     let _y = match Some(5) {
         Some(0) => true,
         _ => false,
     };
 }
 
+#[clippy::msrv = "1.42"]
 fn msrv_1_42() {
-    #![clippy::msrv = "1.42"]
-
     let _y = matches!(Some(5), Some(0));
 }
diff --git a/tests/ui/match_expr_like_matches_macro.rs b/tests/ui/match_expr_like_matches_macro.rs
index c6b479e27c5..5d645e108e5 100644
--- a/tests/ui/match_expr_like_matches_macro.rs
+++ b/tests/ui/match_expr_like_matches_macro.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::match_like_matches_macro)]
 #![allow(
     unreachable_patterns,
@@ -241,18 +240,16 @@ fn main() {
     };
 }
 
+#[clippy::msrv = "1.41"]
 fn msrv_1_41() {
-    #![clippy::msrv = "1.41"]
-
     let _y = match Some(5) {
         Some(0) => true,
         _ => false,
     };
 }
 
+#[clippy::msrv = "1.42"]
 fn msrv_1_42() {
-    #![clippy::msrv = "1.42"]
-
     let _y = match Some(5) {
         Some(0) => true,
         _ => false,
diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_expr_like_matches_macro.stderr
index a4df8008ac2..46f67ef4900 100644
--- a/tests/ui/match_expr_like_matches_macro.stderr
+++ b/tests/ui/match_expr_like_matches_macro.stderr
@@ -1,5 +1,5 @@
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:16:14
+  --> $DIR/match_expr_like_matches_macro.rs:15:14
    |
 LL |       let _y = match x {
    |  ______________^
@@ -11,7 +11,7 @@ LL | |     };
    = note: `-D clippy::match-like-matches-macro` implied by `-D warnings`
 
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:22:14
+  --> $DIR/match_expr_like_matches_macro.rs:21:14
    |
 LL |       let _w = match x {
    |  ______________^
@@ -21,7 +21,7 @@ LL | |     };
    | |_____^ help: try this: `matches!(x, Some(_))`
 
 error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/match_expr_like_matches_macro.rs:28:14
+  --> $DIR/match_expr_like_matches_macro.rs:27:14
    |
 LL |       let _z = match x {
    |  ______________^
@@ -33,7 +33,7 @@ LL | |     };
    = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
 
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:34:15
+  --> $DIR/match_expr_like_matches_macro.rs:33:15
    |
 LL |       let _zz = match x {
    |  _______________^
@@ -43,13 +43,13 @@ LL | |     };
    | |_____^ help: try this: `!matches!(x, Some(r) if r == 0)`
 
 error: if let .. else expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:40:16
+  --> $DIR/match_expr_like_matches_macro.rs:39:16
    |
 LL |     let _zzz = if let Some(5) = x { true } else { false };
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))`
 
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:64:20
+  --> $DIR/match_expr_like_matches_macro.rs:63:20
    |
 LL |           let _ans = match x {
    |  ____________________^
@@ -60,7 +60,7 @@ LL | |         };
    | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))`
 
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:74:20
+  --> $DIR/match_expr_like_matches_macro.rs:73:20
    |
 LL |           let _ans = match x {
    |  ____________________^
@@ -73,7 +73,7 @@ LL | |         };
    | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))`
 
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:84:20
+  --> $DIR/match_expr_like_matches_macro.rs:83:20
    |
 LL |           let _ans = match x {
    |  ____________________^
@@ -84,7 +84,7 @@ LL | |         };
    | |_________^ help: try this: `!matches!(x, E::B(_) | E::C)`
 
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:144:18
+  --> $DIR/match_expr_like_matches_macro.rs:143:18
    |
 LL |           let _z = match &z {
    |  __________________^
@@ -94,7 +94,7 @@ LL | |         };
    | |_________^ help: try this: `matches!(z, Some(3))`
 
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:153:18
+  --> $DIR/match_expr_like_matches_macro.rs:152:18
    |
 LL |           let _z = match &z {
    |  __________________^
@@ -104,7 +104,7 @@ LL | |         };
    | |_________^ help: try this: `matches!(&z, Some(3))`
 
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:170:21
+  --> $DIR/match_expr_like_matches_macro.rs:169:21
    |
 LL |               let _ = match &z {
    |  _____________________^
@@ -114,7 +114,7 @@ LL | |             };
    | |_____________^ help: try this: `matches!(&z, AnEnum::X)`
 
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:184:20
+  --> $DIR/match_expr_like_matches_macro.rs:183:20
    |
 LL |           let _res = match &val {
    |  ____________________^
@@ -124,7 +124,7 @@ LL | |         };
    | |_________^ help: try this: `matches!(&val, &Some(ref _a))`
 
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:196:20
+  --> $DIR/match_expr_like_matches_macro.rs:195:20
    |
 LL |           let _res = match &val {
    |  ____________________^
@@ -134,7 +134,7 @@ LL | |         };
    | |_________^ help: try this: `matches!(&val, &Some(ref _a))`
 
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:256:14
+  --> $DIR/match_expr_like_matches_macro.rs:253:14
    |
 LL |       let _y = match Some(5) {
    |  ______________^
diff --git a/tests/ui/mem_replace.fixed b/tests/ui/mem_replace.fixed
index ae237395b95..874d5584330 100644
--- a/tests/ui/mem_replace.fixed
+++ b/tests/ui/mem_replace.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![allow(unused)]
 #![warn(
     clippy::all,
@@ -80,16 +79,14 @@ fn main() {
     dont_lint_primitive();
 }
 
+#[clippy::msrv = "1.39"]
 fn msrv_1_39() {
-    #![clippy::msrv = "1.39"]
-
     let mut s = String::from("foo");
     let _ = std::mem::replace(&mut s, String::default());
 }
 
+#[clippy::msrv = "1.40"]
 fn msrv_1_40() {
-    #![clippy::msrv = "1.40"]
-
     let mut s = String::from("foo");
     let _ = std::mem::take(&mut s);
 }
diff --git a/tests/ui/mem_replace.rs b/tests/ui/mem_replace.rs
index 3202e99e0be..f4f3bff5144 100644
--- a/tests/ui/mem_replace.rs
+++ b/tests/ui/mem_replace.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![allow(unused)]
 #![warn(
     clippy::all,
@@ -80,16 +79,14 @@ fn main() {
     dont_lint_primitive();
 }
 
+#[clippy::msrv = "1.39"]
 fn msrv_1_39() {
-    #![clippy::msrv = "1.39"]
-
     let mut s = String::from("foo");
     let _ = std::mem::replace(&mut s, String::default());
 }
 
+#[clippy::msrv = "1.40"]
 fn msrv_1_40() {
-    #![clippy::msrv = "1.40"]
-
     let mut s = String::from("foo");
     let _ = std::mem::replace(&mut s, String::default());
 }
diff --git a/tests/ui/mem_replace.stderr b/tests/ui/mem_replace.stderr
index dd8a50dab90..caa127f76ee 100644
--- a/tests/ui/mem_replace.stderr
+++ b/tests/ui/mem_replace.stderr
@@ -1,5 +1,5 @@
 error: replacing an `Option` with `None`
-  --> $DIR/mem_replace.rs:17:13
+  --> $DIR/mem_replace.rs:16:13
    |
 LL |     let _ = mem::replace(&mut an_option, None);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()`
@@ -7,13 +7,13 @@ LL |     let _ = mem::replace(&mut an_option, None);
    = note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings`
 
 error: replacing an `Option` with `None`
-  --> $DIR/mem_replace.rs:19:13
+  --> $DIR/mem_replace.rs:18:13
    |
 LL |     let _ = mem::replace(an_option, None);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:24:13
+  --> $DIR/mem_replace.rs:23:13
    |
 LL |     let _ = std::mem::replace(&mut s, String::default());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)`
@@ -21,103 +21,103 @@ LL |     let _ = std::mem::replace(&mut s, String::default());
    = note: `-D clippy::mem-replace-with-default` implied by `-D warnings`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:27:13
+  --> $DIR/mem_replace.rs:26:13
    |
 LL |     let _ = std::mem::replace(s, String::default());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:28:13
+  --> $DIR/mem_replace.rs:27:13
    |
 LL |     let _ = std::mem::replace(s, Default::default());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:31:13
+  --> $DIR/mem_replace.rs:30:13
    |
 LL |     let _ = std::mem::replace(&mut v, Vec::default());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:32:13
+  --> $DIR/mem_replace.rs:31:13
    |
 LL |     let _ = std::mem::replace(&mut v, Default::default());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:33:13
+  --> $DIR/mem_replace.rs:32:13
    |
 LL |     let _ = std::mem::replace(&mut v, Vec::new());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:34:13
+  --> $DIR/mem_replace.rs:33:13
    |
 LL |     let _ = std::mem::replace(&mut v, vec![]);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:37:13
+  --> $DIR/mem_replace.rs:36:13
    |
 LL |     let _ = std::mem::replace(&mut hash_map, HashMap::new());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_map)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:40:13
+  --> $DIR/mem_replace.rs:39:13
    |
 LL |     let _ = std::mem::replace(&mut btree_map, BTreeMap::new());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_map)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:43:13
+  --> $DIR/mem_replace.rs:42:13
    |
 LL |     let _ = std::mem::replace(&mut vd, VecDeque::new());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut vd)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:46:13
+  --> $DIR/mem_replace.rs:45:13
    |
 LL |     let _ = std::mem::replace(&mut hash_set, HashSet::new());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_set)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:49:13
+  --> $DIR/mem_replace.rs:48:13
    |
 LL |     let _ = std::mem::replace(&mut btree_set, BTreeSet::new());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_set)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:52:13
+  --> $DIR/mem_replace.rs:51:13
    |
 LL |     let _ = std::mem::replace(&mut list, LinkedList::new());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut list)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:55:13
+  --> $DIR/mem_replace.rs:54:13
    |
 LL |     let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut binary_heap)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:58:13
+  --> $DIR/mem_replace.rs:57:13
    |
 LL |     let _ = std::mem::replace(&mut tuple, (vec![], BinaryHeap::new()));
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut tuple)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:61:13
+  --> $DIR/mem_replace.rs:60:13
    |
 LL |     let _ = std::mem::replace(&mut refstr, "");
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut refstr)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:64:13
+  --> $DIR/mem_replace.rs:63:13
    |
 LL |     let _ = std::mem::replace(&mut slice, &[]);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut slice)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:94:13
+  --> $DIR/mem_replace.rs:91:13
    |
 LL |     let _ = std::mem::replace(&mut s, String::default());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)`
diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs
index cd148063bf0..955e7eb7276 100644
--- a/tests/ui/min_rust_version_attr.rs
+++ b/tests/ui/min_rust_version_attr.rs
@@ -3,27 +3,60 @@
 
 fn main() {}
 
+#[clippy::msrv = "1.42.0"]
 fn just_under_msrv() {
-    #![clippy::msrv = "1.42.0"]
     let log2_10 = 3.321928094887362;
 }
 
+#[clippy::msrv = "1.43.0"]
 fn meets_msrv() {
-    #![clippy::msrv = "1.43.0"]
     let log2_10 = 3.321928094887362;
 }
 
+#[clippy::msrv = "1.44.0"]
 fn just_above_msrv() {
-    #![clippy::msrv = "1.44.0"]
     let log2_10 = 3.321928094887362;
 }
 
+#[clippy::msrv = "1.42"]
 fn no_patch_under() {
-    #![clippy::msrv = "1.42"]
     let log2_10 = 3.321928094887362;
 }
 
+#[clippy::msrv = "1.43"]
 fn no_patch_meets() {
+    let log2_10 = 3.321928094887362;
+}
+
+fn inner_attr_under() {
+    #![clippy::msrv = "1.42"]
+    let log2_10 = 3.321928094887362;
+}
+
+fn inner_attr_meets() {
     #![clippy::msrv = "1.43"]
     let log2_10 = 3.321928094887362;
 }
+
+// https://github.com/rust-lang/rust-clippy/issues/6920
+fn scoping() {
+    mod m {
+        #![clippy::msrv = "1.42.0"]
+    }
+
+    // Should warn
+    let log2_10 = 3.321928094887362;
+
+    mod a {
+        #![clippy::msrv = "1.42.0"]
+
+        fn should_warn() {
+            #![clippy::msrv = "1.43.0"]
+            let log2_10 = 3.321928094887362;
+        }
+
+        fn should_not_warn() {
+            let log2_10 = 3.321928094887362;
+        }
+    }
+}
diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr
index 68aa5874819..7e2135584ef 100644
--- a/tests/ui/min_rust_version_attr.stderr
+++ b/tests/ui/min_rust_version_attr.stderr
@@ -23,5 +23,29 @@ LL |     let log2_10 = 3.321928094887362;
    |
    = help: consider using the constant directly
 
-error: aborting due to 3 previous errors
+error: approximate value of `f{32, 64}::consts::LOG2_10` found
+  --> $DIR/min_rust_version_attr.rs:38:19
+   |
+LL |     let log2_10 = 3.321928094887362;
+   |                   ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::LOG2_10` found
+  --> $DIR/min_rust_version_attr.rs:48:19
+   |
+LL |     let log2_10 = 3.321928094887362;
+   |                   ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::LOG2_10` found
+  --> $DIR/min_rust_version_attr.rs:55:27
+   |
+LL |             let log2_10 = 3.321928094887362;
+   |                           ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using the constant directly
+
+error: aborting due to 6 previous errors
 
diff --git a/tests/ui/min_rust_version_invalid_attr.stderr b/tests/ui/min_rust_version_invalid_attr.stderr
index 93370a0fa9c..675b7803152 100644
--- a/tests/ui/min_rust_version_invalid_attr.stderr
+++ b/tests/ui/min_rust_version_invalid_attr.stderr
@@ -4,7 +4,7 @@ error: `invalid.version` is not a valid Rust version
 LL | #![clippy::msrv = "invalid.version"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: `msrv` cannot be an outer attribute
+error: `invalid.version` is not a valid Rust version
   --> $DIR/min_rust_version_invalid_attr.rs:6:1
    |
 LL | #[clippy::msrv = "invalid.version"]
diff --git a/tests/ui/misnamed_getters.rs b/tests/ui/misnamed_getters.rs
new file mode 100644
index 00000000000..03e7dac7df9
--- /dev/null
+++ b/tests/ui/misnamed_getters.rs
@@ -0,0 +1,124 @@
+#![allow(unused)]
+#![warn(clippy::misnamed_getters)]
+
+struct A {
+    a: u8,
+    b: u8,
+    c: u8,
+}
+
+impl A {
+    fn a(&self) -> &u8 {
+        &self.b
+    }
+    fn a_mut(&mut self) -> &mut u8 {
+        &mut self.b
+    }
+
+    fn b(self) -> u8 {
+        self.a
+    }
+
+    fn b_mut(&mut self) -> &mut u8 {
+        &mut self.a
+    }
+
+    fn c(&self) -> &u8 {
+        &self.b
+    }
+
+    fn c_mut(&mut self) -> &mut u8 {
+        &mut self.a
+    }
+}
+
+union B {
+    a: u8,
+    b: u8,
+}
+
+impl B {
+    unsafe fn a(&self) -> &u8 {
+        &self.b
+    }
+    unsafe fn a_mut(&mut self) -> &mut u8 {
+        &mut self.b
+    }
+
+    unsafe fn b(self) -> u8 {
+        self.a
+    }
+
+    unsafe fn b_mut(&mut self) -> &mut u8 {
+        &mut self.a
+    }
+
+    unsafe fn c(&self) -> &u8 {
+        &self.b
+    }
+
+    unsafe fn c_mut(&mut self) -> &mut u8 {
+        &mut self.a
+    }
+
+    unsafe fn a_unchecked(&self) -> &u8 {
+        &self.b
+    }
+    unsafe fn a_unchecked_mut(&mut self) -> &mut u8 {
+        &mut self.b
+    }
+
+    unsafe fn b_unchecked(self) -> u8 {
+        self.a
+    }
+
+    unsafe fn b_unchecked_mut(&mut self) -> &mut u8 {
+        &mut self.a
+    }
+
+    unsafe fn c_unchecked(&self) -> &u8 {
+        &self.b
+    }
+
+    unsafe fn c_unchecked_mut(&mut self) -> &mut u8 {
+        &mut self.a
+    }
+}
+
+struct D {
+    d: u8,
+    inner: A,
+}
+
+impl core::ops::Deref for D {
+    type Target = A;
+    fn deref(&self) -> &A {
+        &self.inner
+    }
+}
+
+impl core::ops::DerefMut for D {
+    fn deref_mut(&mut self) -> &mut A {
+        &mut self.inner
+    }
+}
+
+impl D {
+    fn a(&self) -> &u8 {
+        &self.b
+    }
+    fn a_mut(&mut self) -> &mut u8 {
+        &mut self.b
+    }
+
+    fn d(&self) -> &u8 {
+        &self.b
+    }
+    fn d_mut(&mut self) -> &mut u8 {
+        &mut self.b
+    }
+}
+
+fn main() {
+    // test code goes here
+}
diff --git a/tests/ui/misnamed_getters.stderr b/tests/ui/misnamed_getters.stderr
new file mode 100644
index 00000000000..1e38a83d019
--- /dev/null
+++ b/tests/ui/misnamed_getters.stderr
@@ -0,0 +1,166 @@
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:11:5
+   |
+LL | /     fn a(&self) -> &u8 {
+LL | |         &self.b
+   | |         ------- help: consider using: `&self.a`
+LL | |     }
+   | |_____^
+   |
+   = note: `-D clippy::misnamed-getters` implied by `-D warnings`
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:14:5
+   |
+LL | /     fn a_mut(&mut self) -> &mut u8 {
+LL | |         &mut self.b
+   | |         ----------- help: consider using: `&mut self.a`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:18:5
+   |
+LL | /     fn b(self) -> u8 {
+LL | |         self.a
+   | |         ------ help: consider using: `self.b`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:22:5
+   |
+LL | /     fn b_mut(&mut self) -> &mut u8 {
+LL | |         &mut self.a
+   | |         ----------- help: consider using: `&mut self.b`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:26:5
+   |
+LL | /     fn c(&self) -> &u8 {
+LL | |         &self.b
+   | |         ------- help: consider using: `&self.c`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:30:5
+   |
+LL | /     fn c_mut(&mut self) -> &mut u8 {
+LL | |         &mut self.a
+   | |         ----------- help: consider using: `&mut self.c`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:41:5
+   |
+LL | /     unsafe fn a(&self) -> &u8 {
+LL | |         &self.b
+   | |         ------- help: consider using: `&self.a`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:44:5
+   |
+LL | /     unsafe fn a_mut(&mut self) -> &mut u8 {
+LL | |         &mut self.b
+   | |         ----------- help: consider using: `&mut self.a`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:48:5
+   |
+LL | /     unsafe fn b(self) -> u8 {
+LL | |         self.a
+   | |         ------ help: consider using: `self.b`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:52:5
+   |
+LL | /     unsafe fn b_mut(&mut self) -> &mut u8 {
+LL | |         &mut self.a
+   | |         ----------- help: consider using: `&mut self.b`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:64:5
+   |
+LL | /     unsafe fn a_unchecked(&self) -> &u8 {
+LL | |         &self.b
+   | |         ------- help: consider using: `&self.a`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:67:5
+   |
+LL | /     unsafe fn a_unchecked_mut(&mut self) -> &mut u8 {
+LL | |         &mut self.b
+   | |         ----------- help: consider using: `&mut self.a`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:71:5
+   |
+LL | /     unsafe fn b_unchecked(self) -> u8 {
+LL | |         self.a
+   | |         ------ help: consider using: `self.b`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:75:5
+   |
+LL | /     unsafe fn b_unchecked_mut(&mut self) -> &mut u8 {
+LL | |         &mut self.a
+   | |         ----------- help: consider using: `&mut self.b`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:107:5
+   |
+LL | /     fn a(&self) -> &u8 {
+LL | |         &self.b
+   | |         ------- help: consider using: `&self.a`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:110:5
+   |
+LL | /     fn a_mut(&mut self) -> &mut u8 {
+LL | |         &mut self.b
+   | |         ----------- help: consider using: `&mut self.a`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:114:5
+   |
+LL | /     fn d(&self) -> &u8 {
+LL | |         &self.b
+   | |         ------- help: consider using: `&self.d`
+LL | |     }
+   | |_____^
+
+error: getter function appears to return the wrong field
+  --> $DIR/misnamed_getters.rs:117:5
+   |
+LL | /     fn d_mut(&mut self) -> &mut u8 {
+LL | |         &mut self.b
+   | |         ----------- help: consider using: `&mut self.d`
+LL | |     }
+   | |_____^
+
+error: aborting due to 18 previous errors
+
diff --git a/tests/ui/missing_const_for_fn/cant_be_const.rs b/tests/ui/missing_const_for_fn/cant_be_const.rs
index b950248ef94..75cace18167 100644
--- a/tests/ui/missing_const_for_fn/cant_be_const.rs
+++ b/tests/ui/missing_const_for_fn/cant_be_const.rs
@@ -7,7 +7,6 @@
 
 #![warn(clippy::missing_const_for_fn)]
 #![feature(start)]
-#![feature(custom_inner_attributes)]
 
 extern crate helper;
 extern crate proc_macro_with_span;
@@ -115,9 +114,8 @@ fn unstably_const_fn() {
     helper::unstably_const_fn()
 }
 
+#[clippy::msrv = "1.46.0"]
 mod const_fn_stabilized_after_msrv {
-    #![clippy::msrv = "1.46.0"]
-
     // Do not lint this because `u8::is_ascii_digit` is stabilized as a const function in 1.47.0.
     fn const_fn_stabilized_after_msrv(byte: u8) {
         byte.is_ascii_digit();
diff --git a/tests/ui/missing_const_for_fn/could_be_const.rs b/tests/ui/missing_const_for_fn/could_be_const.rs
index b85e8878491..0246c8622ed 100644
--- a/tests/ui/missing_const_for_fn/could_be_const.rs
+++ b/tests/ui/missing_const_for_fn/could_be_const.rs
@@ -1,6 +1,5 @@
 #![warn(clippy::missing_const_for_fn)]
 #![allow(incomplete_features, clippy::let_and_return)]
-#![feature(custom_inner_attributes)]
 
 use std::mem::transmute;
 
@@ -68,24 +67,21 @@ mod with_drop {
     }
 }
 
+#[clippy::msrv = "1.47.0"]
 mod const_fn_stabilized_before_msrv {
-    #![clippy::msrv = "1.47.0"]
-
     // This could be const because `u8::is_ascii_digit` is a stable const function in 1.47.
     fn const_fn_stabilized_before_msrv(byte: u8) {
         byte.is_ascii_digit();
     }
 }
 
+#[clippy::msrv = "1.45"]
 fn msrv_1_45() -> i32 {
-    #![clippy::msrv = "1.45"]
-
     45
 }
 
+#[clippy::msrv = "1.46"]
 fn msrv_1_46() -> i32 {
-    #![clippy::msrv = "1.46"]
-
     46
 }
 
diff --git a/tests/ui/missing_const_for_fn/could_be_const.stderr b/tests/ui/missing_const_for_fn/could_be_const.stderr
index f8e221c82f1..955e1ed2634 100644
--- a/tests/ui/missing_const_for_fn/could_be_const.stderr
+++ b/tests/ui/missing_const_for_fn/could_be_const.stderr
@@ -1,5 +1,5 @@
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:13:5
+  --> $DIR/could_be_const.rs:12:5
    |
 LL | /     pub fn new() -> Self {
 LL | |         Self { guess: 42 }
@@ -9,7 +9,7 @@ LL | |     }
    = note: `-D clippy::missing-const-for-fn` implied by `-D warnings`
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:17:5
+  --> $DIR/could_be_const.rs:16:5
    |
 LL | /     fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] {
 LL | |         b
@@ -17,7 +17,7 @@ LL | |     }
    | |_____^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:23:1
+  --> $DIR/could_be_const.rs:22:1
    |
 LL | / fn one() -> i32 {
 LL | |     1
@@ -25,7 +25,7 @@ LL | | }
    | |_^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:28:1
+  --> $DIR/could_be_const.rs:27:1
    |
 LL | / fn two() -> i32 {
 LL | |     let abc = 2;
@@ -34,7 +34,7 @@ LL | | }
    | |_^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:34:1
+  --> $DIR/could_be_const.rs:33:1
    |
 LL | / fn string() -> String {
 LL | |     String::new()
@@ -42,7 +42,7 @@ LL | | }
    | |_^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:39:1
+  --> $DIR/could_be_const.rs:38:1
    |
 LL | / unsafe fn four() -> i32 {
 LL | |     4
@@ -50,7 +50,7 @@ LL | | }
    | |_^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:44:1
+  --> $DIR/could_be_const.rs:43:1
    |
 LL | / fn generic<T>(t: T) -> T {
 LL | |     t
@@ -58,7 +58,7 @@ LL | | }
    | |_^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:52:1
+  --> $DIR/could_be_const.rs:51:1
    |
 LL | / fn generic_arr<T: Copy>(t: [T; 1]) -> T {
 LL | |     t[0]
@@ -66,7 +66,7 @@ LL | | }
    | |_^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:65:9
+  --> $DIR/could_be_const.rs:64:9
    |
 LL | /         pub fn b(self, a: &A) -> B {
 LL | |             B
@@ -74,7 +74,7 @@ LL | |         }
    | |_________^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:75:5
+  --> $DIR/could_be_const.rs:73:5
    |
 LL | /     fn const_fn_stabilized_before_msrv(byte: u8) {
 LL | |         byte.is_ascii_digit();
@@ -82,11 +82,9 @@ LL | |     }
    | |_____^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:86:1
+  --> $DIR/could_be_const.rs:84:1
    |
 LL | / fn msrv_1_46() -> i32 {
-LL | |     #![clippy::msrv = "1.46"]
-LL | |
 LL | |     46
 LL | | }
    | |_^
diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed
index 85b6b639d55..4cb7f6b687f 100644
--- a/tests/ui/needless_borrow.fixed
+++ b/tests/ui/needless_borrow.fixed
@@ -1,13 +1,13 @@
 // run-rustfix
-#![feature(custom_inner_attributes, lint_reasons)]
-
-#[warn(clippy::all, clippy::needless_borrow)]
-#[allow(unused_variables)]
-#[allow(
+#![feature(lint_reasons)]
+#![allow(
+    unused,
     clippy::uninlined_format_args,
     clippy::unnecessary_mut_passed,
     clippy::unnecessary_to_owned
 )]
+#![warn(clippy::needless_borrow)]
+
 fn main() {
     let a = 5;
     let ref_a = &a;
@@ -171,14 +171,12 @@ impl<'a> Trait for &'a str {}
 
 fn h(_: &dyn Trait) {}
 
-#[allow(dead_code)]
 fn check_expect_suppression() {
     let a = 5;
     #[expect(clippy::needless_borrow)]
     let _ = x(&&a);
 }
 
-#[allow(dead_code)]
 mod issue9160 {
     pub struct S<F> {
         f: F,
@@ -267,7 +265,6 @@ where
 }
 
 // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
-#[allow(dead_code)]
 mod copyable_iterator {
     #[derive(Clone, Copy)]
     struct Iter;
@@ -287,25 +284,20 @@ mod copyable_iterator {
     }
 }
 
+#[clippy::msrv = "1.52.0"]
 mod under_msrv {
-    #![allow(dead_code)]
-    #![clippy::msrv = "1.52.0"]
-
     fn foo() {
         let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
     }
 }
 
+#[clippy::msrv = "1.53.0"]
 mod meets_msrv {
-    #![allow(dead_code)]
-    #![clippy::msrv = "1.53.0"]
-
     fn foo() {
         let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap();
     }
 }
 
-#[allow(unused)]
 fn issue9383() {
     // Should not lint because unions need explicit deref when accessing field
     use std::mem::ManuallyDrop;
@@ -334,7 +326,6 @@ fn issue9383() {
     }
 }
 
-#[allow(dead_code)]
 fn closure_test() {
     let env = "env".to_owned();
     let arg = "arg".to_owned();
@@ -348,7 +339,6 @@ fn closure_test() {
     f(arg);
 }
 
-#[allow(dead_code)]
 mod significant_drop {
     #[derive(Debug)]
     struct X;
@@ -368,7 +358,6 @@ mod significant_drop {
     fn debug(_: impl std::fmt::Debug) {}
 }
 
-#[allow(dead_code)]
 mod used_exactly_once {
     fn foo(x: String) {
         use_x(x);
@@ -376,7 +365,6 @@ mod used_exactly_once {
     fn use_x(_: impl AsRef<str>) {}
 }
 
-#[allow(dead_code)]
 mod used_more_than_once {
     fn foo(x: String) {
         use_x(&x);
@@ -387,7 +375,6 @@ mod used_more_than_once {
 }
 
 // https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280
-#[allow(dead_code)]
 mod issue_9111 {
     struct A;
 
@@ -409,7 +396,6 @@ mod issue_9111 {
     }
 }
 
-#[allow(dead_code)]
 mod issue_9710 {
     fn main() {
         let string = String::new();
@@ -421,7 +407,6 @@ mod issue_9710 {
     fn f<T: AsRef<str>>(_: T) {}
 }
 
-#[allow(dead_code)]
 mod issue_9739 {
     fn foo<D: std::fmt::Display>(_it: impl IntoIterator<Item = D>) {}
 
@@ -434,7 +419,6 @@ mod issue_9739 {
     }
 }
 
-#[allow(dead_code)]
 mod issue_9739_method_variant {
     struct S;
 
@@ -451,7 +435,6 @@ mod issue_9739_method_variant {
     }
 }
 
-#[allow(dead_code)]
 mod issue_9782 {
     fn foo<T: AsRef<[u8]>>(t: T) {
         println!("{}", std::mem::size_of::<T>());
@@ -475,7 +458,6 @@ mod issue_9782 {
     }
 }
 
-#[allow(dead_code)]
 mod issue_9782_type_relative_variant {
     struct S;
 
@@ -493,7 +475,6 @@ mod issue_9782_type_relative_variant {
     }
 }
 
-#[allow(dead_code)]
 mod issue_9782_method_variant {
     struct S;
 
diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs
index 7b97bcf3817..9a01190ed8d 100644
--- a/tests/ui/needless_borrow.rs
+++ b/tests/ui/needless_borrow.rs
@@ -1,13 +1,13 @@
 // run-rustfix
-#![feature(custom_inner_attributes, lint_reasons)]
-
-#[warn(clippy::all, clippy::needless_borrow)]
-#[allow(unused_variables)]
-#[allow(
+#![feature(lint_reasons)]
+#![allow(
+    unused,
     clippy::uninlined_format_args,
     clippy::unnecessary_mut_passed,
     clippy::unnecessary_to_owned
 )]
+#![warn(clippy::needless_borrow)]
+
 fn main() {
     let a = 5;
     let ref_a = &a;
@@ -171,14 +171,12 @@ impl<'a> Trait for &'a str {}
 
 fn h(_: &dyn Trait) {}
 
-#[allow(dead_code)]
 fn check_expect_suppression() {
     let a = 5;
     #[expect(clippy::needless_borrow)]
     let _ = x(&&a);
 }
 
-#[allow(dead_code)]
 mod issue9160 {
     pub struct S<F> {
         f: F,
@@ -267,7 +265,6 @@ where
 }
 
 // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
-#[allow(dead_code)]
 mod copyable_iterator {
     #[derive(Clone, Copy)]
     struct Iter;
@@ -287,25 +284,20 @@ mod copyable_iterator {
     }
 }
 
+#[clippy::msrv = "1.52.0"]
 mod under_msrv {
-    #![allow(dead_code)]
-    #![clippy::msrv = "1.52.0"]
-
     fn foo() {
         let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
     }
 }
 
+#[clippy::msrv = "1.53.0"]
 mod meets_msrv {
-    #![allow(dead_code)]
-    #![clippy::msrv = "1.53.0"]
-
     fn foo() {
         let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
     }
 }
 
-#[allow(unused)]
 fn issue9383() {
     // Should not lint because unions need explicit deref when accessing field
     use std::mem::ManuallyDrop;
@@ -334,7 +326,6 @@ fn issue9383() {
     }
 }
 
-#[allow(dead_code)]
 fn closure_test() {
     let env = "env".to_owned();
     let arg = "arg".to_owned();
@@ -348,7 +339,6 @@ fn closure_test() {
     f(arg);
 }
 
-#[allow(dead_code)]
 mod significant_drop {
     #[derive(Debug)]
     struct X;
@@ -368,7 +358,6 @@ mod significant_drop {
     fn debug(_: impl std::fmt::Debug) {}
 }
 
-#[allow(dead_code)]
 mod used_exactly_once {
     fn foo(x: String) {
         use_x(&x);
@@ -376,7 +365,6 @@ mod used_exactly_once {
     fn use_x(_: impl AsRef<str>) {}
 }
 
-#[allow(dead_code)]
 mod used_more_than_once {
     fn foo(x: String) {
         use_x(&x);
@@ -387,7 +375,6 @@ mod used_more_than_once {
 }
 
 // https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280
-#[allow(dead_code)]
 mod issue_9111 {
     struct A;
 
@@ -409,7 +396,6 @@ mod issue_9111 {
     }
 }
 
-#[allow(dead_code)]
 mod issue_9710 {
     fn main() {
         let string = String::new();
@@ -421,7 +407,6 @@ mod issue_9710 {
     fn f<T: AsRef<str>>(_: T) {}
 }
 
-#[allow(dead_code)]
 mod issue_9739 {
     fn foo<D: std::fmt::Display>(_it: impl IntoIterator<Item = D>) {}
 
@@ -434,7 +419,6 @@ mod issue_9739 {
     }
 }
 
-#[allow(dead_code)]
 mod issue_9739_method_variant {
     struct S;
 
@@ -451,7 +435,6 @@ mod issue_9739_method_variant {
     }
 }
 
-#[allow(dead_code)]
 mod issue_9782 {
     fn foo<T: AsRef<[u8]>>(t: T) {
         println!("{}", std::mem::size_of::<T>());
@@ -475,7 +458,6 @@ mod issue_9782 {
     }
 }
 
-#[allow(dead_code)]
 mod issue_9782_type_relative_variant {
     struct S;
 
@@ -493,7 +475,6 @@ mod issue_9782_type_relative_variant {
     }
 }
 
-#[allow(dead_code)]
 mod issue_9782_method_variant {
     struct S;
 
diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr
index 485e6b84c86..d26c317124b 100644
--- a/tests/ui/needless_borrow.stderr
+++ b/tests/ui/needless_borrow.stderr
@@ -163,55 +163,55 @@ LL |     let _ = std::fs::write("x", &"".to_string());
    |                                 ^^^^^^^^^^^^^^^ help: change this to: `"".to_string()`
 
 error: this expression borrows a value the compiler would automatically borrow
-  --> $DIR/needless_borrow.rs:192:13
+  --> $DIR/needless_borrow.rs:190:13
    |
 LL |             (&self.f)()
    |             ^^^^^^^^^ help: change this to: `(self.f)`
 
 error: this expression borrows a value the compiler would automatically borrow
-  --> $DIR/needless_borrow.rs:201:13
+  --> $DIR/needless_borrow.rs:199:13
    |
 LL |             (&mut self.f)()
    |             ^^^^^^^^^^^^^ help: change this to: `(self.f)`
 
 error: the borrowed expression implements the required traits
-  --> $DIR/needless_borrow.rs:286:20
+  --> $DIR/needless_borrow.rs:283:20
    |
 LL |         takes_iter(&mut x)
    |                    ^^^^^^ help: change this to: `x`
 
 error: the borrowed expression implements the required traits
-  --> $DIR/needless_borrow.rs:304:55
+  --> $DIR/needless_borrow.rs:297:55
    |
 LL |         let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
    |                                                       ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]`
 
 error: the borrowed expression implements the required traits
-  --> $DIR/needless_borrow.rs:344:37
+  --> $DIR/needless_borrow.rs:335:37
    |
 LL |         let _ = std::fs::write("x", &arg);
    |                                     ^^^^ help: change this to: `arg`
 
 error: the borrowed expression implements the required traits
-  --> $DIR/needless_borrow.rs:345:37
+  --> $DIR/needless_borrow.rs:336:37
    |
 LL |         let _ = std::fs::write("x", &loc);
    |                                     ^^^^ help: change this to: `loc`
 
 error: the borrowed expression implements the required traits
-  --> $DIR/needless_borrow.rs:364:15
+  --> $DIR/needless_borrow.rs:354:15
    |
 LL |         debug(&x);
    |               ^^ help: change this to: `x`
 
 error: the borrowed expression implements the required traits
-  --> $DIR/needless_borrow.rs:374:15
+  --> $DIR/needless_borrow.rs:363:15
    |
 LL |         use_x(&x);
    |               ^^ help: change this to: `x`
 
 error: the borrowed expression implements the required traits
-  --> $DIR/needless_borrow.rs:474:13
+  --> $DIR/needless_borrow.rs:457:13
    |
 LL |         foo(&a);
    |             ^^ help: change this to: `a`
diff --git a/tests/ui/needless_question_mark.fixed b/tests/ui/needless_question_mark.fixed
index ba9d15e59d0..7eaca571992 100644
--- a/tests/ui/needless_question_mark.fixed
+++ b/tests/ui/needless_question_mark.fixed
@@ -8,7 +8,6 @@
     dead_code,
     unused_must_use
 )]
-#![feature(custom_inner_attributes)]
 
 struct TO {
     magic: Option<usize>,
diff --git a/tests/ui/needless_question_mark.rs b/tests/ui/needless_question_mark.rs
index 3a6523e8fe8..960bc7b7898 100644
--- a/tests/ui/needless_question_mark.rs
+++ b/tests/ui/needless_question_mark.rs
@@ -8,7 +8,6 @@
     dead_code,
     unused_must_use
 )]
-#![feature(custom_inner_attributes)]
 
 struct TO {
     magic: Option<usize>,
diff --git a/tests/ui/needless_question_mark.stderr b/tests/ui/needless_question_mark.stderr
index f8308e24e77..d1f89e326c6 100644
--- a/tests/ui/needless_question_mark.stderr
+++ b/tests/ui/needless_question_mark.stderr
@@ -1,5 +1,5 @@
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:23:12
+  --> $DIR/needless_question_mark.rs:22:12
    |
 LL |     return Some(to.magic?);
    |            ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic`
@@ -7,67 +7,67 @@ LL |     return Some(to.magic?);
    = note: `-D clippy::needless-question-mark` implied by `-D warnings`
 
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:31:12
+  --> $DIR/needless_question_mark.rs:30:12
    |
 LL |     return Some(to.magic?)
    |            ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic`
 
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:36:5
+  --> $DIR/needless_question_mark.rs:35:5
    |
 LL |     Some(to.magic?)
    |     ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic`
 
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:41:21
+  --> $DIR/needless_question_mark.rs:40:21
    |
 LL |     to.and_then(|t| Some(t.magic?))
    |                     ^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `t.magic`
 
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:50:9
+  --> $DIR/needless_question_mark.rs:49:9
    |
 LL |         Some(t.magic?)
    |         ^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `t.magic`
 
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:55:12
+  --> $DIR/needless_question_mark.rs:54:12
    |
 LL |     return Ok(tr.magic?);
    |            ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic`
 
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:62:12
+  --> $DIR/needless_question_mark.rs:61:12
    |
 LL |     return Ok(tr.magic?)
    |            ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic`
 
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:66:5
+  --> $DIR/needless_question_mark.rs:65:5
    |
 LL |     Ok(tr.magic?)
    |     ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic`
 
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:70:21
+  --> $DIR/needless_question_mark.rs:69:21
    |
 LL |     tr.and_then(|t| Ok(t.magic?))
    |                     ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic`
 
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:78:9
+  --> $DIR/needless_question_mark.rs:77:9
    |
 LL |         Ok(t.magic?)
    |         ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic`
 
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:85:16
+  --> $DIR/needless_question_mark.rs:84:16
    |
 LL |         return Ok(t.magic?);
    |                ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic`
 
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:120:27
+  --> $DIR/needless_question_mark.rs:119:27
    |
 LL |         || -> Option<_> { Some(Some($expr)?) }()
    |                           ^^^^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `Some($expr)`
@@ -78,13 +78,13 @@ LL |     let _x = some_and_qmark_in_macro!(x?);
    = note: this error originates in the macro `some_and_qmark_in_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:131:5
+  --> $DIR/needless_question_mark.rs:130:5
    |
 LL |     Some(to.magic?)
    |     ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic`
 
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:139:5
+  --> $DIR/needless_question_mark.rs:138:5
    |
 LL |     Ok(s.magic?)
    |     ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `s.magic`
diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed
index d2163b14fca..4386aaec49e 100644
--- a/tests/ui/needless_return.fixed
+++ b/tests/ui/needless_return.fixed
@@ -59,14 +59,11 @@ fn test_macro_call() -> i32 {
 }
 
 fn test_void_fun() {
-    
 }
 
 fn test_void_if_fun(b: bool) {
     if b {
-        
     } else {
-        
     }
 }
 
@@ -82,7 +79,6 @@ fn test_nested_match(x: u32) {
         0 => (),
         1 => {
             let _ = 42;
-            
         },
         _ => (),
     }
@@ -126,7 +122,6 @@ mod issue6501 {
 
     fn test_closure() {
         let _ = || {
-            
         };
         let _ = || {};
     }
@@ -179,14 +174,11 @@ async fn async_test_macro_call() -> i32 {
 }
 
 async fn async_test_void_fun() {
-    
 }
 
 async fn async_test_void_if_fun(b: bool) {
     if b {
-        
     } else {
-        
     }
 }
 
@@ -269,4 +261,15 @@ fn issue9503(x: usize) -> isize {
     }
 }
 
+mod issue9416 {
+    pub fn with_newline() {
+        let _ = 42;
+    }
+
+    #[rustfmt::skip]
+    pub fn oneline() {
+        let _ = 42;
+    }
+}
+
 fn main() {}
diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs
index 114414b5fac..666dc54b76b 100644
--- a/tests/ui/needless_return.rs
+++ b/tests/ui/needless_return.rs
@@ -269,4 +269,17 @@ fn issue9503(x: usize) -> isize {
     };
 }
 
+mod issue9416 {
+    pub fn with_newline() {
+        let _ = 42;
+
+        return;
+    }
+
+    #[rustfmt::skip]
+    pub fn oneline() {
+        let _ = 42; return;
+    }
+}
+
 fn main() {}
diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr
index 047fb6c2311..a8b5d86cd55 100644
--- a/tests/ui/needless_return.stderr
+++ b/tests/ui/needless_return.stderr
@@ -72,26 +72,32 @@ LL |     return the_answer!();
    = help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:62:5
+  --> $DIR/needless_return.rs:61:21
    |
-LL |     return;
-   |     ^^^^^^
+LL |   fn test_void_fun() {
+   |  _____________________^
+LL | |     return;
+   | |__________^
    |
    = help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:67:9
+  --> $DIR/needless_return.rs:66:11
    |
-LL |         return;
-   |         ^^^^^^
+LL |       if b {
+   |  ___________^
+LL | |         return;
+   | |______________^
    |
    = help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:69:9
+  --> $DIR/needless_return.rs:68:13
    |
-LL |         return;
-   |         ^^^^^^
+LL |       } else {
+   |  _____________^
+LL | |         return;
+   | |______________^
    |
    = help: remove `return`
 
@@ -104,10 +110,12 @@ LL |         _ => return,
    = help: replace `return` with a unit value
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:85:13
+  --> $DIR/needless_return.rs:84:24
    |
-LL |             return;
-   |             ^^^^^^
+LL |               let _ = 42;
+   |  ________________________^
+LL | |             return;
+   | |__________________^
    |
    = help: remove `return`
 
@@ -144,10 +152,12 @@ LL |         bar.unwrap_or_else(|_| return)
    = help: replace `return` with an empty block
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:129:13
+  --> $DIR/needless_return.rs:128:21
    |
-LL |             return;
-   |             ^^^^^^
+LL |           let _ = || {
+   |  _____________________^
+LL | |             return;
+   | |__________________^
    |
    = help: remove `return`
 
@@ -240,26 +250,32 @@ LL |     return the_answer!();
    = help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:182:5
+  --> $DIR/needless_return.rs:181:33
    |
-LL |     return;
-   |     ^^^^^^
+LL |   async fn async_test_void_fun() {
+   |  _________________________________^
+LL | |     return;
+   | |__________^
    |
    = help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:187:9
+  --> $DIR/needless_return.rs:186:11
    |
-LL |         return;
-   |         ^^^^^^
+LL |       if b {
+   |  ___________^
+LL | |         return;
+   | |______________^
    |
    = help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:189:9
+  --> $DIR/needless_return.rs:188:13
    |
-LL |         return;
-   |         ^^^^^^
+LL |       } else {
+   |  _____________^
+LL | |         return;
+   | |______________^
    |
    = help: remove `return`
 
@@ -351,5 +367,24 @@ LL |             return !*(x as *const isize);
    |
    = help: remove `return`
 
-error: aborting due to 44 previous errors
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:274:20
+   |
+LL |           let _ = 42;
+   |  ____________________^
+LL | |
+LL | |         return;
+   | |______________^
+   |
+   = help: remove `return`
+
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:281:20
+   |
+LL |         let _ = 42; return;
+   |                    ^^^^^^^
+   |
+   = help: remove `return`
+
+error: aborting due to 46 previous errors
 
diff --git a/tests/ui/needless_splitn.fixed b/tests/ui/needless_splitn.fixed
index 61f5fc4e679..5496031fefa 100644
--- a/tests/ui/needless_splitn.fixed
+++ b/tests/ui/needless_splitn.fixed
@@ -1,7 +1,6 @@
 // run-rustfix
 // edition:2018
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::needless_splitn)]
 #![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::manual_split_once)]
 
@@ -40,8 +39,8 @@ fn _question_mark(s: &str) -> Option<()> {
     Some(())
 }
 
+#[clippy::msrv = "1.51"]
 fn _test_msrv() {
-    #![clippy::msrv = "1.51"]
     // `manual_split_once` MSRV shouldn't apply to `needless_splitn`
     let _ = "key=value".split('=').nth(0).unwrap();
 }
diff --git a/tests/ui/needless_splitn.rs b/tests/ui/needless_splitn.rs
index 71d9a7077fa..35c2465bae1 100644
--- a/tests/ui/needless_splitn.rs
+++ b/tests/ui/needless_splitn.rs
@@ -1,7 +1,6 @@
 // run-rustfix
 // edition:2018
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::needless_splitn)]
 #![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::manual_split_once)]
 
@@ -40,8 +39,8 @@ fn _question_mark(s: &str) -> Option<()> {
     Some(())
 }
 
+#[clippy::msrv = "1.51"]
 fn _test_msrv() {
-    #![clippy::msrv = "1.51"]
     // `manual_split_once` MSRV shouldn't apply to `needless_splitn`
     let _ = "key=value".splitn(2, '=').nth(0).unwrap();
 }
diff --git a/tests/ui/needless_splitn.stderr b/tests/ui/needless_splitn.stderr
index f112b29e7f2..f607d8e1ab5 100644
--- a/tests/ui/needless_splitn.stderr
+++ b/tests/ui/needless_splitn.stderr
@@ -1,5 +1,5 @@
 error: unnecessary use of `splitn`
-  --> $DIR/needless_splitn.rs:15:13
+  --> $DIR/needless_splitn.rs:14:13
    |
 LL |     let _ = str.splitn(2, '=').next();
    |             ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
@@ -7,73 +7,73 @@ LL |     let _ = str.splitn(2, '=').next();
    = note: `-D clippy::needless-splitn` implied by `-D warnings`
 
 error: unnecessary use of `splitn`
-  --> $DIR/needless_splitn.rs:16:13
+  --> $DIR/needless_splitn.rs:15:13
    |
 LL |     let _ = str.splitn(2, '=').nth(0);
    |             ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
 
 error: unnecessary use of `splitn`
-  --> $DIR/needless_splitn.rs:19:18
+  --> $DIR/needless_splitn.rs:18:18
    |
 LL |     let (_, _) = str.splitn(3, '=').next_tuple().unwrap();
    |                  ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
 
 error: unnecessary use of `rsplitn`
-  --> $DIR/needless_splitn.rs:22:13
+  --> $DIR/needless_splitn.rs:21:13
    |
 LL |     let _ = str.rsplitn(2, '=').next();
    |             ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')`
 
 error: unnecessary use of `rsplitn`
-  --> $DIR/needless_splitn.rs:23:13
+  --> $DIR/needless_splitn.rs:22:13
    |
 LL |     let _ = str.rsplitn(2, '=').nth(0);
    |             ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')`
 
 error: unnecessary use of `rsplitn`
-  --> $DIR/needless_splitn.rs:26:18
+  --> $DIR/needless_splitn.rs:25:18
    |
 LL |     let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap();
    |                  ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')`
 
 error: unnecessary use of `splitn`
-  --> $DIR/needless_splitn.rs:28:13
+  --> $DIR/needless_splitn.rs:27:13
    |
 LL |     let _ = str.splitn(5, '=').next();
    |             ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
 
 error: unnecessary use of `splitn`
-  --> $DIR/needless_splitn.rs:29:13
+  --> $DIR/needless_splitn.rs:28:13
    |
 LL |     let _ = str.splitn(5, '=').nth(3);
    |             ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
 
 error: unnecessary use of `splitn`
-  --> $DIR/needless_splitn.rs:35:13
+  --> $DIR/needless_splitn.rs:34:13
    |
 LL |     let _ = s.splitn(2, '=').next()?;
    |             ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')`
 
 error: unnecessary use of `splitn`
-  --> $DIR/needless_splitn.rs:36:13
+  --> $DIR/needless_splitn.rs:35:13
    |
 LL |     let _ = s.splitn(2, '=').nth(0)?;
    |             ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')`
 
 error: unnecessary use of `rsplitn`
-  --> $DIR/needless_splitn.rs:37:13
+  --> $DIR/needless_splitn.rs:36:13
    |
 LL |     let _ = s.rsplitn(2, '=').next()?;
    |             ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')`
 
 error: unnecessary use of `rsplitn`
-  --> $DIR/needless_splitn.rs:38:13
+  --> $DIR/needless_splitn.rs:37:13
    |
 LL |     let _ = s.rsplitn(2, '=').nth(0)?;
    |             ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')`
 
 error: unnecessary use of `splitn`
-  --> $DIR/needless_splitn.rs:46:13
+  --> $DIR/needless_splitn.rs:45:13
    |
 LL |     let _ = "key=value".splitn(2, '=').nth(0).unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split('=')`
diff --git a/tests/ui/option_as_ref_deref.fixed b/tests/ui/option_as_ref_deref.fixed
index bc376d0d7fb..d124d133faa 100644
--- a/tests/ui/option_as_ref_deref.fixed
+++ b/tests/ui/option_as_ref_deref.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![allow(unused, clippy::redundant_clone)]
 #![warn(clippy::option_as_ref_deref)]
 
@@ -44,16 +43,14 @@ fn main() {
     let _ = opt.as_deref();
 }
 
+#[clippy::msrv = "1.39"]
 fn msrv_1_39() {
-    #![clippy::msrv = "1.39"]
-
     let opt = Some(String::from("123"));
     let _ = opt.as_ref().map(String::as_str);
 }
 
+#[clippy::msrv = "1.40"]
 fn msrv_1_40() {
-    #![clippy::msrv = "1.40"]
-
     let opt = Some(String::from("123"));
     let _ = opt.as_deref();
 }
diff --git a/tests/ui/option_as_ref_deref.rs b/tests/ui/option_as_ref_deref.rs
index ba3a2eedc22..86e354c6716 100644
--- a/tests/ui/option_as_ref_deref.rs
+++ b/tests/ui/option_as_ref_deref.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![allow(unused, clippy::redundant_clone)]
 #![warn(clippy::option_as_ref_deref)]
 
@@ -47,16 +46,14 @@ fn main() {
     let _ = opt.as_ref().map(std::ops::Deref::deref);
 }
 
+#[clippy::msrv = "1.39"]
 fn msrv_1_39() {
-    #![clippy::msrv = "1.39"]
-
     let opt = Some(String::from("123"));
     let _ = opt.as_ref().map(String::as_str);
 }
 
+#[clippy::msrv = "1.40"]
 fn msrv_1_40() {
-    #![clippy::msrv = "1.40"]
-
     let opt = Some(String::from("123"));
     let _ = opt.as_ref().map(String::as_str);
 }
diff --git a/tests/ui/option_as_ref_deref.stderr b/tests/ui/option_as_ref_deref.stderr
index 7de8b3b6ba4..e471b56eea8 100644
--- a/tests/ui/option_as_ref_deref.stderr
+++ b/tests/ui/option_as_ref_deref.stderr
@@ -1,5 +1,5 @@
 error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead
-  --> $DIR/option_as_ref_deref.rs:14:13
+  --> $DIR/option_as_ref_deref.rs:13:13
    |
 LL |     let _ = opt.clone().as_ref().map(Deref::deref).map(str::len);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.clone().as_deref()`
@@ -7,7 +7,7 @@ LL |     let _ = opt.clone().as_ref().map(Deref::deref).map(str::len);
    = note: `-D clippy::option-as-ref-deref` implied by `-D warnings`
 
 error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead
-  --> $DIR/option_as_ref_deref.rs:17:13
+  --> $DIR/option_as_ref_deref.rs:16:13
    |
 LL |       let _ = opt.clone()
    |  _____________^
@@ -17,97 +17,97 @@ LL | |         )
    | |_________^ help: try using as_deref instead: `opt.clone().as_deref()`
 
 error: called `.as_mut().map(DerefMut::deref_mut)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead
-  --> $DIR/option_as_ref_deref.rs:23:13
+  --> $DIR/option_as_ref_deref.rs:22:13
    |
 LL |     let _ = opt.as_mut().map(DerefMut::deref_mut);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()`
 
 error: called `.as_ref().map(String::as_str)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
-  --> $DIR/option_as_ref_deref.rs:25:13
+  --> $DIR/option_as_ref_deref.rs:24:13
    |
 LL |     let _ = opt.as_ref().map(String::as_str);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
 
 error: called `.as_ref().map(|x| x.as_str())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
-  --> $DIR/option_as_ref_deref.rs:26:13
+  --> $DIR/option_as_ref_deref.rs:25:13
    |
 LL |     let _ = opt.as_ref().map(|x| x.as_str());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
 
 error: called `.as_mut().map(String::as_mut_str)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead
-  --> $DIR/option_as_ref_deref.rs:27:13
+  --> $DIR/option_as_ref_deref.rs:26:13
    |
 LL |     let _ = opt.as_mut().map(String::as_mut_str);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()`
 
 error: called `.as_mut().map(|x| x.as_mut_str())` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead
-  --> $DIR/option_as_ref_deref.rs:28:13
+  --> $DIR/option_as_ref_deref.rs:27:13
    |
 LL |     let _ = opt.as_mut().map(|x| x.as_mut_str());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()`
 
 error: called `.as_ref().map(CString::as_c_str)` on an Option value. This can be done more directly by calling `Some(CString::new(vec![]).unwrap()).as_deref()` instead
-  --> $DIR/option_as_ref_deref.rs:29:13
+  --> $DIR/option_as_ref_deref.rs:28:13
    |
 LL |     let _ = Some(CString::new(vec![]).unwrap()).as_ref().map(CString::as_c_str);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(CString::new(vec![]).unwrap()).as_deref()`
 
 error: called `.as_ref().map(OsString::as_os_str)` on an Option value. This can be done more directly by calling `Some(OsString::new()).as_deref()` instead
-  --> $DIR/option_as_ref_deref.rs:30:13
+  --> $DIR/option_as_ref_deref.rs:29:13
    |
 LL |     let _ = Some(OsString::new()).as_ref().map(OsString::as_os_str);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(OsString::new()).as_deref()`
 
 error: called `.as_ref().map(PathBuf::as_path)` on an Option value. This can be done more directly by calling `Some(PathBuf::new()).as_deref()` instead
-  --> $DIR/option_as_ref_deref.rs:31:13
+  --> $DIR/option_as_ref_deref.rs:30:13
    |
 LL |     let _ = Some(PathBuf::new()).as_ref().map(PathBuf::as_path);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(PathBuf::new()).as_deref()`
 
 error: called `.as_ref().map(Vec::as_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref()` instead
-  --> $DIR/option_as_ref_deref.rs:32:13
+  --> $DIR/option_as_ref_deref.rs:31:13
    |
 LL |     let _ = Some(Vec::<()>::new()).as_ref().map(Vec::as_slice);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(Vec::<()>::new()).as_deref()`
 
 error: called `.as_mut().map(Vec::as_mut_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref_mut()` instead
-  --> $DIR/option_as_ref_deref.rs:33:13
+  --> $DIR/option_as_ref_deref.rs:32:13
    |
 LL |     let _ = Some(Vec::<()>::new()).as_mut().map(Vec::as_mut_slice);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `Some(Vec::<()>::new()).as_deref_mut()`
 
 error: called `.as_ref().map(|x| x.deref())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
-  --> $DIR/option_as_ref_deref.rs:35:13
+  --> $DIR/option_as_ref_deref.rs:34:13
    |
 LL |     let _ = opt.as_ref().map(|x| x.deref());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
 
 error: called `.as_mut().map(|x| x.deref_mut())` on an Option value. This can be done more directly by calling `opt.clone().as_deref_mut()` instead
-  --> $DIR/option_as_ref_deref.rs:36:13
+  --> $DIR/option_as_ref_deref.rs:35:13
    |
 LL |     let _ = opt.clone().as_mut().map(|x| x.deref_mut()).map(|x| x.len());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.clone().as_deref_mut()`
 
 error: called `.as_ref().map(|x| &**x)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
-  --> $DIR/option_as_ref_deref.rs:43:13
+  --> $DIR/option_as_ref_deref.rs:42:13
    |
 LL |     let _ = opt.as_ref().map(|x| &**x);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
 
 error: called `.as_mut().map(|x| &mut **x)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead
-  --> $DIR/option_as_ref_deref.rs:44:13
+  --> $DIR/option_as_ref_deref.rs:43:13
    |
 LL |     let _ = opt.as_mut().map(|x| &mut **x);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()`
 
 error: called `.as_ref().map(std::ops::Deref::deref)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
-  --> $DIR/option_as_ref_deref.rs:47:13
+  --> $DIR/option_as_ref_deref.rs:46:13
    |
 LL |     let _ = opt.as_ref().map(std::ops::Deref::deref);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
 
 error: called `.as_ref().map(String::as_str)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
-  --> $DIR/option_as_ref_deref.rs:61:13
+  --> $DIR/option_as_ref_deref.rs:58:13
    |
 LL |     let _ = opt.as_ref().map(String::as_str);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
diff --git a/tests/ui/ptr_as_ptr.fixed b/tests/ui/ptr_as_ptr.fixed
index bea6be66a8e..df36a9b842b 100644
--- a/tests/ui/ptr_as_ptr.fixed
+++ b/tests/ui/ptr_as_ptr.fixed
@@ -2,7 +2,6 @@
 // aux-build:macro_rules.rs
 
 #![warn(clippy::ptr_as_ptr)]
-#![feature(custom_inner_attributes)]
 
 extern crate macro_rules;
 
@@ -45,8 +44,8 @@ fn main() {
     let _ = macro_rules::ptr_as_ptr_cast!(ptr);
 }
 
+#[clippy::msrv = "1.37"]
 fn _msrv_1_37() {
-    #![clippy::msrv = "1.37"]
     let ptr: *const u32 = &42_u32;
     let mut_ptr: *mut u32 = &mut 42_u32;
 
@@ -55,8 +54,8 @@ fn _msrv_1_37() {
     let _ = mut_ptr as *mut i32;
 }
 
+#[clippy::msrv = "1.38"]
 fn _msrv_1_38() {
-    #![clippy::msrv = "1.38"]
     let ptr: *const u32 = &42_u32;
     let mut_ptr: *mut u32 = &mut 42_u32;
 
diff --git a/tests/ui/ptr_as_ptr.rs b/tests/ui/ptr_as_ptr.rs
index ca2616b0069..302c66462d9 100644
--- a/tests/ui/ptr_as_ptr.rs
+++ b/tests/ui/ptr_as_ptr.rs
@@ -2,7 +2,6 @@
 // aux-build:macro_rules.rs
 
 #![warn(clippy::ptr_as_ptr)]
-#![feature(custom_inner_attributes)]
 
 extern crate macro_rules;
 
@@ -45,8 +44,8 @@ fn main() {
     let _ = macro_rules::ptr_as_ptr_cast!(ptr);
 }
 
+#[clippy::msrv = "1.37"]
 fn _msrv_1_37() {
-    #![clippy::msrv = "1.37"]
     let ptr: *const u32 = &42_u32;
     let mut_ptr: *mut u32 = &mut 42_u32;
 
@@ -55,8 +54,8 @@ fn _msrv_1_37() {
     let _ = mut_ptr as *mut i32;
 }
 
+#[clippy::msrv = "1.38"]
 fn _msrv_1_38() {
-    #![clippy::msrv = "1.38"]
     let ptr: *const u32 = &42_u32;
     let mut_ptr: *mut u32 = &mut 42_u32;
 
diff --git a/tests/ui/ptr_as_ptr.stderr b/tests/ui/ptr_as_ptr.stderr
index c58c55cfd83..a68e1cab6d3 100644
--- a/tests/ui/ptr_as_ptr.stderr
+++ b/tests/ui/ptr_as_ptr.stderr
@@ -1,5 +1,5 @@
 error: `as` casting between raw pointers without changing its mutability
-  --> $DIR/ptr_as_ptr.rs:19:13
+  --> $DIR/ptr_as_ptr.rs:18:13
    |
 LL |     let _ = ptr as *const i32;
    |             ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::<i32>()`
@@ -7,31 +7,31 @@ LL |     let _ = ptr as *const i32;
    = note: `-D clippy::ptr-as-ptr` implied by `-D warnings`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> $DIR/ptr_as_ptr.rs:20:13
+  --> $DIR/ptr_as_ptr.rs:19:13
    |
 LL |     let _ = mut_ptr as *mut i32;
    |             ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::<i32>()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> $DIR/ptr_as_ptr.rs:25:17
+  --> $DIR/ptr_as_ptr.rs:24:17
    |
 LL |         let _ = *ptr_ptr as *const i32;
    |                 ^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `(*ptr_ptr).cast::<i32>()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> $DIR/ptr_as_ptr.rs:38:25
+  --> $DIR/ptr_as_ptr.rs:37:25
    |
 LL |     let _: *const i32 = ptr as *const _;
    |                         ^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> $DIR/ptr_as_ptr.rs:39:23
+  --> $DIR/ptr_as_ptr.rs:38:23
    |
 LL |     let _: *mut i32 = mut_ptr as _;
    |                       ^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> $DIR/ptr_as_ptr.rs:11:9
+  --> $DIR/ptr_as_ptr.rs:10:9
    |
 LL |         $ptr as *const i32
    |         ^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `$ptr.cast::<i32>()`
@@ -42,13 +42,13 @@ LL |     let _ = cast_it!(ptr);
    = note: this error originates in the macro `cast_it` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: `as` casting between raw pointers without changing its mutability
-  --> $DIR/ptr_as_ptr.rs:63:13
+  --> $DIR/ptr_as_ptr.rs:62:13
    |
 LL |     let _ = ptr as *const i32;
    |             ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::<i32>()`
 
 error: `as` casting between raw pointers without changing its mutability
-  --> $DIR/ptr_as_ptr.rs:64:13
+  --> $DIR/ptr_as_ptr.rs:63:13
    |
 LL |     let _ = mut_ptr as *mut i32;
    |             ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::<i32>()`
diff --git a/tests/ui/range_contains.fixed b/tests/ui/range_contains.fixed
index 824f00cb99e..4923731fe45 100644
--- a/tests/ui/range_contains.fixed
+++ b/tests/ui/range_contains.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::manual_range_contains)]
 #![allow(unused)]
 #![allow(clippy::no_effect)]
@@ -65,16 +64,14 @@ pub const fn in_range(a: i32) -> bool {
     3 <= a && a <= 20
 }
 
+#[clippy::msrv = "1.34"]
 fn msrv_1_34() {
-    #![clippy::msrv = "1.34"]
-
     let x = 5;
     x >= 8 && x < 34;
 }
 
+#[clippy::msrv = "1.35"]
 fn msrv_1_35() {
-    #![clippy::msrv = "1.35"]
-
     let x = 5;
     (8..35).contains(&x);
 }
diff --git a/tests/ui/range_contains.rs b/tests/ui/range_contains.rs
index df925eeadfe..d623ccb5da6 100644
--- a/tests/ui/range_contains.rs
+++ b/tests/ui/range_contains.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::manual_range_contains)]
 #![allow(unused)]
 #![allow(clippy::no_effect)]
@@ -65,16 +64,14 @@ pub const fn in_range(a: i32) -> bool {
     3 <= a && a <= 20
 }
 
+#[clippy::msrv = "1.34"]
 fn msrv_1_34() {
-    #![clippy::msrv = "1.34"]
-
     let x = 5;
     x >= 8 && x < 34;
 }
 
+#[clippy::msrv = "1.35"]
 fn msrv_1_35() {
-    #![clippy::msrv = "1.35"]
-
     let x = 5;
     x >= 8 && x < 35;
 }
diff --git a/tests/ui/range_contains.stderr b/tests/ui/range_contains.stderr
index 9689e665b05..ea34023a466 100644
--- a/tests/ui/range_contains.stderr
+++ b/tests/ui/range_contains.stderr
@@ -1,5 +1,5 @@
 error: manual `Range::contains` implementation
-  --> $DIR/range_contains.rs:14:5
+  --> $DIR/range_contains.rs:13:5
    |
 LL |     x >= 8 && x < 12;
    |     ^^^^^^^^^^^^^^^^ help: use: `(8..12).contains(&x)`
@@ -7,121 +7,121 @@ LL |     x >= 8 && x < 12;
    = note: `-D clippy::manual-range-contains` implied by `-D warnings`
 
 error: manual `Range::contains` implementation
-  --> $DIR/range_contains.rs:15:5
+  --> $DIR/range_contains.rs:14:5
    |
 LL |     x < 42 && x >= 21;
    |     ^^^^^^^^^^^^^^^^^ help: use: `(21..42).contains(&x)`
 
 error: manual `Range::contains` implementation
-  --> $DIR/range_contains.rs:16:5
+  --> $DIR/range_contains.rs:15:5
    |
 LL |     100 > x && 1 <= x;
    |     ^^^^^^^^^^^^^^^^^ help: use: `(1..100).contains(&x)`
 
 error: manual `RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:19:5
+  --> $DIR/range_contains.rs:18:5
    |
 LL |     x >= 9 && x <= 99;
    |     ^^^^^^^^^^^^^^^^^ help: use: `(9..=99).contains(&x)`
 
 error: manual `RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:20:5
+  --> $DIR/range_contains.rs:19:5
    |
 LL |     x <= 33 && x >= 1;
    |     ^^^^^^^^^^^^^^^^^ help: use: `(1..=33).contains(&x)`
 
 error: manual `RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:21:5
+  --> $DIR/range_contains.rs:20:5
    |
 LL |     999 >= x && 1 <= x;
    |     ^^^^^^^^^^^^^^^^^^ help: use: `(1..=999).contains(&x)`
 
 error: manual `!Range::contains` implementation
-  --> $DIR/range_contains.rs:24:5
+  --> $DIR/range_contains.rs:23:5
    |
 LL |     x < 8 || x >= 12;
    |     ^^^^^^^^^^^^^^^^ help: use: `!(8..12).contains(&x)`
 
 error: manual `!Range::contains` implementation
-  --> $DIR/range_contains.rs:25:5
+  --> $DIR/range_contains.rs:24:5
    |
 LL |     x >= 42 || x < 21;
    |     ^^^^^^^^^^^^^^^^^ help: use: `!(21..42).contains(&x)`
 
 error: manual `!Range::contains` implementation
-  --> $DIR/range_contains.rs:26:5
+  --> $DIR/range_contains.rs:25:5
    |
 LL |     100 <= x || 1 > x;
    |     ^^^^^^^^^^^^^^^^^ help: use: `!(1..100).contains(&x)`
 
 error: manual `!RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:29:5
+  --> $DIR/range_contains.rs:28:5
    |
 LL |     x < 9 || x > 99;
    |     ^^^^^^^^^^^^^^^ help: use: `!(9..=99).contains(&x)`
 
 error: manual `!RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:30:5
+  --> $DIR/range_contains.rs:29:5
    |
 LL |     x > 33 || x < 1;
    |     ^^^^^^^^^^^^^^^ help: use: `!(1..=33).contains(&x)`
 
 error: manual `!RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:31:5
+  --> $DIR/range_contains.rs:30:5
    |
 LL |     999 < x || 1 > x;
    |     ^^^^^^^^^^^^^^^^ help: use: `!(1..=999).contains(&x)`
 
 error: manual `Range::contains` implementation
-  --> $DIR/range_contains.rs:46:5
+  --> $DIR/range_contains.rs:45:5
    |
 LL |     y >= 0. && y < 1.;
    |     ^^^^^^^^^^^^^^^^^ help: use: `(0. ..1.).contains(&y)`
 
 error: manual `!RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:47:5
+  --> $DIR/range_contains.rs:46:5
    |
 LL |     y < 0. || y > 1.;
    |     ^^^^^^^^^^^^^^^^ help: use: `!(0. ..=1.).contains(&y)`
 
 error: manual `RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:50:5
+  --> $DIR/range_contains.rs:49:5
    |
 LL |     x >= -10 && x <= 10;
    |     ^^^^^^^^^^^^^^^^^^^ help: use: `(-10..=10).contains(&x)`
 
 error: manual `RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:52:5
+  --> $DIR/range_contains.rs:51:5
    |
 LL |     y >= -3. && y <= 3.;
    |     ^^^^^^^^^^^^^^^^^^^ help: use: `(-3. ..=3.).contains(&y)`
 
 error: manual `RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:57:30
+  --> $DIR/range_contains.rs:56:30
    |
 LL |     (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10);
    |                              ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&z)`
 
 error: manual `RangeInclusive::contains` implementation
-  --> $DIR/range_contains.rs:57:5
+  --> $DIR/range_contains.rs:56:5
    |
 LL |     (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10);
    |     ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&x)`
 
 error: manual `!Range::contains` implementation
-  --> $DIR/range_contains.rs:58:29
+  --> $DIR/range_contains.rs:57:29
    |
 LL |     (x < 0) || (x >= 10) || (z < 0) || (z >= 10);
    |                             ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&z)`
 
 error: manual `!Range::contains` implementation
-  --> $DIR/range_contains.rs:58:5
+  --> $DIR/range_contains.rs:57:5
    |
 LL |     (x < 0) || (x >= 10) || (z < 0) || (z >= 10);
    |     ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&x)`
 
 error: manual `Range::contains` implementation
-  --> $DIR/range_contains.rs:79:5
+  --> $DIR/range_contains.rs:76:5
    |
 LL |     x >= 8 && x < 35;
    |     ^^^^^^^^^^^^^^^^ help: use: `(8..35).contains(&x)`
diff --git a/tests/ui/redundant_closure_call_fixable.fixed b/tests/ui/redundant_closure_call_fixable.fixed
index 7cd687c95a0..c0e49ff4caa 100644
--- a/tests/ui/redundant_closure_call_fixable.fixed
+++ b/tests/ui/redundant_closure_call_fixable.fixed
@@ -25,4 +25,16 @@ fn main() {
         x * y
     };
     let d = async { something().await };
+
+    macro_rules! m {
+        () => {
+            0
+        };
+    }
+    macro_rules! m2 {
+        () => {
+            m!()
+        };
+    }
+    m2!();
 }
diff --git a/tests/ui/redundant_closure_call_fixable.rs b/tests/ui/redundant_closure_call_fixable.rs
index 37e4d223864..9e6e54348a8 100644
--- a/tests/ui/redundant_closure_call_fixable.rs
+++ b/tests/ui/redundant_closure_call_fixable.rs
@@ -25,4 +25,16 @@ fn main() {
         x * y
     })();
     let d = (async || something().await)();
+
+    macro_rules! m {
+        () => {
+            (|| 0)()
+        };
+    }
+    macro_rules! m2 {
+        () => {
+            (|| m!())()
+        };
+    }
+    m2!();
 }
diff --git a/tests/ui/redundant_closure_call_fixable.stderr b/tests/ui/redundant_closure_call_fixable.stderr
index 56a8e57c0c3..d71bcba2a82 100644
--- a/tests/ui/redundant_closure_call_fixable.stderr
+++ b/tests/ui/redundant_closure_call_fixable.stderr
@@ -52,5 +52,27 @@ error: try not to call a closure in the expression where it is declared
 LL |     let d = (async || something().await)();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async { something().await }`
 
-error: aborting due to 4 previous errors
+error: try not to call a closure in the expression where it is declared
+  --> $DIR/redundant_closure_call_fixable.rs:36:13
+   |
+LL |             (|| m!())()
+   |             ^^^^^^^^^^^ help: try doing something like: `m!()`
+...
+LL |     m2!();
+   |     ----- in this macro invocation
+   |
+   = 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
+   |
+LL |             (|| 0)()
+   |             ^^^^^^^^ help: try doing something like: `0`
+...
+LL |     m2!();
+   |     ----- in this macro invocation
+   |
+   = note: this error originates in the macro `m` which comes from the expansion of the macro `m2` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 6 previous errors
 
diff --git a/tests/ui/redundant_field_names.fixed b/tests/ui/redundant_field_names.fixed
index 34ab552cb1d..ec7f8ae923a 100644
--- a/tests/ui/redundant_field_names.fixed
+++ b/tests/ui/redundant_field_names.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::redundant_field_names)]
 #![allow(clippy::no_effect, dead_code, unused_variables)]
 
@@ -72,16 +71,14 @@ fn issue_3476() {
     S { foo: foo::<i32> };
 }
 
+#[clippy::msrv = "1.16"]
 fn msrv_1_16() {
-    #![clippy::msrv = "1.16"]
-
     let start = 0;
     let _ = RangeFrom { start: start };
 }
 
+#[clippy::msrv = "1.17"]
 fn msrv_1_17() {
-    #![clippy::msrv = "1.17"]
-
     let start = 0;
     let _ = RangeFrom { start };
 }
diff --git a/tests/ui/redundant_field_names.rs b/tests/ui/redundant_field_names.rs
index a051b1f96f0..73122016cf6 100644
--- a/tests/ui/redundant_field_names.rs
+++ b/tests/ui/redundant_field_names.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::redundant_field_names)]
 #![allow(clippy::no_effect, dead_code, unused_variables)]
 
@@ -72,16 +71,14 @@ fn issue_3476() {
     S { foo: foo::<i32> };
 }
 
+#[clippy::msrv = "1.16"]
 fn msrv_1_16() {
-    #![clippy::msrv = "1.16"]
-
     let start = 0;
     let _ = RangeFrom { start: start };
 }
 
+#[clippy::msrv = "1.17"]
 fn msrv_1_17() {
-    #![clippy::msrv = "1.17"]
-
     let start = 0;
     let _ = RangeFrom { start: start };
 }
diff --git a/tests/ui/redundant_field_names.stderr b/tests/ui/redundant_field_names.stderr
index 8b82e062b93..00a72c50cf7 100644
--- a/tests/ui/redundant_field_names.stderr
+++ b/tests/ui/redundant_field_names.stderr
@@ -1,5 +1,5 @@
 error: redundant field names in struct initialization
-  --> $DIR/redundant_field_names.rs:36:9
+  --> $DIR/redundant_field_names.rs:35:9
    |
 LL |         gender: gender,
    |         ^^^^^^^^^^^^^^ help: replace it with: `gender`
@@ -7,43 +7,43 @@ LL |         gender: gender,
    = note: `-D clippy::redundant-field-names` implied by `-D warnings`
 
 error: redundant field names in struct initialization
-  --> $DIR/redundant_field_names.rs:37:9
+  --> $DIR/redundant_field_names.rs:36:9
    |
 LL |         age: age,
    |         ^^^^^^^^ help: replace it with: `age`
 
 error: redundant field names in struct initialization
-  --> $DIR/redundant_field_names.rs:58:25
+  --> $DIR/redundant_field_names.rs:57:25
    |
 LL |     let _ = RangeFrom { start: start };
    |                         ^^^^^^^^^^^^ help: replace it with: `start`
 
 error: redundant field names in struct initialization
-  --> $DIR/redundant_field_names.rs:59:23
+  --> $DIR/redundant_field_names.rs:58:23
    |
 LL |     let _ = RangeTo { end: end };
    |                       ^^^^^^^^ help: replace it with: `end`
 
 error: redundant field names in struct initialization
-  --> $DIR/redundant_field_names.rs:60:21
+  --> $DIR/redundant_field_names.rs:59:21
    |
 LL |     let _ = Range { start: start, end: end };
    |                     ^^^^^^^^^^^^ help: replace it with: `start`
 
 error: redundant field names in struct initialization
-  --> $DIR/redundant_field_names.rs:60:35
+  --> $DIR/redundant_field_names.rs:59:35
    |
 LL |     let _ = Range { start: start, end: end };
    |                                   ^^^^^^^^ help: replace it with: `end`
 
 error: redundant field names in struct initialization
-  --> $DIR/redundant_field_names.rs:62:32
+  --> $DIR/redundant_field_names.rs:61:32
    |
 LL |     let _ = RangeToInclusive { end: end };
    |                                ^^^^^^^^ help: replace it with: `end`
 
 error: redundant field names in struct initialization
-  --> $DIR/redundant_field_names.rs:86:25
+  --> $DIR/redundant_field_names.rs:83:25
    |
 LL |     let _ = RangeFrom { start: start };
    |                         ^^^^^^^^^^^^ help: replace it with: `start`
diff --git a/tests/ui/redundant_static_lifetimes.fixed b/tests/ui/redundant_static_lifetimes.fixed
index 42110dbe81e..4c5846fe837 100644
--- a/tests/ui/redundant_static_lifetimes.fixed
+++ b/tests/ui/redundant_static_lifetimes.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![allow(unused)]
 
 #[derive(Debug)]
@@ -56,14 +55,12 @@ impl Bar for Foo {
     const TRAIT_VAR: &'static str = "foo";
 }
 
+#[clippy::msrv = "1.16"]
 fn msrv_1_16() {
-    #![clippy::msrv = "1.16"]
-
     static V: &'static u8 = &16;
 }
 
+#[clippy::msrv = "1.17"]
 fn msrv_1_17() {
-    #![clippy::msrv = "1.17"]
-
     static V: &u8 = &17;
 }
diff --git a/tests/ui/redundant_static_lifetimes.rs b/tests/ui/redundant_static_lifetimes.rs
index bc5200bc862..64a66be1a83 100644
--- a/tests/ui/redundant_static_lifetimes.rs
+++ b/tests/ui/redundant_static_lifetimes.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![allow(unused)]
 
 #[derive(Debug)]
@@ -56,14 +55,12 @@ impl Bar for Foo {
     const TRAIT_VAR: &'static str = "foo";
 }
 
+#[clippy::msrv = "1.16"]
 fn msrv_1_16() {
-    #![clippy::msrv = "1.16"]
-
     static V: &'static u8 = &16;
 }
 
+#[clippy::msrv = "1.17"]
 fn msrv_1_17() {
-    #![clippy::msrv = "1.17"]
-
     static V: &'static u8 = &17;
 }
diff --git a/tests/ui/redundant_static_lifetimes.stderr b/tests/ui/redundant_static_lifetimes.stderr
index 735113460d2..0938ebf783f 100644
--- a/tests/ui/redundant_static_lifetimes.stderr
+++ b/tests/ui/redundant_static_lifetimes.stderr
@@ -1,5 +1,5 @@
 error: constants have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:9:17
+  --> $DIR/redundant_static_lifetimes.rs:8:17
    |
 LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static.
    |                -^^^^^^^---- help: consider removing `'static`: `&str`
@@ -7,97 +7,97 @@ LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removin
    = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings`
 
 error: constants have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:13:21
+  --> $DIR/redundant_static_lifetimes.rs:12:21
    |
 LL | const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
    |                    -^^^^^^^---- help: consider removing `'static`: `&str`
 
 error: constants have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:15:32
+  --> $DIR/redundant_static_lifetimes.rs:14:32
    |
 LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
    |                               -^^^^^^^---- help: consider removing `'static`: `&str`
 
 error: constants have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:15:47
+  --> $DIR/redundant_static_lifetimes.rs:14:47
    |
 LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
    |                                              -^^^^^^^---- help: consider removing `'static`: `&str`
 
 error: constants have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:17:17
+  --> $DIR/redundant_static_lifetimes.rs:16:17
    |
 LL | const VAR_SIX: &'static u8 = &5;
    |                -^^^^^^^--- help: consider removing `'static`: `&u8`
 
 error: constants have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:19:20
+  --> $DIR/redundant_static_lifetimes.rs:18:20
    |
 LL | const VAR_HEIGHT: &'static Foo = &Foo {};
    |                   -^^^^^^^---- help: consider removing `'static`: `&Foo`
 
 error: constants have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:21:19
+  --> $DIR/redundant_static_lifetimes.rs:20:19
    |
 LL | const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static.
    |                  -^^^^^^^----- help: consider removing `'static`: `&[u8]`
 
 error: constants have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:23:19
+  --> $DIR/redundant_static_lifetimes.rs:22:19
    |
 LL | const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
    |                  -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)`
 
 error: constants have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:25:19
+  --> $DIR/redundant_static_lifetimes.rs:24:19
    |
 LL | const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
    |                  -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]`
 
 error: statics have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:27:25
+  --> $DIR/redundant_static_lifetimes.rs:26:25
    |
 LL | static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static.
    |                        -^^^^^^^---- help: consider removing `'static`: `&str`
 
 error: statics have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:31:29
+  --> $DIR/redundant_static_lifetimes.rs:30:29
    |
 LL | static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
    |                            -^^^^^^^---- help: consider removing `'static`: `&str`
 
 error: statics have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:33:25
+  --> $DIR/redundant_static_lifetimes.rs:32:25
    |
 LL | static STATIC_VAR_SIX: &'static u8 = &5;
    |                        -^^^^^^^--- help: consider removing `'static`: `&u8`
 
 error: statics have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:35:28
+  --> $DIR/redundant_static_lifetimes.rs:34:28
    |
 LL | static STATIC_VAR_HEIGHT: &'static Foo = &Foo {};
    |                           -^^^^^^^---- help: consider removing `'static`: `&Foo`
 
 error: statics have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:37:27
+  --> $DIR/redundant_static_lifetimes.rs:36:27
    |
 LL | static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static.
    |                          -^^^^^^^----- help: consider removing `'static`: `&[u8]`
 
 error: statics have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:39:27
+  --> $DIR/redundant_static_lifetimes.rs:38:27
    |
 LL | static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
    |                          -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)`
 
 error: statics have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:41:27
+  --> $DIR/redundant_static_lifetimes.rs:40:27
    |
 LL | static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
    |                          -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]`
 
 error: statics have by default a `'static` lifetime
-  --> $DIR/redundant_static_lifetimes.rs:68:16
+  --> $DIR/redundant_static_lifetimes.rs:65:16
    |
 LL |     static V: &'static u8 = &17;
    |               -^^^^^^^--- help: consider removing `'static`: `&u8`
diff --git a/tests/ui/result_large_err.rs b/tests/ui/result_large_err.rs
index 9dd27d6dc01..1c12cebfd97 100644
--- a/tests/ui/result_large_err.rs
+++ b/tests/ui/result_large_err.rs
@@ -108,4 +108,10 @@ pub fn array_error<T, U>() -> Result<(), ArrayError<(i32, T), U>> {
     Ok(())
 }
 
+// Issue #10005
+enum Empty {}
+fn _empty_error() -> Result<(), Empty> {
+    Ok(())
+}
+
 fn main() {}
diff --git a/tests/ui/seek_from_current.fixed b/tests/ui/seek_from_current.fixed
index 4b5303324bc..1309c91b81c 100644
--- a/tests/ui/seek_from_current.fixed
+++ b/tests/ui/seek_from_current.fixed
@@ -1,12 +1,11 @@
 // run-rustfix
 #![warn(clippy::seek_from_current)]
-#![feature(custom_inner_attributes)]
 
 use std::fs::File;
 use std::io::{self, Seek, SeekFrom, Write};
 
+#[clippy::msrv = "1.50"]
 fn _msrv_1_50() -> io::Result<()> {
-    #![clippy::msrv = "1.50"]
     let mut f = File::create("foo.txt")?;
     f.write_all(b"Hi!")?;
     f.seek(SeekFrom::Current(0))?;
@@ -14,8 +13,8 @@ fn _msrv_1_50() -> io::Result<()> {
     Ok(())
 }
 
+#[clippy::msrv = "1.51"]
 fn _msrv_1_51() -> io::Result<()> {
-    #![clippy::msrv = "1.51"]
     let mut f = File::create("foo.txt")?;
     f.write_all(b"Hi!")?;
     f.stream_position()?;
diff --git a/tests/ui/seek_from_current.rs b/tests/ui/seek_from_current.rs
index f93639261a1..5d9b1424cf6 100644
--- a/tests/ui/seek_from_current.rs
+++ b/tests/ui/seek_from_current.rs
@@ -1,12 +1,11 @@
 // run-rustfix
 #![warn(clippy::seek_from_current)]
-#![feature(custom_inner_attributes)]
 
 use std::fs::File;
 use std::io::{self, Seek, SeekFrom, Write};
 
+#[clippy::msrv = "1.50"]
 fn _msrv_1_50() -> io::Result<()> {
-    #![clippy::msrv = "1.50"]
     let mut f = File::create("foo.txt")?;
     f.write_all(b"Hi!")?;
     f.seek(SeekFrom::Current(0))?;
@@ -14,8 +13,8 @@ fn _msrv_1_50() -> io::Result<()> {
     Ok(())
 }
 
+#[clippy::msrv = "1.51"]
 fn _msrv_1_51() -> io::Result<()> {
-    #![clippy::msrv = "1.51"]
     let mut f = File::create("foo.txt")?;
     f.write_all(b"Hi!")?;
     f.seek(SeekFrom::Current(0))?;
diff --git a/tests/ui/seek_from_current.stderr b/tests/ui/seek_from_current.stderr
index db1125b53cd..c079f361192 100644
--- a/tests/ui/seek_from_current.stderr
+++ b/tests/ui/seek_from_current.stderr
@@ -1,5 +1,5 @@
 error: using `SeekFrom::Current` to start from current position
-  --> $DIR/seek_from_current.rs:21:5
+  --> $DIR/seek_from_current.rs:20:5
    |
 LL |     f.seek(SeekFrom::Current(0))?;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `f.stream_position()`
diff --git a/tests/ui/seek_to_start_instead_of_rewind.fixed b/tests/ui/seek_to_start_instead_of_rewind.fixed
index 464b6cdef63..9d0d1124c46 100644
--- a/tests/ui/seek_to_start_instead_of_rewind.fixed
+++ b/tests/ui/seek_to_start_instead_of_rewind.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 #![allow(unused)]
-#![feature(custom_inner_attributes)]
 #![warn(clippy::seek_to_start_instead_of_rewind)]
 
 use std::fs::OpenOptions;
@@ -94,9 +93,8 @@ fn main() {
     assert_eq!(&buf, hello);
 }
 
+#[clippy::msrv = "1.54"]
 fn msrv_1_54() {
-    #![clippy::msrv = "1.54"]
-
     let mut f = OpenOptions::new()
         .write(true)
         .read(true)
@@ -115,9 +113,8 @@ fn msrv_1_54() {
     assert_eq!(&buf, hello);
 }
 
+#[clippy::msrv = "1.55"]
 fn msrv_1_55() {
-    #![clippy::msrv = "1.55"]
-
     let mut f = OpenOptions::new()
         .write(true)
         .read(true)
diff --git a/tests/ui/seek_to_start_instead_of_rewind.rs b/tests/ui/seek_to_start_instead_of_rewind.rs
index 68e09bd7c1f..c5bc57cc3a7 100644
--- a/tests/ui/seek_to_start_instead_of_rewind.rs
+++ b/tests/ui/seek_to_start_instead_of_rewind.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 #![allow(unused)]
-#![feature(custom_inner_attributes)]
 #![warn(clippy::seek_to_start_instead_of_rewind)]
 
 use std::fs::OpenOptions;
@@ -94,9 +93,8 @@ fn main() {
     assert_eq!(&buf, hello);
 }
 
+#[clippy::msrv = "1.54"]
 fn msrv_1_54() {
-    #![clippy::msrv = "1.54"]
-
     let mut f = OpenOptions::new()
         .write(true)
         .read(true)
@@ -115,9 +113,8 @@ fn msrv_1_54() {
     assert_eq!(&buf, hello);
 }
 
+#[clippy::msrv = "1.55"]
 fn msrv_1_55() {
-    #![clippy::msrv = "1.55"]
-
     let mut f = OpenOptions::new()
         .write(true)
         .read(true)
diff --git a/tests/ui/seek_to_start_instead_of_rewind.stderr b/tests/ui/seek_to_start_instead_of_rewind.stderr
index de0eec5d909..6cce025359f 100644
--- a/tests/ui/seek_to_start_instead_of_rewind.stderr
+++ b/tests/ui/seek_to_start_instead_of_rewind.stderr
@@ -1,5 +1,5 @@
 error: used `seek` to go to the start of the stream
-  --> $DIR/seek_to_start_instead_of_rewind.rs:54:7
+  --> $DIR/seek_to_start_instead_of_rewind.rs:53:7
    |
 LL |     t.seek(SeekFrom::Start(0));
    |       ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()`
@@ -7,13 +7,13 @@ LL |     t.seek(SeekFrom::Start(0));
    = note: `-D clippy::seek-to-start-instead-of-rewind` implied by `-D warnings`
 
 error: used `seek` to go to the start of the stream
-  --> $DIR/seek_to_start_instead_of_rewind.rs:59:7
+  --> $DIR/seek_to_start_instead_of_rewind.rs:58:7
    |
 LL |     t.seek(SeekFrom::Start(0));
    |       ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()`
 
 error: used `seek` to go to the start of the stream
-  --> $DIR/seek_to_start_instead_of_rewind.rs:131:7
+  --> $DIR/seek_to_start_instead_of_rewind.rs:128:7
    |
 LL |     f.seek(SeekFrom::Start(0));
    |       ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()`
diff --git a/tests/ui/temporary_assignment.rs b/tests/ui/temporary_assignment.rs
index b4a931043b0..ac4c1bc6597 100644
--- a/tests/ui/temporary_assignment.rs
+++ b/tests/ui/temporary_assignment.rs
@@ -1,5 +1,4 @@
 #![warn(clippy::temporary_assignment)]
-#![allow(const_item_mutation)]
 
 use std::ops::{Deref, DerefMut};
 
diff --git a/tests/ui/temporary_assignment.stderr b/tests/ui/temporary_assignment.stderr
index 4cc32c79f05..7d79901a28d 100644
--- a/tests/ui/temporary_assignment.stderr
+++ b/tests/ui/temporary_assignment.stderr
@@ -1,5 +1,5 @@
 error: assignment to temporary
-  --> $DIR/temporary_assignment.rs:48:5
+  --> $DIR/temporary_assignment.rs:47:5
    |
 LL |     Struct { field: 0 }.field = 1;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -7,7 +7,7 @@ LL |     Struct { field: 0 }.field = 1;
    = note: `-D clippy::temporary-assignment` implied by `-D warnings`
 
 error: assignment to temporary
-  --> $DIR/temporary_assignment.rs:49:5
+  --> $DIR/temporary_assignment.rs:48:5
    |
 LL | /     MultiStruct {
 LL | |         structure: Struct { field: 0 },
@@ -17,13 +17,13 @@ LL | |     .field = 1;
    | |______________^
 
 error: assignment to temporary
-  --> $DIR/temporary_assignment.rs:54:5
+  --> $DIR/temporary_assignment.rs:53:5
    |
 LL |     ArrayStruct { array: [0] }.array[0] = 1;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: assignment to temporary
-  --> $DIR/temporary_assignment.rs:55:5
+  --> $DIR/temporary_assignment.rs:54:5
    |
 LL |     (0, 0).0 = 1;
    |     ^^^^^^^^^^^^
diff --git a/tests/ui/transmute_ptr_to_ref.fixed b/tests/ui/transmute_ptr_to_ref.fixed
index e5fe9133f97..074dae5fb28 100644
--- a/tests/ui/transmute_ptr_to_ref.fixed
+++ b/tests/ui/transmute_ptr_to_ref.fixed
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::transmute_ptr_to_ref)]
 #![allow(clippy::match_single_binding)]
 
@@ -51,8 +50,8 @@ unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'
     }
 }
 
+#[clippy::msrv = "1.38"]
 unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
-    #![clippy::msrv = "1.38"]
     let a = 0u32;
     let a = &a as *const u32;
     let _: &u32 = &*a;
@@ -63,8 +62,8 @@ unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
     }
 }
 
+#[clippy::msrv = "1.37"]
 unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
-    #![clippy::msrv = "1.37"]
     let a = 0u32;
     let a = &a as *const u32;
     let _: &u32 = &*a;
diff --git a/tests/ui/transmute_ptr_to_ref.rs b/tests/ui/transmute_ptr_to_ref.rs
index fe49cdc324f..2edc122cf47 100644
--- a/tests/ui/transmute_ptr_to_ref.rs
+++ b/tests/ui/transmute_ptr_to_ref.rs
@@ -1,6 +1,5 @@
 // run-rustfix
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::transmute_ptr_to_ref)]
 #![allow(clippy::match_single_binding)]
 
@@ -51,8 +50,8 @@ unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'
     }
 }
 
+#[clippy::msrv = "1.38"]
 unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
-    #![clippy::msrv = "1.38"]
     let a = 0u32;
     let a = &a as *const u32;
     let _: &u32 = std::mem::transmute(a);
@@ -63,8 +62,8 @@ unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
     }
 }
 
+#[clippy::msrv = "1.37"]
 unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
-    #![clippy::msrv = "1.37"]
     let a = 0u32;
     let a = &a as *const u32;
     let _: &u32 = std::mem::transmute(a);
diff --git a/tests/ui/transmute_ptr_to_ref.stderr b/tests/ui/transmute_ptr_to_ref.stderr
index 10117ee9182..b3e6c09d2d7 100644
--- a/tests/ui/transmute_ptr_to_ref.stderr
+++ b/tests/ui/transmute_ptr_to_ref.stderr
@@ -1,5 +1,5 @@
 error: transmute from a pointer type (`*const T`) to a reference type (`&T`)
-  --> $DIR/transmute_ptr_to_ref.rs:8:17
+  --> $DIR/transmute_ptr_to_ref.rs:7:17
    |
 LL |     let _: &T = std::mem::transmute(p);
    |                 ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*p`
@@ -7,127 +7,127 @@ LL |     let _: &T = std::mem::transmute(p);
    = note: `-D clippy::transmute-ptr-to-ref` implied by `-D warnings`
 
 error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`)
-  --> $DIR/transmute_ptr_to_ref.rs:11:21
+  --> $DIR/transmute_ptr_to_ref.rs:10:21
    |
 LL |     let _: &mut T = std::mem::transmute(m);
    |                     ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *m`
 
 error: transmute from a pointer type (`*mut T`) to a reference type (`&T`)
-  --> $DIR/transmute_ptr_to_ref.rs:14:17
+  --> $DIR/transmute_ptr_to_ref.rs:13:17
    |
 LL |     let _: &T = std::mem::transmute(m);
    |                 ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*m`
 
 error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`)
-  --> $DIR/transmute_ptr_to_ref.rs:17:21
+  --> $DIR/transmute_ptr_to_ref.rs:16:21
    |
 LL |     let _: &mut T = std::mem::transmute(p as *mut T);
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(p as *mut T)`
 
 error: transmute from a pointer type (`*const U`) to a reference type (`&T`)
-  --> $DIR/transmute_ptr_to_ref.rs:20:17
+  --> $DIR/transmute_ptr_to_ref.rs:19:17
    |
 LL |     let _: &T = std::mem::transmute(o);
    |                 ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(o as *const T)`
 
 error: transmute from a pointer type (`*mut U`) to a reference type (`&mut T`)
-  --> $DIR/transmute_ptr_to_ref.rs:23:21
+  --> $DIR/transmute_ptr_to_ref.rs:22:21
    |
 LL |     let _: &mut T = std::mem::transmute(om);
    |                     ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(om as *mut T)`
 
 error: transmute from a pointer type (`*mut U`) to a reference type (`&T`)
-  --> $DIR/transmute_ptr_to_ref.rs:26:17
+  --> $DIR/transmute_ptr_to_ref.rs:25:17
    |
 LL |     let _: &T = std::mem::transmute(om);
    |                 ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(om as *const T)`
 
 error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, u8>`)
-  --> $DIR/transmute_ptr_to_ref.rs:36:32
+  --> $DIR/transmute_ptr_to_ref.rs:35:32
    |
 LL |     let _: &Foo<u8> = unsafe { std::mem::transmute::<_, &Foo<_>>(raw) };
    |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::<Foo<_>>()`
 
 error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, &u8>`)
-  --> $DIR/transmute_ptr_to_ref.rs:38:33
+  --> $DIR/transmute_ptr_to_ref.rs:37:33
    |
 LL |     let _: &Foo<&u8> = unsafe { std::mem::transmute::<_, &Foo<&_>>(raw) };
    |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::<Foo<&_>>()`
 
 error: transmute from a pointer type (`*const i32`) to a reference type (`&u8`)
-  --> $DIR/transmute_ptr_to_ref.rs:42:14
+  --> $DIR/transmute_ptr_to_ref.rs:41:14
    |
 LL |     unsafe { std::mem::transmute::<_, Bar>(raw) };
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const u8)`
 
 error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
-  --> $DIR/transmute_ptr_to_ref.rs:47:14
+  --> $DIR/transmute_ptr_to_ref.rs:46:14
    |
 LL |         0 => std::mem::transmute(x),
    |              ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()`
 
 error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
-  --> $DIR/transmute_ptr_to_ref.rs:48:14
+  --> $DIR/transmute_ptr_to_ref.rs:47:14
    |
 LL |         1 => std::mem::transmute(y),
    |              ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&u32>()`
 
 error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
-  --> $DIR/transmute_ptr_to_ref.rs:49:14
+  --> $DIR/transmute_ptr_to_ref.rs:48:14
    |
 LL |         2 => std::mem::transmute::<_, &&'b u32>(x),
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()`
 
 error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
-  --> $DIR/transmute_ptr_to_ref.rs:50:14
+  --> $DIR/transmute_ptr_to_ref.rs:49:14
    |
 LL |         _ => std::mem::transmute::<_, &&'b u32>(y),
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&'b u32>()`
 
 error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
-  --> $DIR/transmute_ptr_to_ref.rs:58:19
+  --> $DIR/transmute_ptr_to_ref.rs:57:19
    |
 LL |     let _: &u32 = std::mem::transmute(a);
    |                   ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a`
 
 error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
-  --> $DIR/transmute_ptr_to_ref.rs:59:19
+  --> $DIR/transmute_ptr_to_ref.rs:58:19
    |
 LL |     let _: &u32 = std::mem::transmute::<_, &u32>(a);
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a.cast::<u32>()`
 
 error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
-  --> $DIR/transmute_ptr_to_ref.rs:61:14
+  --> $DIR/transmute_ptr_to_ref.rs:60:14
    |
 LL |         0 => std::mem::transmute(x),
    |              ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()`
 
 error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
-  --> $DIR/transmute_ptr_to_ref.rs:62:14
+  --> $DIR/transmute_ptr_to_ref.rs:61:14
    |
 LL |         _ => std::mem::transmute::<_, &&'b u32>(x),
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()`
 
 error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
-  --> $DIR/transmute_ptr_to_ref.rs:70:19
+  --> $DIR/transmute_ptr_to_ref.rs:69:19
    |
 LL |     let _: &u32 = std::mem::transmute(a);
    |                   ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a`
 
 error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
-  --> $DIR/transmute_ptr_to_ref.rs:71:19
+  --> $DIR/transmute_ptr_to_ref.rs:70:19
    |
 LL |     let _: &u32 = std::mem::transmute::<_, &u32>(a);
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a as *const u32)`
 
 error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
-  --> $DIR/transmute_ptr_to_ref.rs:73:14
+  --> $DIR/transmute_ptr_to_ref.rs:72:14
    |
 LL |         0 => std::mem::transmute(x),
    |              ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &u32)`
 
 error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
-  --> $DIR/transmute_ptr_to_ref.rs:74:14
+  --> $DIR/transmute_ptr_to_ref.rs:73:14
    |
 LL |         _ => std::mem::transmute::<_, &&'b u32>(x),
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &'b u32)`
diff --git a/tests/ui/undocumented_unsafe_blocks.rs b/tests/ui/undocumented_unsafe_blocks.rs
index cbc6768033e..c05eb447b2e 100644
--- a/tests/ui/undocumented_unsafe_blocks.rs
+++ b/tests/ui/undocumented_unsafe_blocks.rs
@@ -1,6 +1,6 @@
 // aux-build:proc_macro_unsafe.rs
 
-#![warn(clippy::undocumented_unsafe_blocks)]
+#![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)]
 #![allow(clippy::let_unit_value, clippy::missing_safety_doc)]
 
 extern crate proc_macro_unsafe;
diff --git a/tests/ui/undocumented_unsafe_blocks.stderr b/tests/ui/undocumented_unsafe_blocks.stderr
index ba4de9806d1..d1c1bb5ffea 100644
--- a/tests/ui/undocumented_unsafe_blocks.stderr
+++ b/tests/ui/undocumented_unsafe_blocks.stderr
@@ -239,6 +239,19 @@ LL |     unsafe impl TrailingComment for () {} // SAFETY:
    |
    = help: consider adding a safety comment on the preceding line
 
+error: constant item has unnecessary safety comment
+  --> $DIR/undocumented_unsafe_blocks.rs:471:5
+   |
+LL |     const BIG_NUMBER: i32 = 1000000;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider removing the safety comment
+  --> $DIR/undocumented_unsafe_blocks.rs:470:5
+   |
+LL |     // SAFETY:
+   |     ^^^^^^^^^^
+   = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings`
+
 error: unsafe impl missing a safety comment
   --> $DIR/undocumented_unsafe_blocks.rs:472:5
    |
@@ -271,6 +284,24 @@ LL |         unsafe {};
    |
    = help: consider adding a safety comment on the preceding line
 
+error: statement has unnecessary safety comment
+  --> $DIR/undocumented_unsafe_blocks.rs:501:5
+   |
+LL | /     let _ = {
+LL | |         if unsafe { true } {
+LL | |             todo!();
+LL | |         } else {
+...  |
+LL | |         }
+LL | |     };
+   | |______^
+   |
+help: consider removing the safety comment
+  --> $DIR/undocumented_unsafe_blocks.rs:500:5
+   |
+LL |     // SAFETY: this is more than one level away, so it should warn
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
 error: unsafe block missing a safety comment
   --> $DIR/undocumented_unsafe_blocks.rs:502:12
    |
@@ -287,5 +318,5 @@ LL |             let bar = unsafe {};
    |
    = help: consider adding a safety comment on the preceding line
 
-error: aborting due to 34 previous errors
+error: aborting due to 36 previous errors
 
diff --git a/tests/ui/uninlined_format_args.fixed b/tests/ui/uninlined_format_args.fixed
index 10627447975..9d08e80cf9a 100644
--- a/tests/ui/uninlined_format_args.fixed
+++ b/tests/ui/uninlined_format_args.fixed
@@ -1,6 +1,5 @@
 // aux-build:proc_macro_with_span.rs
 // run-rustfix
-#![feature(custom_inner_attributes)]
 #![warn(clippy::uninlined_format_args)]
 #![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)]
 #![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)]
@@ -44,9 +43,7 @@ fn tester(fn_arg: i32) {
     println!("val='{local_i32}'"); // space+tab
     println!("val='{local_i32}'"); // tab+space
     println!(
-        "val='{
-    }'",
-        local_i32
+        "val='{local_i32}'"
     );
     println!("{local_i32}");
     println!("{fn_arg}");
@@ -57,11 +54,11 @@ fn tester(fn_arg: i32) {
     println!("{local_i32:<3}");
     println!("{local_i32:#010x}");
     println!("{local_f64:.1}");
-    println!("Hello {} is {local_f64:.local_i32$}", "x");
-    println!("Hello {local_i32} is {local_f64:.*}", 5);
-    println!("Hello {local_i32} is {local_f64:.*}", 5);
+    println!("Hello {} is {:.*}", "x", local_i32, local_f64);
+    println!("Hello {} is {:.*}", local_i32, 5, local_f64);
+    println!("Hello {} is {2:.*}", local_i32, 5, local_f64);
     println!("{local_i32} {local_f64}");
-    println!("{local_i32}, {}", local_opt.unwrap());
+    println!("{}, {}", local_i32, local_opt.unwrap());
     println!("{val}");
     println!("{val}");
     println!("{} {1}", local_i32, 42);
@@ -110,8 +107,7 @@ fn tester(fn_arg: i32) {
     println!("{local_f64:width$.prec$}");
     println!("{local_f64:width$.prec$} {local_f64} {width} {prec}");
     println!(
-        "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}",
-        local_i32, width, prec,
+        "{local_i32:width$.prec$} {local_i32:prec$.width$} {width:local_i32$.prec$} {width:prec$.local_i32$} {prec:local_i32$.width$} {prec:width$.local_i32$}",
     );
     println!(
         "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$} {3}",
@@ -142,9 +138,7 @@ fn tester(fn_arg: i32) {
     println!(no_param_str!(), local_i32);
 
     println!(
-        "{}",
-        // comment with a comma , in it
-        val,
+        "{val}",
     );
     println!("{val}");
 
@@ -169,14 +163,14 @@ fn main() {
     tester(42);
 }
 
+#[clippy::msrv = "1.57"]
 fn _under_msrv() {
-    #![clippy::msrv = "1.57"]
     let local_i32 = 1;
     println!("don't expand='{}'", local_i32);
 }
 
+#[clippy::msrv = "1.58"]
 fn _meets_msrv() {
-    #![clippy::msrv = "1.58"]
     let local_i32 = 1;
     println!("expand='{local_i32}'");
 }
diff --git a/tests/ui/uninlined_format_args.rs b/tests/ui/uninlined_format_args.rs
index 8e495ebd083..35b3677a896 100644
--- a/tests/ui/uninlined_format_args.rs
+++ b/tests/ui/uninlined_format_args.rs
@@ -1,6 +1,5 @@
 // aux-build:proc_macro_with_span.rs
 // run-rustfix
-#![feature(custom_inner_attributes)]
 #![warn(clippy::uninlined_format_args)]
 #![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)]
 #![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)]
@@ -169,14 +168,14 @@ fn main() {
     tester(42);
 }
 
+#[clippy::msrv = "1.57"]
 fn _under_msrv() {
-    #![clippy::msrv = "1.57"]
     let local_i32 = 1;
     println!("don't expand='{}'", local_i32);
 }
 
+#[clippy::msrv = "1.58"]
 fn _meets_msrv() {
-    #![clippy::msrv = "1.58"]
     let local_i32 = 1;
     println!("expand='{}'", local_i32);
 }
diff --git a/tests/ui/uninlined_format_args.stderr b/tests/ui/uninlined_format_args.stderr
index 2ce3b7fa960..a12abf8bef8 100644
--- a/tests/ui/uninlined_format_args.stderr
+++ b/tests/ui/uninlined_format_args.stderr
@@ -1,5 +1,5 @@
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:41:5
+  --> $DIR/uninlined_format_args.rs:40:5
    |
 LL |     println!("val='{}'", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -12,7 +12,7 @@ LL +     println!("val='{local_i32}'");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:42:5
+  --> $DIR/uninlined_format_args.rs:41:5
    |
 LL |     println!("val='{   }'", local_i32); // 3 spaces
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -24,7 +24,7 @@ LL +     println!("val='{local_i32}'"); // 3 spaces
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:43:5
+  --> $DIR/uninlined_format_args.rs:42:5
    |
 LL |     println!("val='{    }'", local_i32); // tab
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -36,7 +36,7 @@ LL +     println!("val='{local_i32}'"); // tab
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:44:5
+  --> $DIR/uninlined_format_args.rs:43:5
    |
 LL |     println!("val='{     }'", local_i32); // space+tab
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -48,7 +48,7 @@ LL +     println!("val='{local_i32}'"); // space+tab
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:45:5
+  --> $DIR/uninlined_format_args.rs:44:5
    |
 LL |     println!("val='{     }'", local_i32); // tab+space
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -60,7 +60,17 @@ LL +     println!("val='{local_i32}'"); // tab+space
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:51:5
+  --> $DIR/uninlined_format_args.rs:45:5
+   |
+LL | /     println!(
+LL | |         "val='{
+LL | |     }'",
+LL | |         local_i32
+LL | |     );
+   | |_____^
+
+error: variables can be used directly in the `format!` string
+  --> $DIR/uninlined_format_args.rs:50:5
    |
 LL |     println!("{}", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -72,7 +82,7 @@ LL +     println!("{local_i32}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:52:5
+  --> $DIR/uninlined_format_args.rs:51:5
    |
 LL |     println!("{}", fn_arg);
    |     ^^^^^^^^^^^^^^^^^^^^^^
@@ -84,7 +94,7 @@ LL +     println!("{fn_arg}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:53:5
+  --> $DIR/uninlined_format_args.rs:52:5
    |
 LL |     println!("{:?}", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -96,7 +106,7 @@ LL +     println!("{local_i32:?}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:54:5
+  --> $DIR/uninlined_format_args.rs:53:5
    |
 LL |     println!("{:#?}", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -108,7 +118,7 @@ LL +     println!("{local_i32:#?}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:55:5
+  --> $DIR/uninlined_format_args.rs:54:5
    |
 LL |     println!("{:4}", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -120,7 +130,7 @@ LL +     println!("{local_i32:4}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:56:5
+  --> $DIR/uninlined_format_args.rs:55:5
    |
 LL |     println!("{:04}", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -132,7 +142,7 @@ LL +     println!("{local_i32:04}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:57:5
+  --> $DIR/uninlined_format_args.rs:56:5
    |
 LL |     println!("{:<3}", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -144,7 +154,7 @@ LL +     println!("{local_i32:<3}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:58:5
+  --> $DIR/uninlined_format_args.rs:57:5
    |
 LL |     println!("{:#010x}", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -156,7 +166,7 @@ LL +     println!("{local_i32:#010x}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:59:5
+  --> $DIR/uninlined_format_args.rs:58:5
    |
 LL |     println!("{:.1}", local_f64);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -168,44 +178,8 @@ LL +     println!("{local_f64:.1}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:60:5
-   |
-LL |     println!("Hello {} is {:.*}", "x", local_i32, local_f64);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: change this to
-   |
-LL -     println!("Hello {} is {:.*}", "x", local_i32, local_f64);
-LL +     println!("Hello {} is {local_f64:.local_i32$}", "x");
-   |
-
-error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:61:5
-   |
-LL |     println!("Hello {} is {:.*}", local_i32, 5, local_f64);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: change this to
-   |
-LL -     println!("Hello {} is {:.*}", local_i32, 5, local_f64);
-LL +     println!("Hello {local_i32} is {local_f64:.*}", 5);
-   |
-
-error: variables can be used directly in the `format!` string
   --> $DIR/uninlined_format_args.rs:62:5
    |
-LL |     println!("Hello {} is {2:.*}", local_i32, 5, local_f64);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: change this to
-   |
-LL -     println!("Hello {} is {2:.*}", local_i32, 5, local_f64);
-LL +     println!("Hello {local_i32} is {local_f64:.*}", 5);
-   |
-
-error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:63:5
-   |
 LL |     println!("{} {}", local_i32, local_f64);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
@@ -218,18 +192,6 @@ LL +     println!("{local_i32} {local_f64}");
 error: variables can be used directly in the `format!` string
   --> $DIR/uninlined_format_args.rs:64:5
    |
-LL |     println!("{}, {}", local_i32, local_opt.unwrap());
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: change this to
-   |
-LL -     println!("{}, {}", local_i32, local_opt.unwrap());
-LL +     println!("{local_i32}, {}", local_opt.unwrap());
-   |
-
-error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:65:5
-   |
 LL |     println!("{}", val);
    |     ^^^^^^^^^^^^^^^^^^^
    |
@@ -240,7 +202,7 @@ LL +     println!("{val}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:66:5
+  --> $DIR/uninlined_format_args.rs:65:5
    |
 LL |     println!("{}", v = val);
    |     ^^^^^^^^^^^^^^^^^^^^^^^
@@ -252,7 +214,7 @@ LL +     println!("{val}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:68:5
+  --> $DIR/uninlined_format_args.rs:67:5
    |
 LL |     println!("val='{/t }'", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -264,7 +226,7 @@ LL +     println!("val='{local_i32}'");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:69:5
+  --> $DIR/uninlined_format_args.rs:68:5
    |
 LL |     println!("val='{/n }'", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -276,7 +238,7 @@ LL +     println!("val='{local_i32}'");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:70:5
+  --> $DIR/uninlined_format_args.rs:69:5
    |
 LL |     println!("val='{local_i32}'", local_i32 = local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -288,7 +250,7 @@ LL +     println!("val='{local_i32}'");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:71:5
+  --> $DIR/uninlined_format_args.rs:70:5
    |
 LL |     println!("val='{local_i32}'", local_i32 = fn_arg);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -300,7 +262,7 @@ LL +     println!("val='{fn_arg}'");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:72:5
+  --> $DIR/uninlined_format_args.rs:71:5
    |
 LL |     println!("{0}", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -312,7 +274,7 @@ LL +     println!("{local_i32}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:73:5
+  --> $DIR/uninlined_format_args.rs:72:5
    |
 LL |     println!("{0:?}", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -324,7 +286,7 @@ LL +     println!("{local_i32:?}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:74:5
+  --> $DIR/uninlined_format_args.rs:73:5
    |
 LL |     println!("{0:#?}", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -336,7 +298,7 @@ LL +     println!("{local_i32:#?}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:75:5
+  --> $DIR/uninlined_format_args.rs:74:5
    |
 LL |     println!("{0:04}", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -348,7 +310,7 @@ LL +     println!("{local_i32:04}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:76:5
+  --> $DIR/uninlined_format_args.rs:75:5
    |
 LL |     println!("{0:<3}", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -360,7 +322,7 @@ LL +     println!("{local_i32:<3}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:77:5
+  --> $DIR/uninlined_format_args.rs:76:5
    |
 LL |     println!("{0:#010x}", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -372,7 +334,7 @@ LL +     println!("{local_i32:#010x}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:78:5
+  --> $DIR/uninlined_format_args.rs:77:5
    |
 LL |     println!("{0:.1}", local_f64);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -384,7 +346,7 @@ LL +     println!("{local_f64:.1}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:79:5
+  --> $DIR/uninlined_format_args.rs:78:5
    |
 LL |     println!("{0} {0}", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -396,7 +358,7 @@ LL +     println!("{local_i32} {local_i32}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:80:5
+  --> $DIR/uninlined_format_args.rs:79:5
    |
 LL |     println!("{1} {} {0} {}", local_i32, local_f64);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -408,7 +370,7 @@ LL +     println!("{local_f64} {local_i32} {local_i32} {local_f64}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:81:5
+  --> $DIR/uninlined_format_args.rs:80:5
    |
 LL |     println!("{0} {1}", local_i32, local_f64);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -420,7 +382,7 @@ LL +     println!("{local_i32} {local_f64}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:82:5
+  --> $DIR/uninlined_format_args.rs:81:5
    |
 LL |     println!("{1} {0}", local_i32, local_f64);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -432,7 +394,7 @@ LL +     println!("{local_f64} {local_i32}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:83:5
+  --> $DIR/uninlined_format_args.rs:82:5
    |
 LL |     println!("{1} {0} {1} {0}", local_i32, local_f64);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -444,7 +406,7 @@ LL +     println!("{local_f64} {local_i32} {local_f64} {local_i32}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:85:5
+  --> $DIR/uninlined_format_args.rs:84:5
    |
 LL |     println!("{v}", v = local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -456,7 +418,7 @@ LL +     println!("{local_i32}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:86:5
+  --> $DIR/uninlined_format_args.rs:85:5
    |
 LL |     println!("{local_i32:0$}", width);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -468,7 +430,7 @@ LL +     println!("{local_i32:width$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:87:5
+  --> $DIR/uninlined_format_args.rs:86:5
    |
 LL |     println!("{local_i32:w$}", w = width);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -480,7 +442,7 @@ LL +     println!("{local_i32:width$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:88:5
+  --> $DIR/uninlined_format_args.rs:87:5
    |
 LL |     println!("{local_i32:.0$}", prec);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -492,7 +454,7 @@ LL +     println!("{local_i32:.prec$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:89:5
+  --> $DIR/uninlined_format_args.rs:88:5
    |
 LL |     println!("{local_i32:.p$}", p = prec);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -504,7 +466,7 @@ LL +     println!("{local_i32:.prec$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:90:5
+  --> $DIR/uninlined_format_args.rs:89:5
    |
 LL |     println!("{:0$}", v = val);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -516,7 +478,7 @@ LL +     println!("{val:val$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:91:5
+  --> $DIR/uninlined_format_args.rs:90:5
    |
 LL |     println!("{0:0$}", v = val);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -528,7 +490,7 @@ LL +     println!("{val:val$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:92:5
+  --> $DIR/uninlined_format_args.rs:91:5
    |
 LL |     println!("{:0$.0$}", v = val);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -540,7 +502,7 @@ LL +     println!("{val:val$.val$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:93:5
+  --> $DIR/uninlined_format_args.rs:92:5
    |
 LL |     println!("{0:0$.0$}", v = val);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -552,7 +514,7 @@ LL +     println!("{val:val$.val$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:94:5
+  --> $DIR/uninlined_format_args.rs:93:5
    |
 LL |     println!("{0:0$.v$}", v = val);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -564,7 +526,7 @@ LL +     println!("{val:val$.val$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:95:5
+  --> $DIR/uninlined_format_args.rs:94:5
    |
 LL |     println!("{0:v$.0$}", v = val);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -576,7 +538,7 @@ LL +     println!("{val:val$.val$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:96:5
+  --> $DIR/uninlined_format_args.rs:95:5
    |
 LL |     println!("{v:0$.0$}", v = val);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -588,7 +550,7 @@ LL +     println!("{val:val$.val$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:97:5
+  --> $DIR/uninlined_format_args.rs:96:5
    |
 LL |     println!("{v:v$.0$}", v = val);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -600,7 +562,7 @@ LL +     println!("{val:val$.val$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:98:5
+  --> $DIR/uninlined_format_args.rs:97:5
    |
 LL |     println!("{v:0$.v$}", v = val);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -612,7 +574,7 @@ LL +     println!("{val:val$.val$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:99:5
+  --> $DIR/uninlined_format_args.rs:98:5
    |
 LL |     println!("{v:v$.v$}", v = val);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -624,7 +586,7 @@ LL +     println!("{val:val$.val$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:100:5
+  --> $DIR/uninlined_format_args.rs:99:5
    |
 LL |     println!("{:0$}", width);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -636,7 +598,7 @@ LL +     println!("{width:width$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:101:5
+  --> $DIR/uninlined_format_args.rs:100:5
    |
 LL |     println!("{:1$}", local_i32, width);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -648,7 +610,7 @@ LL +     println!("{local_i32:width$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:102:5
+  --> $DIR/uninlined_format_args.rs:101:5
    |
 LL |     println!("{:w$}", w = width);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -660,7 +622,7 @@ LL +     println!("{width:width$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:103:5
+  --> $DIR/uninlined_format_args.rs:102:5
    |
 LL |     println!("{:w$}", local_i32, w = width);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -672,7 +634,7 @@ LL +     println!("{local_i32:width$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:104:5
+  --> $DIR/uninlined_format_args.rs:103:5
    |
 LL |     println!("{:.0$}", prec);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -684,7 +646,7 @@ LL +     println!("{prec:.prec$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:105:5
+  --> $DIR/uninlined_format_args.rs:104:5
    |
 LL |     println!("{:.1$}", local_i32, prec);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -696,7 +658,7 @@ LL +     println!("{local_i32:.prec$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:106:5
+  --> $DIR/uninlined_format_args.rs:105:5
    |
 LL |     println!("{:.p$}", p = prec);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -708,7 +670,7 @@ LL +     println!("{prec:.prec$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:107:5
+  --> $DIR/uninlined_format_args.rs:106:5
    |
 LL |     println!("{:.p$}", local_i32, p = prec);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -720,7 +682,7 @@ LL +     println!("{local_i32:.prec$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:108:5
+  --> $DIR/uninlined_format_args.rs:107:5
    |
 LL |     println!("{:0$.1$}", width, prec);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -732,7 +694,7 @@ LL +     println!("{width:width$.prec$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:109:5
+  --> $DIR/uninlined_format_args.rs:108:5
    |
 LL |     println!("{:0$.w$}", width, w = prec);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -744,7 +706,7 @@ LL +     println!("{width:width$.prec$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:110:5
+  --> $DIR/uninlined_format_args.rs:109:5
    |
 LL |     println!("{:1$.2$}", local_f64, width, prec);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -756,7 +718,7 @@ LL +     println!("{local_f64:width$.prec$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:111:5
+  --> $DIR/uninlined_format_args.rs:110:5
    |
 LL |     println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -768,7 +730,16 @@ LL +     println!("{local_f64:width$.prec$} {local_f64} {width} {prec}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:123:5
+  --> $DIR/uninlined_format_args.rs:111:5
+   |
+LL | /     println!(
+LL | |         "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}",
+LL | |         local_i32, width, prec,
+LL | |     );
+   | |_____^
+
+error: variables can be used directly in the `format!` string
+  --> $DIR/uninlined_format_args.rs:122:5
    |
 LL |     println!("Width = {}, value with width = {:0$}", local_i32, local_f64);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -780,7 +751,7 @@ LL +     println!("Width = {local_i32}, value with width = {local_f64:local_i32$
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:124:5
+  --> $DIR/uninlined_format_args.rs:123:5
    |
 LL |     println!("{:w$.p$}", local_i32, w = width, p = prec);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -792,7 +763,7 @@ LL +     println!("{local_i32:width$.prec$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:125:5
+  --> $DIR/uninlined_format_args.rs:124:5
    |
 LL |     println!("{:w$.p$}", w = width, p = prec);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -804,7 +775,7 @@ LL +     println!("{width:width$.prec$}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:126:20
+  --> $DIR/uninlined_format_args.rs:125:20
    |
 LL |     println!("{}", format!("{}", local_i32));
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -816,7 +787,17 @@ LL +     println!("{}", format!("{local_i32}"));
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:149:5
+  --> $DIR/uninlined_format_args.rs:143:5
+   |
+LL | /     println!(
+LL | |         "{}",
+LL | |         // comment with a comma , in it
+LL | |         val,
+LL | |     );
+   | |_____^
+
+error: variables can be used directly in the `format!` string
+  --> $DIR/uninlined_format_args.rs:148:5
    |
 LL |     println!("{}", /* comment with a comma , in it */ val);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -828,7 +809,7 @@ LL +     println!("{val}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:155:9
+  --> $DIR/uninlined_format_args.rs:154:9
    |
 LL |         panic!("p1 {}", local_i32);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -840,7 +821,7 @@ LL +         panic!("p1 {local_i32}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:158:9
+  --> $DIR/uninlined_format_args.rs:157:9
    |
 LL |         panic!("p2 {0}", local_i32);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -852,7 +833,7 @@ LL +         panic!("p2 {local_i32}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:161:9
+  --> $DIR/uninlined_format_args.rs:160:9
    |
 LL |         panic!("p3 {local_i32}", local_i32 = local_i32);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -864,7 +845,7 @@ LL +         panic!("p3 {local_i32}");
    |
 
 error: variables can be used directly in the `format!` string
-  --> $DIR/uninlined_format_args.rs:181:5
+  --> $DIR/uninlined_format_args.rs:180:5
    |
 LL |     println!("expand='{}'", local_i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -875,5 +856,5 @@ LL -     println!("expand='{}'", local_i32);
 LL +     println!("expand='{local_i32}'");
    |
 
-error: aborting due to 73 previous errors
+error: aborting due to 72 previous errors
 
diff --git a/tests/ui/unnecessary_cast.fixed b/tests/ui/unnecessary_cast.fixed
index ec8c6abfab9..2f7e2997e73 100644
--- a/tests/ui/unnecessary_cast.fixed
+++ b/tests/ui/unnecessary_cast.fixed
@@ -41,6 +41,17 @@ fn main() {
     // do not lint cast to alias type
     1 as I32Alias;
     &1 as &I32Alias;
+
+    // issue #9960
+    macro_rules! bind_var {
+        ($id:ident, $e:expr) => {{
+            let $id = 0usize;
+            let _ = $e != 0usize;
+            let $id = 0isize;
+            let _ = $e != 0usize;
+        }}
+    }
+    bind_var!(x, (x as usize) + 1);
 }
 
 type I32Alias = i32;
@@ -85,6 +96,9 @@ mod fixable {
 
         let _ = 1 as I32Alias;
         let _ = &1 as &I32Alias;
+
+        let x = 1i32;
+        let _ = &{ x };
     }
 
     type I32Alias = i32;
diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs
index 5213cdc269b..54dd46ba59f 100644
--- a/tests/ui/unnecessary_cast.rs
+++ b/tests/ui/unnecessary_cast.rs
@@ -41,6 +41,17 @@ fn main() {
     // do not lint cast to alias type
     1 as I32Alias;
     &1 as &I32Alias;
+
+    // issue #9960
+    macro_rules! bind_var {
+        ($id:ident, $e:expr) => {{
+            let $id = 0usize;
+            let _ = $e != 0usize;
+            let $id = 0isize;
+            let _ = $e != 0usize;
+        }}
+    }
+    bind_var!(x, (x as usize) + 1);
 }
 
 type I32Alias = i32;
@@ -85,6 +96,9 @@ mod fixable {
 
         let _ = 1 as I32Alias;
         let _ = &1 as &I32Alias;
+
+        let x = 1i32;
+        let _ = &(x as i32);
     }
 
     type I32Alias = i32;
diff --git a/tests/ui/unnecessary_cast.stderr b/tests/ui/unnecessary_cast.stderr
index e5c3dd5e53f..fcee4ee2a65 100644
--- a/tests/ui/unnecessary_cast.stderr
+++ b/tests/ui/unnecessary_cast.stderr
@@ -49,136 +49,142 @@ LL |     1_f32 as f32;
    |     ^^^^^^^^^^^^ help: try: `1_f32`
 
 error: casting integer literal to `f32` is unnecessary
-  --> $DIR/unnecessary_cast.rs:53:9
+  --> $DIR/unnecessary_cast.rs:64:9
    |
 LL |         100 as f32;
    |         ^^^^^^^^^^ help: try: `100_f32`
 
 error: casting integer literal to `f64` is unnecessary
-  --> $DIR/unnecessary_cast.rs:54:9
+  --> $DIR/unnecessary_cast.rs:65:9
    |
 LL |         100 as f64;
    |         ^^^^^^^^^^ help: try: `100_f64`
 
 error: casting integer literal to `f64` is unnecessary
-  --> $DIR/unnecessary_cast.rs:55:9
+  --> $DIR/unnecessary_cast.rs:66:9
    |
 LL |         100_i32 as f64;
    |         ^^^^^^^^^^^^^^ help: try: `100_f64`
 
 error: casting integer literal to `f32` is unnecessary
-  --> $DIR/unnecessary_cast.rs:56:17
+  --> $DIR/unnecessary_cast.rs:67:17
    |
 LL |         let _ = -100 as f32;
    |                 ^^^^^^^^^^^ help: try: `-100_f32`
 
 error: casting integer literal to `f64` is unnecessary
-  --> $DIR/unnecessary_cast.rs:57:17
+  --> $DIR/unnecessary_cast.rs:68:17
    |
 LL |         let _ = -100 as f64;
    |                 ^^^^^^^^^^^ help: try: `-100_f64`
 
 error: casting integer literal to `f64` is unnecessary
-  --> $DIR/unnecessary_cast.rs:58:17
+  --> $DIR/unnecessary_cast.rs:69:17
    |
 LL |         let _ = -100_i32 as f64;
    |                 ^^^^^^^^^^^^^^^ help: try: `-100_f64`
 
 error: casting float literal to `f32` is unnecessary
-  --> $DIR/unnecessary_cast.rs:59:9
+  --> $DIR/unnecessary_cast.rs:70:9
    |
 LL |         100. as f32;
    |         ^^^^^^^^^^^ help: try: `100_f32`
 
 error: casting float literal to `f64` is unnecessary
-  --> $DIR/unnecessary_cast.rs:60:9
+  --> $DIR/unnecessary_cast.rs:71:9
    |
 LL |         100. as f64;
    |         ^^^^^^^^^^^ help: try: `100_f64`
 
 error: casting integer literal to `u32` is unnecessary
-  --> $DIR/unnecessary_cast.rs:72:9
+  --> $DIR/unnecessary_cast.rs:83:9
    |
 LL |         1 as u32;
    |         ^^^^^^^^ help: try: `1_u32`
 
 error: casting integer literal to `i32` is unnecessary
-  --> $DIR/unnecessary_cast.rs:73:9
+  --> $DIR/unnecessary_cast.rs:84:9
    |
 LL |         0x10 as i32;
    |         ^^^^^^^^^^^ help: try: `0x10_i32`
 
 error: casting integer literal to `usize` is unnecessary
-  --> $DIR/unnecessary_cast.rs:74:9
+  --> $DIR/unnecessary_cast.rs:85:9
    |
 LL |         0b10 as usize;
    |         ^^^^^^^^^^^^^ help: try: `0b10_usize`
 
 error: casting integer literal to `u16` is unnecessary
-  --> $DIR/unnecessary_cast.rs:75:9
+  --> $DIR/unnecessary_cast.rs:86:9
    |
 LL |         0o73 as u16;
    |         ^^^^^^^^^^^ help: try: `0o73_u16`
 
 error: casting integer literal to `u32` is unnecessary
-  --> $DIR/unnecessary_cast.rs:76:9
+  --> $DIR/unnecessary_cast.rs:87:9
    |
 LL |         1_000_000_000 as u32;
    |         ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32`
 
 error: casting float literal to `f64` is unnecessary
-  --> $DIR/unnecessary_cast.rs:78:9
+  --> $DIR/unnecessary_cast.rs:89:9
    |
 LL |         1.0 as f64;
    |         ^^^^^^^^^^ help: try: `1.0_f64`
 
 error: casting float literal to `f32` is unnecessary
-  --> $DIR/unnecessary_cast.rs:79:9
+  --> $DIR/unnecessary_cast.rs:90:9
    |
 LL |         0.5 as f32;
    |         ^^^^^^^^^^ help: try: `0.5_f32`
 
 error: casting integer literal to `i32` is unnecessary
-  --> $DIR/unnecessary_cast.rs:83:17
+  --> $DIR/unnecessary_cast.rs:94:17
    |
 LL |         let _ = -1 as i32;
    |                 ^^^^^^^^^ help: try: `-1_i32`
 
 error: casting float literal to `f32` is unnecessary
-  --> $DIR/unnecessary_cast.rs:84:17
+  --> $DIR/unnecessary_cast.rs:95:17
    |
 LL |         let _ = -1.0 as f32;
    |                 ^^^^^^^^^^^ help: try: `-1.0_f32`
 
+error: casting to the same type is unnecessary (`i32` -> `i32`)
+  --> $DIR/unnecessary_cast.rs:101:18
+   |
+LL |         let _ = &(x as i32);
+   |                  ^^^^^^^^^^ help: try: `{ x }`
+
 error: casting integer literal to `i32` is unnecessary
-  --> $DIR/unnecessary_cast.rs:93:22
+  --> $DIR/unnecessary_cast.rs:107:22
    |
 LL |         let _: i32 = -(1) as i32;
    |                      ^^^^^^^^^^^ help: try: `-1_i32`
 
 error: casting integer literal to `i64` is unnecessary
-  --> $DIR/unnecessary_cast.rs:95:22
+  --> $DIR/unnecessary_cast.rs:109:22
    |
 LL |         let _: i64 = -(1) as i64;
    |                      ^^^^^^^^^^^ help: try: `-1_i64`
 
 error: casting float literal to `f64` is unnecessary
-  --> $DIR/unnecessary_cast.rs:102:22
+  --> $DIR/unnecessary_cast.rs:116:22
    |
 LL |         let _: f64 = (-8.0 as f64).exp();
    |                      ^^^^^^^^^^^^^ help: try: `(-8.0_f64)`
 
 error: casting float literal to `f64` is unnecessary
-  --> $DIR/unnecessary_cast.rs:104:23
+  --> $DIR/unnecessary_cast.rs:118:23
    |
 LL |         let _: f64 = -(8.0 as f64).exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior
    |                       ^^^^^^^^^^^^ help: try: `8.0_f64`
 
 error: casting to the same type is unnecessary (`f32` -> `f32`)
-  --> $DIR/unnecessary_cast.rs:112:20
+  --> $DIR/unnecessary_cast.rs:126:20
    |
 LL |         let _num = foo() as f32;
    |                    ^^^^^^^^^^^^ help: try: `foo()`
 
-error: aborting due to 30 previous errors
+error: aborting due to 31 previous errors
 
diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed
index ce4a82e0217..22e9bd8bdc5 100644
--- a/tests/ui/unnecessary_lazy_eval.fixed
+++ b/tests/ui/unnecessary_lazy_eval.fixed
@@ -33,6 +33,14 @@ impl Drop for Issue9427 {
     }
 }
 
+struct Issue9427FollowUp;
+
+impl Drop for Issue9427FollowUp {
+    fn drop(&mut self) {
+        panic!("side effect drop");
+    }
+}
+
 fn main() {
     let astronomers_pi = 10;
     let ext_arr: [usize; 1] = [2];
@@ -87,6 +95,7 @@ fn main() {
 
     // Should not lint - bool
     let _ = (0 == 1).then(|| Issue9427(0)); // Issue9427 has a significant drop
+    let _ = false.then(|| Issue9427FollowUp); // Issue9427FollowUp has a significant drop
 
     // should not lint, bind_instead_of_map takes priority
     let _ = Some(10).and_then(|idx| Some(ext_arr[idx]));
@@ -133,13 +142,13 @@ fn main() {
     let _: Result<usize, usize> = res.or(Ok(astronomers_pi));
     let _: Result<usize, usize> = res.or(Ok(ext_str.some_field));
     let _: Result<usize, usize> = res.
-        // some lines
-        // some lines
-        // some lines
-        // some lines
-        // some lines
-        // some lines
-        or(Ok(ext_str.some_field));
+    // some lines
+    // some lines
+    // some lines
+    // some lines
+    // some lines
+    // some lines
+    or(Ok(ext_str.some_field));
 
     // neither bind_instead_of_map nor unnecessary_lazy_eval applies here
     let _: Result<usize, usize> = res.and_then(|x| Err(x));
diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs
index 59cdf662854..8726d84a23f 100644
--- a/tests/ui/unnecessary_lazy_eval.rs
+++ b/tests/ui/unnecessary_lazy_eval.rs
@@ -33,6 +33,14 @@ impl Drop for Issue9427 {
     }
 }
 
+struct Issue9427FollowUp;
+
+impl Drop for Issue9427FollowUp {
+    fn drop(&mut self) {
+        panic!("side effect drop");
+    }
+}
+
 fn main() {
     let astronomers_pi = 10;
     let ext_arr: [usize; 1] = [2];
@@ -87,6 +95,7 @@ fn main() {
 
     // Should not lint - bool
     let _ = (0 == 1).then(|| Issue9427(0)); // Issue9427 has a significant drop
+    let _ = false.then(|| Issue9427FollowUp); // Issue9427FollowUp has a significant drop
 
     // should not lint, bind_instead_of_map takes priority
     let _ = Some(10).and_then(|idx| Some(ext_arr[idx]));
@@ -133,13 +142,13 @@ fn main() {
     let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
     let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
     let _: Result<usize, usize> = res.
-        // some lines
-        // some lines
-        // some lines
-        // some lines
-        // some lines
-        // some lines
-        or_else(|_| Ok(ext_str.some_field));
+    // some lines
+    // some lines
+    // some lines
+    // some lines
+    // some lines
+    // some lines
+    or_else(|_| Ok(ext_str.some_field));
 
     // neither bind_instead_of_map nor unnecessary_lazy_eval applies here
     let _: Result<usize, usize> = res.and_then(|x| Err(x));
diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr
index 8a9ece4aa7e..0339755442c 100644
--- a/tests/ui/unnecessary_lazy_eval.stderr
+++ b/tests/ui/unnecessary_lazy_eval.stderr
@@ -1,5 +1,5 @@
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:48:13
+  --> $DIR/unnecessary_lazy_eval.rs:56:13
    |
 LL |     let _ = opt.unwrap_or_else(|| 2);
    |             ^^^^--------------------
@@ -9,7 +9,7 @@ LL |     let _ = opt.unwrap_or_else(|| 2);
    = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:49:13
+  --> $DIR/unnecessary_lazy_eval.rs:57:13
    |
 LL |     let _ = opt.unwrap_or_else(|| astronomers_pi);
    |             ^^^^---------------------------------
@@ -17,7 +17,7 @@ LL |     let _ = opt.unwrap_or_else(|| astronomers_pi);
    |                 help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:50:13
+  --> $DIR/unnecessary_lazy_eval.rs:58:13
    |
 LL |     let _ = opt.unwrap_or_else(|| ext_str.some_field);
    |             ^^^^-------------------------------------
@@ -25,7 +25,7 @@ LL |     let _ = opt.unwrap_or_else(|| ext_str.some_field);
    |                 help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:52:13
+  --> $DIR/unnecessary_lazy_eval.rs:60:13
    |
 LL |     let _ = opt.and_then(|_| ext_opt);
    |             ^^^^---------------------
@@ -33,7 +33,7 @@ LL |     let _ = opt.and_then(|_| ext_opt);
    |                 help: use `and(..)` instead: `and(ext_opt)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:53:13
+  --> $DIR/unnecessary_lazy_eval.rs:61:13
    |
 LL |     let _ = opt.or_else(|| ext_opt);
    |             ^^^^-------------------
@@ -41,7 +41,7 @@ LL |     let _ = opt.or_else(|| ext_opt);
    |                 help: use `or(..)` instead: `or(ext_opt)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:54:13
+  --> $DIR/unnecessary_lazy_eval.rs:62:13
    |
 LL |     let _ = opt.or_else(|| None);
    |             ^^^^----------------
@@ -49,7 +49,7 @@ LL |     let _ = opt.or_else(|| None);
    |                 help: use `or(..)` instead: `or(None)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:55:13
+  --> $DIR/unnecessary_lazy_eval.rs:63:13
    |
 LL |     let _ = opt.get_or_insert_with(|| 2);
    |             ^^^^------------------------
@@ -57,7 +57,7 @@ LL |     let _ = opt.get_or_insert_with(|| 2);
    |                 help: use `get_or_insert(..)` instead: `get_or_insert(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:56:13
+  --> $DIR/unnecessary_lazy_eval.rs:64:13
    |
 LL |     let _ = opt.ok_or_else(|| 2);
    |             ^^^^----------------
@@ -65,7 +65,7 @@ LL |     let _ = opt.ok_or_else(|| 2);
    |                 help: use `ok_or(..)` instead: `ok_or(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:57:13
+  --> $DIR/unnecessary_lazy_eval.rs:65:13
    |
 LL |     let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
    |             ^^^^^^^^^^^^^^^^^-------------------------------
@@ -73,7 +73,7 @@ LL |     let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
    |                              help: use `unwrap_or(..)` instead: `unwrap_or(Some((1, 2)))`
 
 error: unnecessary closure used with `bool::then`
-  --> $DIR/unnecessary_lazy_eval.rs:58:13
+  --> $DIR/unnecessary_lazy_eval.rs:66:13
    |
 LL |     let _ = cond.then(|| astronomers_pi);
    |             ^^^^^-----------------------
@@ -81,7 +81,7 @@ LL |     let _ = cond.then(|| astronomers_pi);
    |                  help: use `then_some(..)` instead: `then_some(astronomers_pi)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:61:13
+  --> $DIR/unnecessary_lazy_eval.rs:69:13
    |
 LL |     let _ = Some(10).unwrap_or_else(|| 2);
    |             ^^^^^^^^^--------------------
@@ -89,7 +89,7 @@ LL |     let _ = Some(10).unwrap_or_else(|| 2);
    |                      help: use `unwrap_or(..)` instead: `unwrap_or(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:62:13
+  --> $DIR/unnecessary_lazy_eval.rs:70:13
    |
 LL |     let _ = Some(10).and_then(|_| ext_opt);
    |             ^^^^^^^^^---------------------
@@ -97,7 +97,7 @@ LL |     let _ = Some(10).and_then(|_| ext_opt);
    |                      help: use `and(..)` instead: `and(ext_opt)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:63:28
+  --> $DIR/unnecessary_lazy_eval.rs:71:28
    |
 LL |     let _: Option<usize> = None.or_else(|| ext_opt);
    |                            ^^^^^-------------------
@@ -105,7 +105,7 @@ LL |     let _: Option<usize> = None.or_else(|| ext_opt);
    |                                 help: use `or(..)` instead: `or(ext_opt)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:64:13
+  --> $DIR/unnecessary_lazy_eval.rs:72:13
    |
 LL |     let _ = None.get_or_insert_with(|| 2);
    |             ^^^^^------------------------
@@ -113,7 +113,7 @@ LL |     let _ = None.get_or_insert_with(|| 2);
    |                  help: use `get_or_insert(..)` instead: `get_or_insert(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:65:35
+  --> $DIR/unnecessary_lazy_eval.rs:73:35
    |
 LL |     let _: Result<usize, usize> = None.ok_or_else(|| 2);
    |                                   ^^^^^----------------
@@ -121,7 +121,7 @@ LL |     let _: Result<usize, usize> = None.ok_or_else(|| 2);
    |                                        help: use `ok_or(..)` instead: `ok_or(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:66:28
+  --> $DIR/unnecessary_lazy_eval.rs:74:28
    |
 LL |     let _: Option<usize> = None.or_else(|| None);
    |                            ^^^^^----------------
@@ -129,7 +129,7 @@ LL |     let _: Option<usize> = None.or_else(|| None);
    |                                 help: use `or(..)` instead: `or(None)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:69:13
+  --> $DIR/unnecessary_lazy_eval.rs:77:13
    |
 LL |     let _ = deep.0.unwrap_or_else(|| 2);
    |             ^^^^^^^--------------------
@@ -137,7 +137,7 @@ LL |     let _ = deep.0.unwrap_or_else(|| 2);
    |                    help: use `unwrap_or(..)` instead: `unwrap_or(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:70:13
+  --> $DIR/unnecessary_lazy_eval.rs:78:13
    |
 LL |     let _ = deep.0.and_then(|_| ext_opt);
    |             ^^^^^^^---------------------
@@ -145,7 +145,7 @@ LL |     let _ = deep.0.and_then(|_| ext_opt);
    |                    help: use `and(..)` instead: `and(ext_opt)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:71:13
+  --> $DIR/unnecessary_lazy_eval.rs:79:13
    |
 LL |     let _ = deep.0.or_else(|| None);
    |             ^^^^^^^----------------
@@ -153,7 +153,7 @@ LL |     let _ = deep.0.or_else(|| None);
    |                    help: use `or(..)` instead: `or(None)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:72:13
+  --> $DIR/unnecessary_lazy_eval.rs:80:13
    |
 LL |     let _ = deep.0.get_or_insert_with(|| 2);
    |             ^^^^^^^------------------------
@@ -161,7 +161,7 @@ LL |     let _ = deep.0.get_or_insert_with(|| 2);
    |                    help: use `get_or_insert(..)` instead: `get_or_insert(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:73:13
+  --> $DIR/unnecessary_lazy_eval.rs:81:13
    |
 LL |     let _ = deep.0.ok_or_else(|| 2);
    |             ^^^^^^^----------------
@@ -169,7 +169,7 @@ LL |     let _ = deep.0.ok_or_else(|| 2);
    |                    help: use `ok_or(..)` instead: `ok_or(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:96:28
+  --> $DIR/unnecessary_lazy_eval.rs:105:28
    |
 LL |     let _: Option<usize> = None.or_else(|| Some(3));
    |                            ^^^^^-------------------
@@ -177,7 +177,7 @@ LL |     let _: Option<usize> = None.or_else(|| Some(3));
    |                                 help: use `or(..)` instead: `or(Some(3))`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:97:13
+  --> $DIR/unnecessary_lazy_eval.rs:106:13
    |
 LL |     let _ = deep.0.or_else(|| Some(3));
    |             ^^^^^^^-------------------
@@ -185,7 +185,7 @@ LL |     let _ = deep.0.or_else(|| Some(3));
    |                    help: use `or(..)` instead: `or(Some(3))`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:98:13
+  --> $DIR/unnecessary_lazy_eval.rs:107:13
    |
 LL |     let _ = opt.or_else(|| Some(3));
    |             ^^^^-------------------
@@ -193,7 +193,7 @@ LL |     let _ = opt.or_else(|| Some(3));
    |                 help: use `or(..)` instead: `or(Some(3))`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:104:13
+  --> $DIR/unnecessary_lazy_eval.rs:113:13
    |
 LL |     let _ = res2.unwrap_or_else(|_| 2);
    |             ^^^^^---------------------
@@ -201,7 +201,7 @@ LL |     let _ = res2.unwrap_or_else(|_| 2);
    |                  help: use `unwrap_or(..)` instead: `unwrap_or(2)`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:105:13
+  --> $DIR/unnecessary_lazy_eval.rs:114:13
    |
 LL |     let _ = res2.unwrap_or_else(|_| astronomers_pi);
    |             ^^^^^----------------------------------
@@ -209,7 +209,7 @@ LL |     let _ = res2.unwrap_or_else(|_| astronomers_pi);
    |                  help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:106:13
+  --> $DIR/unnecessary_lazy_eval.rs:115:13
    |
 LL |     let _ = res2.unwrap_or_else(|_| ext_str.some_field);
    |             ^^^^^--------------------------------------
@@ -217,7 +217,7 @@ LL |     let _ = res2.unwrap_or_else(|_| ext_str.some_field);
    |                  help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:128:35
+  --> $DIR/unnecessary_lazy_eval.rs:137:35
    |
 LL |     let _: Result<usize, usize> = res.and_then(|_| Err(2));
    |                                   ^^^^--------------------
@@ -225,7 +225,7 @@ LL |     let _: Result<usize, usize> = res.and_then(|_| Err(2));
    |                                       help: use `and(..)` instead: `and(Err(2))`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:129:35
+  --> $DIR/unnecessary_lazy_eval.rs:138:35
    |
 LL |     let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi));
    |                                   ^^^^---------------------------------
@@ -233,7 +233,7 @@ LL |     let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi));
    |                                       help: use `and(..)` instead: `and(Err(astronomers_pi))`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:130:35
+  --> $DIR/unnecessary_lazy_eval.rs:139:35
    |
 LL |     let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field));
    |                                   ^^^^-------------------------------------
@@ -241,7 +241,7 @@ LL |     let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field))
    |                                       help: use `and(..)` instead: `and(Err(ext_str.some_field))`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:132:35
+  --> $DIR/unnecessary_lazy_eval.rs:141:35
    |
 LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(2));
    |                                   ^^^^------------------
@@ -249,7 +249,7 @@ LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(2));
    |                                       help: use `or(..)` instead: `or(Ok(2))`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:133:35
+  --> $DIR/unnecessary_lazy_eval.rs:142:35
    |
 LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
    |                                   ^^^^-------------------------------
@@ -257,7 +257,7 @@ LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
    |                                       help: use `or(..)` instead: `or(Ok(astronomers_pi))`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:134:35
+  --> $DIR/unnecessary_lazy_eval.rs:143:35
    |
 LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
    |                                   ^^^^-----------------------------------
@@ -265,19 +265,19 @@ LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
    |                                       help: use `or(..)` instead: `or(Ok(ext_str.some_field))`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:135:35
+  --> $DIR/unnecessary_lazy_eval.rs:144:35
    |
 LL |       let _: Result<usize, usize> = res.
    |  ___________________________________^
-LL | |         // some lines
-LL | |         // some lines
-LL | |         // some lines
+LL | |     // some lines
+LL | |     // some lines
+LL | |     // some lines
 ...  |
-LL | |         // some lines
-LL | |         or_else(|_| Ok(ext_str.some_field));
-   | |_________----------------------------------^
-   |           |
-   |           help: use `or(..)` instead: `or(Ok(ext_str.some_field))`
+LL | |     // some lines
+LL | |     or_else(|_| Ok(ext_str.some_field));
+   | |_____----------------------------------^
+   |       |
+   |       help: use `or(..)` instead: `or(Ok(ext_str.some_field))`
 
 error: aborting due to 34 previous errors
 
diff --git a/tests/ui/unnecessary_operation.fixed b/tests/ui/unnecessary_operation.fixed
index bf0ec8deb34..d37163570ab 100644
--- a/tests/ui/unnecessary_operation.fixed
+++ b/tests/ui/unnecessary_operation.fixed
@@ -76,4 +76,13 @@ fn main() {
     DropStruct { ..get_drop_struct() };
     DropEnum::Tuple(get_number());
     DropEnum::Struct { field: get_number() };
+
+    // Issue #9954
+    fn one() -> i8 {
+        1
+    }
+    macro_rules! use_expr {
+        ($($e:expr),*) => {{ $($e;)* }}
+    }
+    use_expr!(isize::MIN / -(one() as isize), i8::MIN / -one());
 }
diff --git a/tests/ui/unnecessary_operation.rs b/tests/ui/unnecessary_operation.rs
index 08cb9ab522e..a14fd4bca0e 100644
--- a/tests/ui/unnecessary_operation.rs
+++ b/tests/ui/unnecessary_operation.rs
@@ -80,4 +80,13 @@ fn main() {
     DropStruct { ..get_drop_struct() };
     DropEnum::Tuple(get_number());
     DropEnum::Struct { field: get_number() };
+
+    // Issue #9954
+    fn one() -> i8 {
+        1
+    }
+    macro_rules! use_expr {
+        ($($e:expr),*) => {{ $($e;)* }}
+    }
+    use_expr!(isize::MIN / -(one() as isize), i8::MIN / -one());
 }
diff --git a/tests/ui/unnecessary_safety_comment.rs b/tests/ui/unnecessary_safety_comment.rs
new file mode 100644
index 00000000000..7fefea7051d
--- /dev/null
+++ b/tests/ui/unnecessary_safety_comment.rs
@@ -0,0 +1,51 @@
+#![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)]
+#![allow(clippy::let_unit_value, clippy::missing_safety_doc)]
+
+mod unsafe_items_invalid_comment {
+    // SAFETY:
+    const CONST: u32 = 0;
+    // SAFETY:
+    static STATIC: u32 = 0;
+    // SAFETY:
+    struct Struct;
+    // SAFETY:
+    enum Enum {}
+    // SAFETY:
+    mod module {}
+}
+
+mod unnecessary_from_macro {
+    trait T {}
+
+    macro_rules! no_safety_comment {
+        ($t:ty) => {
+            impl T for $t {}
+        };
+    }
+
+    // FIXME: This is not caught
+    // Safety: unnecessary
+    no_safety_comment!(());
+
+    macro_rules! with_safety_comment {
+        ($t:ty) => {
+            // Safety: unnecessary
+            impl T for $t {}
+        };
+    }
+
+    with_safety_comment!(i32);
+}
+
+fn unnecessary_on_stmt_and_expr() -> u32 {
+    // SAFETY: unnecessary
+    let num = 42;
+
+    // SAFETY: unnecessary
+    if num > 24 {}
+
+    // SAFETY: unnecessary
+    24
+}
+
+fn main() {}
diff --git a/tests/ui/unnecessary_safety_comment.stderr b/tests/ui/unnecessary_safety_comment.stderr
new file mode 100644
index 00000000000..7b2af67d64c
--- /dev/null
+++ b/tests/ui/unnecessary_safety_comment.stderr
@@ -0,0 +1,115 @@
+error: constant item has unnecessary safety comment
+  --> $DIR/unnecessary_safety_comment.rs:6:5
+   |
+LL |     const CONST: u32 = 0;
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider removing the safety comment
+  --> $DIR/unnecessary_safety_comment.rs:5:5
+   |
+LL |     // SAFETY:
+   |     ^^^^^^^^^^
+   = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings`
+
+error: static item has unnecessary safety comment
+  --> $DIR/unnecessary_safety_comment.rs:8:5
+   |
+LL |     static STATIC: u32 = 0;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider removing the safety comment
+  --> $DIR/unnecessary_safety_comment.rs:7:5
+   |
+LL |     // SAFETY:
+   |     ^^^^^^^^^^
+
+error: struct has unnecessary safety comment
+  --> $DIR/unnecessary_safety_comment.rs:10:5
+   |
+LL |     struct Struct;
+   |     ^^^^^^^^^^^^^^
+   |
+help: consider removing the safety comment
+  --> $DIR/unnecessary_safety_comment.rs:9:5
+   |
+LL |     // SAFETY:
+   |     ^^^^^^^^^^
+
+error: enum has unnecessary safety comment
+  --> $DIR/unnecessary_safety_comment.rs:12:5
+   |
+LL |     enum Enum {}
+   |     ^^^^^^^^^^^^
+   |
+help: consider removing the safety comment
+  --> $DIR/unnecessary_safety_comment.rs:11:5
+   |
+LL |     // SAFETY:
+   |     ^^^^^^^^^^
+
+error: module has unnecessary safety comment
+  --> $DIR/unnecessary_safety_comment.rs:14:5
+   |
+LL |     mod module {}
+   |     ^^^^^^^^^^^^^
+   |
+help: consider removing the safety comment
+  --> $DIR/unnecessary_safety_comment.rs:13:5
+   |
+LL |     // SAFETY:
+   |     ^^^^^^^^^^
+
+error: impl has unnecessary safety comment
+  --> $DIR/unnecessary_safety_comment.rs:33:13
+   |
+LL |             impl T for $t {}
+   |             ^^^^^^^^^^^^^^^^
+...
+LL |     with_safety_comment!(i32);
+   |     ------------------------- in this macro invocation
+   |
+help: consider removing the safety comment
+  --> $DIR/unnecessary_safety_comment.rs:32:13
+   |
+LL |             // Safety: unnecessary
+   |             ^^^^^^^^^^^^^^^^^^^^^^
+   = note: this error originates in the macro `with_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: expression has unnecessary safety comment
+  --> $DIR/unnecessary_safety_comment.rs:48:5
+   |
+LL |     24
+   |     ^^
+   |
+help: consider removing the safety comment
+  --> $DIR/unnecessary_safety_comment.rs:47:5
+   |
+LL |     // SAFETY: unnecessary
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+
+error: statement has unnecessary safety comment
+  --> $DIR/unnecessary_safety_comment.rs:42:5
+   |
+LL |     let num = 42;
+   |     ^^^^^^^^^^^^^
+   |
+help: consider removing the safety comment
+  --> $DIR/unnecessary_safety_comment.rs:41:5
+   |
+LL |     // SAFETY: unnecessary
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+
+error: statement has unnecessary safety comment
+  --> $DIR/unnecessary_safety_comment.rs:45:5
+   |
+LL |     if num > 24 {}
+   |     ^^^^^^^^^^^^^^
+   |
+help: consider removing the safety comment
+  --> $DIR/unnecessary_safety_comment.rs:44:5
+   |
+LL |     // SAFETY: unnecessary
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 9 previous errors
+
diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed
index fe09aad06bc..ddeda795f81 100644
--- a/tests/ui/unnecessary_to_owned.fixed
+++ b/tests/ui/unnecessary_to_owned.fixed
@@ -2,7 +2,6 @@
 
 #![allow(clippy::needless_borrow, clippy::ptr_arg)]
 #![warn(clippy::unnecessary_to_owned)]
-#![feature(custom_inner_attributes)]
 
 use std::borrow::Cow;
 use std::ffi::{CStr, CString, OsStr, OsString};
@@ -215,14 +214,14 @@ fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::E
 
 fn require_string(_: &String) {}
 
+#[clippy::msrv = "1.35"]
 fn _msrv_1_35() {
-    #![clippy::msrv = "1.35"]
     // `copied` was stabilized in 1.36, so clippy should use `cloned`.
     let _ = &["x"][..].iter().cloned();
 }
 
+#[clippy::msrv = "1.36"]
 fn _msrv_1_36() {
-    #![clippy::msrv = "1.36"]
     let _ = &["x"][..].iter().copied();
 }
 
@@ -426,3 +425,32 @@ mod issue_9504 {
         foo(std::path::PathBuf::new().to_string_lossy().to_string()).await;
     }
 }
+
+mod issue_9771a {
+    #![allow(dead_code)]
+
+    use std::marker::PhantomData;
+
+    pub struct Key<K: AsRef<[u8]>, V: ?Sized>(K, PhantomData<V>);
+
+    impl<K: AsRef<[u8]>, V: ?Sized> Key<K, V> {
+        pub fn new(key: K) -> Key<K, V> {
+            Key(key, PhantomData)
+        }
+    }
+
+    pub fn pkh(pkh: &[u8]) -> Key<Vec<u8>, String> {
+        Key::new([b"pkh-", pkh].concat().to_vec())
+    }
+}
+
+mod issue_9771b {
+    #![allow(dead_code)]
+
+    pub struct Key<K: AsRef<[u8]>>(K);
+
+    pub fn from(c: &[u8]) -> Key<Vec<u8>> {
+        let v = [c].concat();
+        Key(v.to_vec())
+    }
+}
diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs
index 3de6d0903c0..95d2576733c 100644
--- a/tests/ui/unnecessary_to_owned.rs
+++ b/tests/ui/unnecessary_to_owned.rs
@@ -2,7 +2,6 @@
 
 #![allow(clippy::needless_borrow, clippy::ptr_arg)]
 #![warn(clippy::unnecessary_to_owned)]
-#![feature(custom_inner_attributes)]
 
 use std::borrow::Cow;
 use std::ffi::{CStr, CString, OsStr, OsString};
@@ -215,14 +214,14 @@ fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::E
 
 fn require_string(_: &String) {}
 
+#[clippy::msrv = "1.35"]
 fn _msrv_1_35() {
-    #![clippy::msrv = "1.35"]
     // `copied` was stabilized in 1.36, so clippy should use `cloned`.
     let _ = &["x"][..].to_vec().into_iter();
 }
 
+#[clippy::msrv = "1.36"]
 fn _msrv_1_36() {
-    #![clippy::msrv = "1.36"]
     let _ = &["x"][..].to_vec().into_iter();
 }
 
@@ -426,3 +425,32 @@ mod issue_9504 {
         foo(std::path::PathBuf::new().to_string_lossy().to_string()).await;
     }
 }
+
+mod issue_9771a {
+    #![allow(dead_code)]
+
+    use std::marker::PhantomData;
+
+    pub struct Key<K: AsRef<[u8]>, V: ?Sized>(K, PhantomData<V>);
+
+    impl<K: AsRef<[u8]>, V: ?Sized> Key<K, V> {
+        pub fn new(key: K) -> Key<K, V> {
+            Key(key, PhantomData)
+        }
+    }
+
+    pub fn pkh(pkh: &[u8]) -> Key<Vec<u8>, String> {
+        Key::new([b"pkh-", pkh].concat().to_vec())
+    }
+}
+
+mod issue_9771b {
+    #![allow(dead_code)]
+
+    pub struct Key<K: AsRef<[u8]>>(K);
+
+    pub fn from(c: &[u8]) -> Key<Vec<u8>> {
+        let v = [c].concat();
+        Key(v.to_vec())
+    }
+}
diff --git a/tests/ui/unnecessary_to_owned.stderr b/tests/ui/unnecessary_to_owned.stderr
index 02bf45a33fb..4918fe35598 100644
--- a/tests/ui/unnecessary_to_owned.stderr
+++ b/tests/ui/unnecessary_to_owned.stderr
@@ -1,66 +1,66 @@
 error: redundant clone
-  --> $DIR/unnecessary_to_owned.rs:151:64
+  --> $DIR/unnecessary_to_owned.rs:150:64
    |
 LL |     require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned());
    |                                                                ^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/unnecessary_to_owned.rs:151:20
+  --> $DIR/unnecessary_to_owned.rs:150:20
    |
 LL |     require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned());
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: `-D clippy::redundant-clone` implied by `-D warnings`
 
 error: redundant clone
-  --> $DIR/unnecessary_to_owned.rs:152:40
+  --> $DIR/unnecessary_to_owned.rs:151:40
    |
 LL |     require_os_str(&OsString::from("x").to_os_string());
    |                                        ^^^^^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/unnecessary_to_owned.rs:152:21
+  --> $DIR/unnecessary_to_owned.rs:151:21
    |
 LL |     require_os_str(&OsString::from("x").to_os_string());
    |                     ^^^^^^^^^^^^^^^^^^^
 
 error: redundant clone
-  --> $DIR/unnecessary_to_owned.rs:153:48
+  --> $DIR/unnecessary_to_owned.rs:152:48
    |
 LL |     require_path(&std::path::PathBuf::from("x").to_path_buf());
    |                                                ^^^^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/unnecessary_to_owned.rs:153:19
+  --> $DIR/unnecessary_to_owned.rs:152:19
    |
 LL |     require_path(&std::path::PathBuf::from("x").to_path_buf());
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: redundant clone
-  --> $DIR/unnecessary_to_owned.rs:154:35
+  --> $DIR/unnecessary_to_owned.rs:153:35
    |
 LL |     require_str(&String::from("x").to_string());
    |                                   ^^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/unnecessary_to_owned.rs:154:18
+  --> $DIR/unnecessary_to_owned.rs:153:18
    |
 LL |     require_str(&String::from("x").to_string());
    |                  ^^^^^^^^^^^^^^^^^
 
 error: redundant clone
-  --> $DIR/unnecessary_to_owned.rs:155:39
+  --> $DIR/unnecessary_to_owned.rs:154:39
    |
 LL |     require_slice(&[String::from("x")].to_owned());
    |                                       ^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/unnecessary_to_owned.rs:155:20
+  --> $DIR/unnecessary_to_owned.rs:154:20
    |
 LL |     require_slice(&[String::from("x")].to_owned());
    |                    ^^^^^^^^^^^^^^^^^^^
 
 error: unnecessary use of `into_owned`
-  --> $DIR/unnecessary_to_owned.rs:60:36
+  --> $DIR/unnecessary_to_owned.rs:59:36
    |
 LL |     require_c_str(&Cow::from(c_str).into_owned());
    |                                    ^^^^^^^^^^^^^ help: remove this
@@ -68,415 +68,415 @@ LL |     require_c_str(&Cow::from(c_str).into_owned());
    = note: `-D clippy::unnecessary-to-owned` implied by `-D warnings`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:61:19
+  --> $DIR/unnecessary_to_owned.rs:60:19
    |
 LL |     require_c_str(&c_str.to_owned());
    |                   ^^^^^^^^^^^^^^^^^ help: use: `c_str`
 
 error: unnecessary use of `to_os_string`
-  --> $DIR/unnecessary_to_owned.rs:63:20
+  --> $DIR/unnecessary_to_owned.rs:62:20
    |
 LL |     require_os_str(&os_str.to_os_string());
    |                    ^^^^^^^^^^^^^^^^^^^^^^ help: use: `os_str`
 
 error: unnecessary use of `into_owned`
-  --> $DIR/unnecessary_to_owned.rs:64:38
+  --> $DIR/unnecessary_to_owned.rs:63:38
    |
 LL |     require_os_str(&Cow::from(os_str).into_owned());
    |                                      ^^^^^^^^^^^^^ help: remove this
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:65:20
+  --> $DIR/unnecessary_to_owned.rs:64:20
    |
 LL |     require_os_str(&os_str.to_owned());
    |                    ^^^^^^^^^^^^^^^^^^ help: use: `os_str`
 
 error: unnecessary use of `to_path_buf`
-  --> $DIR/unnecessary_to_owned.rs:67:18
+  --> $DIR/unnecessary_to_owned.rs:66:18
    |
 LL |     require_path(&path.to_path_buf());
    |                  ^^^^^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `into_owned`
-  --> $DIR/unnecessary_to_owned.rs:68:34
+  --> $DIR/unnecessary_to_owned.rs:67:34
    |
 LL |     require_path(&Cow::from(path).into_owned());
    |                                  ^^^^^^^^^^^^^ help: remove this
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:69:18
+  --> $DIR/unnecessary_to_owned.rs:68:18
    |
 LL |     require_path(&path.to_owned());
    |                  ^^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `to_string`
-  --> $DIR/unnecessary_to_owned.rs:71:17
+  --> $DIR/unnecessary_to_owned.rs:70:17
    |
 LL |     require_str(&s.to_string());
    |                 ^^^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `into_owned`
-  --> $DIR/unnecessary_to_owned.rs:72:30
+  --> $DIR/unnecessary_to_owned.rs:71:30
    |
 LL |     require_str(&Cow::from(s).into_owned());
    |                              ^^^^^^^^^^^^^ help: remove this
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:73:17
+  --> $DIR/unnecessary_to_owned.rs:72:17
    |
 LL |     require_str(&s.to_owned());
    |                 ^^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_string`
-  --> $DIR/unnecessary_to_owned.rs:74:17
+  --> $DIR/unnecessary_to_owned.rs:73:17
    |
 LL |     require_str(&x_ref.to_string());
    |                 ^^^^^^^^^^^^^^^^^^ help: use: `x_ref.as_ref()`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:76:19
+  --> $DIR/unnecessary_to_owned.rs:75:19
    |
 LL |     require_slice(&slice.to_vec());
    |                   ^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `into_owned`
-  --> $DIR/unnecessary_to_owned.rs:77:36
+  --> $DIR/unnecessary_to_owned.rs:76:36
    |
 LL |     require_slice(&Cow::from(slice).into_owned());
    |                                    ^^^^^^^^^^^^^ help: remove this
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:78:19
+  --> $DIR/unnecessary_to_owned.rs:77:19
    |
 LL |     require_slice(&array.to_owned());
    |                   ^^^^^^^^^^^^^^^^^ help: use: `array.as_ref()`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:79:19
+  --> $DIR/unnecessary_to_owned.rs:78:19
    |
 LL |     require_slice(&array_ref.to_owned());
    |                   ^^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref.as_ref()`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:80:19
+  --> $DIR/unnecessary_to_owned.rs:79:19
    |
 LL |     require_slice(&slice.to_owned());
    |                   ^^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `into_owned`
-  --> $DIR/unnecessary_to_owned.rs:83:42
+  --> $DIR/unnecessary_to_owned.rs:82:42
    |
 LL |     require_x(&Cow::<X>::Owned(x.clone()).into_owned());
    |                                          ^^^^^^^^^^^^^ help: remove this
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:86:25
+  --> $DIR/unnecessary_to_owned.rs:85:25
    |
 LL |     require_deref_c_str(c_str.to_owned());
    |                         ^^^^^^^^^^^^^^^^ help: use: `c_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:87:26
+  --> $DIR/unnecessary_to_owned.rs:86:26
    |
 LL |     require_deref_os_str(os_str.to_owned());
    |                          ^^^^^^^^^^^^^^^^^ help: use: `os_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:88:24
+  --> $DIR/unnecessary_to_owned.rs:87:24
    |
 LL |     require_deref_path(path.to_owned());
    |                        ^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:89:23
+  --> $DIR/unnecessary_to_owned.rs:88:23
    |
 LL |     require_deref_str(s.to_owned());
    |                       ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:90:25
+  --> $DIR/unnecessary_to_owned.rs:89:25
    |
 LL |     require_deref_slice(slice.to_owned());
    |                         ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:92:30
+  --> $DIR/unnecessary_to_owned.rs:91:30
    |
 LL |     require_impl_deref_c_str(c_str.to_owned());
    |                              ^^^^^^^^^^^^^^^^ help: use: `c_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:93:31
+  --> $DIR/unnecessary_to_owned.rs:92:31
    |
 LL |     require_impl_deref_os_str(os_str.to_owned());
    |                               ^^^^^^^^^^^^^^^^^ help: use: `os_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:94:29
+  --> $DIR/unnecessary_to_owned.rs:93:29
    |
 LL |     require_impl_deref_path(path.to_owned());
    |                             ^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:95:28
+  --> $DIR/unnecessary_to_owned.rs:94:28
    |
 LL |     require_impl_deref_str(s.to_owned());
    |                            ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:96:30
+  --> $DIR/unnecessary_to_owned.rs:95:30
    |
 LL |     require_impl_deref_slice(slice.to_owned());
    |                              ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:98:29
+  --> $DIR/unnecessary_to_owned.rs:97:29
    |
 LL |     require_deref_str_slice(s.to_owned(), slice.to_owned());
    |                             ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:98:43
+  --> $DIR/unnecessary_to_owned.rs:97:43
    |
 LL |     require_deref_str_slice(s.to_owned(), slice.to_owned());
    |                                           ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:99:29
+  --> $DIR/unnecessary_to_owned.rs:98:29
    |
 LL |     require_deref_slice_str(slice.to_owned(), s.to_owned());
    |                             ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:99:47
+  --> $DIR/unnecessary_to_owned.rs:98:47
    |
 LL |     require_deref_slice_str(slice.to_owned(), s.to_owned());
    |                                               ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:101:26
+  --> $DIR/unnecessary_to_owned.rs:100:26
    |
 LL |     require_as_ref_c_str(c_str.to_owned());
    |                          ^^^^^^^^^^^^^^^^ help: use: `c_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:102:27
+  --> $DIR/unnecessary_to_owned.rs:101:27
    |
 LL |     require_as_ref_os_str(os_str.to_owned());
    |                           ^^^^^^^^^^^^^^^^^ help: use: `os_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:103:25
+  --> $DIR/unnecessary_to_owned.rs:102:25
    |
 LL |     require_as_ref_path(path.to_owned());
    |                         ^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:104:24
+  --> $DIR/unnecessary_to_owned.rs:103:24
    |
 LL |     require_as_ref_str(s.to_owned());
    |                        ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:105:24
+  --> $DIR/unnecessary_to_owned.rs:104:24
    |
 LL |     require_as_ref_str(x.to_owned());
    |                        ^^^^^^^^^^^^ help: use: `&x`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:106:26
+  --> $DIR/unnecessary_to_owned.rs:105:26
    |
 LL |     require_as_ref_slice(array.to_owned());
    |                          ^^^^^^^^^^^^^^^^ help: use: `array`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:107:26
+  --> $DIR/unnecessary_to_owned.rs:106:26
    |
 LL |     require_as_ref_slice(array_ref.to_owned());
    |                          ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:108:26
+  --> $DIR/unnecessary_to_owned.rs:107:26
    |
 LL |     require_as_ref_slice(slice.to_owned());
    |                          ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:110:31
+  --> $DIR/unnecessary_to_owned.rs:109:31
    |
 LL |     require_impl_as_ref_c_str(c_str.to_owned());
    |                               ^^^^^^^^^^^^^^^^ help: use: `c_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:111:32
+  --> $DIR/unnecessary_to_owned.rs:110:32
    |
 LL |     require_impl_as_ref_os_str(os_str.to_owned());
    |                                ^^^^^^^^^^^^^^^^^ help: use: `os_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:112:30
+  --> $DIR/unnecessary_to_owned.rs:111:30
    |
 LL |     require_impl_as_ref_path(path.to_owned());
    |                              ^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:113:29
+  --> $DIR/unnecessary_to_owned.rs:112:29
    |
 LL |     require_impl_as_ref_str(s.to_owned());
    |                             ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:114:29
+  --> $DIR/unnecessary_to_owned.rs:113:29
    |
 LL |     require_impl_as_ref_str(x.to_owned());
    |                             ^^^^^^^^^^^^ help: use: `&x`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:115:31
+  --> $DIR/unnecessary_to_owned.rs:114:31
    |
 LL |     require_impl_as_ref_slice(array.to_owned());
    |                               ^^^^^^^^^^^^^^^^ help: use: `array`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:116:31
+  --> $DIR/unnecessary_to_owned.rs:115:31
    |
 LL |     require_impl_as_ref_slice(array_ref.to_owned());
    |                               ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:117:31
+  --> $DIR/unnecessary_to_owned.rs:116:31
    |
 LL |     require_impl_as_ref_slice(slice.to_owned());
    |                               ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:119:30
+  --> $DIR/unnecessary_to_owned.rs:118:30
    |
 LL |     require_as_ref_str_slice(s.to_owned(), array.to_owned());
    |                              ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:119:44
+  --> $DIR/unnecessary_to_owned.rs:118:44
    |
 LL |     require_as_ref_str_slice(s.to_owned(), array.to_owned());
    |                                            ^^^^^^^^^^^^^^^^ help: use: `array`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:120:30
+  --> $DIR/unnecessary_to_owned.rs:119:30
    |
 LL |     require_as_ref_str_slice(s.to_owned(), array_ref.to_owned());
    |                              ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:120:44
+  --> $DIR/unnecessary_to_owned.rs:119:44
    |
 LL |     require_as_ref_str_slice(s.to_owned(), array_ref.to_owned());
    |                                            ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:121:30
+  --> $DIR/unnecessary_to_owned.rs:120:30
    |
 LL |     require_as_ref_str_slice(s.to_owned(), slice.to_owned());
    |                              ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:121:44
+  --> $DIR/unnecessary_to_owned.rs:120:44
    |
 LL |     require_as_ref_str_slice(s.to_owned(), slice.to_owned());
    |                                            ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:122:30
+  --> $DIR/unnecessary_to_owned.rs:121:30
    |
 LL |     require_as_ref_slice_str(array.to_owned(), s.to_owned());
    |                              ^^^^^^^^^^^^^^^^ help: use: `array`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:122:48
+  --> $DIR/unnecessary_to_owned.rs:121:48
    |
 LL |     require_as_ref_slice_str(array.to_owned(), s.to_owned());
    |                                                ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:123:30
+  --> $DIR/unnecessary_to_owned.rs:122:30
    |
 LL |     require_as_ref_slice_str(array_ref.to_owned(), s.to_owned());
    |                              ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:123:52
+  --> $DIR/unnecessary_to_owned.rs:122:52
    |
 LL |     require_as_ref_slice_str(array_ref.to_owned(), s.to_owned());
    |                                                    ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:124:30
+  --> $DIR/unnecessary_to_owned.rs:123:30
    |
 LL |     require_as_ref_slice_str(slice.to_owned(), s.to_owned());
    |                              ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:124:48
+  --> $DIR/unnecessary_to_owned.rs:123:48
    |
 LL |     require_as_ref_slice_str(slice.to_owned(), s.to_owned());
    |                                                ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_string`
-  --> $DIR/unnecessary_to_owned.rs:126:20
+  --> $DIR/unnecessary_to_owned.rs:125:20
    |
 LL |     let _ = x.join(&x_ref.to_string());
    |                    ^^^^^^^^^^^^^^^^^^ help: use: `x_ref`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:128:13
+  --> $DIR/unnecessary_to_owned.rs:127:13
    |
 LL |     let _ = slice.to_vec().into_iter();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:129:13
+  --> $DIR/unnecessary_to_owned.rs:128:13
    |
 LL |     let _ = slice.to_owned().into_iter();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:130:13
+  --> $DIR/unnecessary_to_owned.rs:129:13
    |
 LL |     let _ = [std::path::PathBuf::new()][..].to_vec().into_iter();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:131:13
+  --> $DIR/unnecessary_to_owned.rs:130:13
    |
 LL |     let _ = [std::path::PathBuf::new()][..].to_owned().into_iter();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:133:13
+  --> $DIR/unnecessary_to_owned.rs:132:13
    |
 LL |     let _ = IntoIterator::into_iter(slice.to_vec());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:134:13
+  --> $DIR/unnecessary_to_owned.rs:133:13
    |
 LL |     let _ = IntoIterator::into_iter(slice.to_owned());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:135:13
+  --> $DIR/unnecessary_to_owned.rs:134:13
    |
 LL |     let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:136:13
+  --> $DIR/unnecessary_to_owned.rs:135:13
    |
 LL |     let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:198:14
+  --> $DIR/unnecessary_to_owned.rs:197:14
    |
 LL |     for t in file_types.to_vec() {
    |              ^^^^^^^^^^^^^^^^^^^
@@ -492,25 +492,25 @@ LL +         let path = match get_file_path(t) {
    |
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:221:14
+  --> $DIR/unnecessary_to_owned.rs:220:14
    |
 LL |     let _ = &["x"][..].to_vec().into_iter();
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:226:14
+  --> $DIR/unnecessary_to_owned.rs:225:14
    |
 LL |     let _ = &["x"][..].to_vec().into_iter();
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()`
 
 error: unnecessary use of `to_string`
-  --> $DIR/unnecessary_to_owned.rs:273:24
+  --> $DIR/unnecessary_to_owned.rs:272:24
    |
 LL |         Box::new(build(y.to_string()))
    |                        ^^^^^^^^^^^^^ help: use: `y`
 
 error: unnecessary use of `to_string`
-  --> $DIR/unnecessary_to_owned.rs:381:12
+  --> $DIR/unnecessary_to_owned.rs:380:12
    |
 LL |         id("abc".to_string())
    |            ^^^^^^^^^^^^^^^^^ help: use: `"abc"`
diff --git a/tests/ui/doc_unnecessary_unsafe.rs b/tests/ui/unnecessary_unsafety_doc.rs
index d9e9363b0f4..c160e31afd3 100644
--- a/tests/ui/doc_unnecessary_unsafe.rs
+++ b/tests/ui/unnecessary_unsafety_doc.rs
@@ -1,6 +1,7 @@
 // aux-build:doc_unsafe_macros.rs
 
 #![allow(clippy::let_unit_value)]
+#![warn(clippy::unnecessary_safety_doc)]
 
 #[macro_use]
 extern crate doc_unsafe_macros;
diff --git a/tests/ui/doc_unnecessary_unsafe.stderr b/tests/ui/unnecessary_unsafety_doc.stderr
index 83b2efbb346..72898c93fa1 100644
--- a/tests/ui/doc_unnecessary_unsafe.stderr
+++ b/tests/ui/unnecessary_unsafety_doc.stderr
@@ -1,5 +1,5 @@
 error: safe function's docs have unnecessary `# Safety` section
-  --> $DIR/doc_unnecessary_unsafe.rs:18:1
+  --> $DIR/unnecessary_unsafety_doc.rs:19:1
    |
 LL | pub fn apocalypse(universe: &mut ()) {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -7,31 +7,31 @@ LL | pub fn apocalypse(universe: &mut ()) {
    = note: `-D clippy::unnecessary-safety-doc` implied by `-D warnings`
 
 error: safe function's docs have unnecessary `# Safety` section
-  --> $DIR/doc_unnecessary_unsafe.rs:44:5
+  --> $DIR/unnecessary_unsafety_doc.rs:45:5
    |
 LL |     pub fn republished() {
    |     ^^^^^^^^^^^^^^^^^^^^
 
 error: safe function's docs have unnecessary `# Safety` section
-  --> $DIR/doc_unnecessary_unsafe.rs:57:5
+  --> $DIR/unnecessary_unsafety_doc.rs:58:5
    |
 LL |     fn documented(self);
    |     ^^^^^^^^^^^^^^^^^^^^
 
 error: docs for safe trait have unnecessary `# Safety` section
-  --> $DIR/doc_unnecessary_unsafe.rs:67:1
+  --> $DIR/unnecessary_unsafety_doc.rs:68:1
    |
 LL | pub trait DocumentedSafeTrait {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: safe function's docs have unnecessary `# Safety` section
-  --> $DIR/doc_unnecessary_unsafe.rs:95:5
+  --> $DIR/unnecessary_unsafety_doc.rs:96:5
    |
 LL |     pub fn documented() -> Self {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: safe function's docs have unnecessary `# Safety` section
-  --> $DIR/doc_unnecessary_unsafe.rs:122:9
+  --> $DIR/unnecessary_unsafety_doc.rs:123:9
    |
 LL |         pub fn drive() {
    |         ^^^^^^^^^^^^^^
@@ -42,7 +42,7 @@ LL | very_safe!();
    = note: this error originates in the macro `very_safe` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: docs for safe trait have unnecessary `# Safety` section
-  --> $DIR/doc_unnecessary_unsafe.rs:146:1
+  --> $DIR/unnecessary_unsafety_doc.rs:147:1
    |
 LL | pub trait DocumentedSafeTraitWithImplementationHeader {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/unnested_or_patterns.fixed b/tests/ui/unnested_or_patterns.fixed
index 9786c7b1212..0a8e7b34dfa 100644
--- a/tests/ui/unnested_or_patterns.fixed
+++ b/tests/ui/unnested_or_patterns.fixed
@@ -1,6 +1,6 @@
 // run-rustfix
 
-#![feature(box_patterns, custom_inner_attributes)]
+#![feature(box_patterns)]
 #![warn(clippy::unnested_or_patterns)]
 #![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)]
 #![allow(unreachable_patterns, irrefutable_let_patterns, unused)]
@@ -34,14 +34,12 @@ fn main() {
     if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
 }
 
+#[clippy::msrv = "1.52"]
 fn msrv_1_52() {
-    #![clippy::msrv = "1.52"]
-
     if let [1] | [52] = [0] {}
 }
 
+#[clippy::msrv = "1.53"]
 fn msrv_1_53() {
-    #![clippy::msrv = "1.53"]
-
     if let [1 | 53] = [0] {}
 }
diff --git a/tests/ui/unnested_or_patterns.rs b/tests/ui/unnested_or_patterns.rs
index f57322396d4..2c454adfe89 100644
--- a/tests/ui/unnested_or_patterns.rs
+++ b/tests/ui/unnested_or_patterns.rs
@@ -1,6 +1,6 @@
 // run-rustfix
 
-#![feature(box_patterns, custom_inner_attributes)]
+#![feature(box_patterns)]
 #![warn(clippy::unnested_or_patterns)]
 #![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)]
 #![allow(unreachable_patterns, irrefutable_let_patterns, unused)]
@@ -34,14 +34,12 @@ fn main() {
     if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
 }
 
+#[clippy::msrv = "1.52"]
 fn msrv_1_52() {
-    #![clippy::msrv = "1.52"]
-
     if let [1] | [52] = [0] {}
 }
 
+#[clippy::msrv = "1.53"]
 fn msrv_1_53() {
-    #![clippy::msrv = "1.53"]
-
     if let [1] | [53] = [0] {}
 }
diff --git a/tests/ui/unnested_or_patterns.stderr b/tests/ui/unnested_or_patterns.stderr
index fbc12fff0b0..a1f193db555 100644
--- a/tests/ui/unnested_or_patterns.stderr
+++ b/tests/ui/unnested_or_patterns.stderr
@@ -176,7 +176,7 @@ LL |     if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {}
    |            ~~~~~~~~~~~~~~~~~
 
 error: unnested or-patterns
-  --> $DIR/unnested_or_patterns.rs:46:12
+  --> $DIR/unnested_or_patterns.rs:44:12
    |
 LL |     if let [1] | [53] = [0] {}
    |            ^^^^^^^^^^
diff --git a/tests/ui/unused_rounding.fixed b/tests/ui/unused_rounding.fixed
index 38fe6c34cfe..f6f734c05ed 100644
--- a/tests/ui/unused_rounding.fixed
+++ b/tests/ui/unused_rounding.fixed
@@ -11,4 +11,7 @@ fn main() {
     let _ = 3.3_f32.round();
     let _ = 3.3_f64.round();
     let _ = 3.0_f32;
+
+    let _ = 3_3.0_0_f32;
+    let _ = 3_3.0_1_f64.round();
 }
diff --git a/tests/ui/unused_rounding.rs b/tests/ui/unused_rounding.rs
index a5cac64d023..a0267d8144a 100644
--- a/tests/ui/unused_rounding.rs
+++ b/tests/ui/unused_rounding.rs
@@ -11,4 +11,7 @@ fn main() {
     let _ = 3.3_f32.round();
     let _ = 3.3_f64.round();
     let _ = 3.0_f32.round();
+
+    let _ = 3_3.0_0_f32.round();
+    let _ = 3_3.0_1_f64.round();
 }
diff --git a/tests/ui/unused_rounding.stderr b/tests/ui/unused_rounding.stderr
index 1eeb5d1de88..b867996fe57 100644
--- a/tests/ui/unused_rounding.stderr
+++ b/tests/ui/unused_rounding.stderr
@@ -24,5 +24,11 @@ error: used the `round` method with a whole number float
 LL |     let _ = 3.0_f32.round();
    |             ^^^^^^^^^^^^^^^ help: remove the `round` method call: `3.0_f32`
 
-error: aborting due to 4 previous errors
+error: used the `round` method with a whole number float
+  --> $DIR/unused_rounding.rs:15:13
+   |
+LL |     let _ = 3_3.0_0_f32.round();
+   |             ^^^^^^^^^^^^^^^^^^^ help: remove the `round` method call: `3_3.0_0_f32`
+
+error: aborting due to 5 previous errors
 
diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed
index 3b54fe9d5ff..0a6166571eb 100644
--- a/tests/ui/use_self.fixed
+++ b/tests/ui/use_self.fixed
@@ -1,7 +1,6 @@
 // run-rustfix
 // aux-build:proc_macro_derive.rs
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::use_self)]
 #![allow(dead_code, unreachable_code)]
 #![allow(
@@ -619,9 +618,8 @@ mod issue6902 {
     }
 }
 
+#[clippy::msrv = "1.36"]
 fn msrv_1_36() {
-    #![clippy::msrv = "1.36"]
-
     enum E {
         A,
     }
@@ -635,9 +633,8 @@ fn msrv_1_36() {
     }
 }
 
+#[clippy::msrv = "1.37"]
 fn msrv_1_37() {
-    #![clippy::msrv = "1.37"]
-
     enum E {
         A,
     }
diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs
index bf87633cd2d..39c2b431f7f 100644
--- a/tests/ui/use_self.rs
+++ b/tests/ui/use_self.rs
@@ -1,7 +1,6 @@
 // run-rustfix
 // aux-build:proc_macro_derive.rs
 
-#![feature(custom_inner_attributes)]
 #![warn(clippy::use_self)]
 #![allow(dead_code, unreachable_code)]
 #![allow(
@@ -619,9 +618,8 @@ mod issue6902 {
     }
 }
 
+#[clippy::msrv = "1.36"]
 fn msrv_1_36() {
-    #![clippy::msrv = "1.36"]
-
     enum E {
         A,
     }
@@ -635,9 +633,8 @@ fn msrv_1_36() {
     }
 }
 
+#[clippy::msrv = "1.37"]
 fn msrv_1_37() {
-    #![clippy::msrv = "1.37"]
-
     enum E {
         A,
     }
diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr
index 16fb0609242..48364c40c3b 100644
--- a/tests/ui/use_self.stderr
+++ b/tests/ui/use_self.stderr
@@ -1,5 +1,5 @@
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:23:21
+  --> $DIR/use_self.rs:22:21
    |
 LL |         fn new() -> Foo {
    |                     ^^^ help: use the applicable keyword: `Self`
@@ -7,247 +7,247 @@ LL |         fn new() -> Foo {
    = note: `-D clippy::use-self` implied by `-D warnings`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:24:13
+  --> $DIR/use_self.rs:23:13
    |
 LL |             Foo {}
    |             ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:26:22
+  --> $DIR/use_self.rs:25:22
    |
 LL |         fn test() -> Foo {
    |                      ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:27:13
+  --> $DIR/use_self.rs:26:13
    |
 LL |             Foo::new()
    |             ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:32:25
+  --> $DIR/use_self.rs:31:25
    |
 LL |         fn default() -> Foo {
    |                         ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:33:13
+  --> $DIR/use_self.rs:32:13
    |
 LL |             Foo::new()
    |             ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:98:24
+  --> $DIR/use_self.rs:97:24
    |
 LL |         fn bad(foos: &[Foo]) -> impl Iterator<Item = &Foo> {
    |                        ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:98:55
+  --> $DIR/use_self.rs:97:55
    |
 LL |         fn bad(foos: &[Foo]) -> impl Iterator<Item = &Foo> {
    |                                                       ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:113:13
+  --> $DIR/use_self.rs:112:13
    |
 LL |             TS(0)
    |             ^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:148:29
+  --> $DIR/use_self.rs:147:29
    |
 LL |                 fn bar() -> Bar {
    |                             ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:149:21
+  --> $DIR/use_self.rs:148:21
    |
 LL |                     Bar { foo: Foo {} }
    |                     ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:160:21
+  --> $DIR/use_self.rs:159:21
    |
 LL |         fn baz() -> Foo {
    |                     ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:161:13
+  --> $DIR/use_self.rs:160:13
    |
 LL |             Foo {}
    |             ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:178:21
+  --> $DIR/use_self.rs:177:21
    |
 LL |             let _ = Enum::B(42);
    |                     ^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:179:21
+  --> $DIR/use_self.rs:178:21
    |
 LL |             let _ = Enum::C { field: true };
    |                     ^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:180:21
+  --> $DIR/use_self.rs:179:21
    |
 LL |             let _ = Enum::A;
    |                     ^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:222:13
+  --> $DIR/use_self.rs:221:13
    |
 LL |             nested::A::fun_1();
    |             ^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:223:13
+  --> $DIR/use_self.rs:222:13
    |
 LL |             nested::A::A;
    |             ^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:225:13
+  --> $DIR/use_self.rs:224:13
    |
 LL |             nested::A {};
    |             ^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:244:13
+  --> $DIR/use_self.rs:243:13
    |
 LL |             TestStruct::from_something()
    |             ^^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:258:25
+  --> $DIR/use_self.rs:257:25
    |
 LL |         async fn g() -> S {
    |                         ^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:259:13
+  --> $DIR/use_self.rs:258:13
    |
 LL |             S {}
    |             ^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:263:16
+  --> $DIR/use_self.rs:262:16
    |
 LL |             &p[S::A..S::B]
    |                ^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:263:22
+  --> $DIR/use_self.rs:262:22
    |
 LL |             &p[S::A..S::B]
    |                      ^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:286:29
+  --> $DIR/use_self.rs:285:29
    |
 LL |         fn foo(value: T) -> Foo<T> {
    |                             ^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:287:13
+  --> $DIR/use_self.rs:286:13
    |
 LL |             Foo::<T> { value }
    |             ^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:459:13
+  --> $DIR/use_self.rs:458:13
    |
 LL |             A::new::<submod::B>(submod::B {})
    |             ^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:496:13
+  --> $DIR/use_self.rs:495:13
    |
 LL |             S2::new()
    |             ^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:533:17
+  --> $DIR/use_self.rs:532:17
    |
 LL |                 Foo::Bar => unimplemented!(),
    |                 ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:534:17
+  --> $DIR/use_self.rs:533:17
    |
 LL |                 Foo::Baz => unimplemented!(),
    |                 ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:540:20
+  --> $DIR/use_self.rs:539:20
    |
 LL |             if let Foo::Bar = self {
    |                    ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:564:17
+  --> $DIR/use_self.rs:563:17
    |
 LL |                 Something::Num(n) => *n,
    |                 ^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:565:17
+  --> $DIR/use_self.rs:564:17
    |
 LL |                 Something::TupleNums(n, _m) => *n,
    |                 ^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:566:17
+  --> $DIR/use_self.rs:565:17
    |
 LL |                 Something::StructNums { one, two: _ } => *one,
    |                 ^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:572:17
+  --> $DIR/use_self.rs:571:17
    |
 LL |                 crate::issue8845::Something::Num(n) => *n,
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:573:17
+  --> $DIR/use_self.rs:572:17
    |
 LL |                 crate::issue8845::Something::TupleNums(n, _m) => *n,
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:574:17
+  --> $DIR/use_self.rs:573:17
    |
 LL |                 crate::issue8845::Something::StructNums { one, two: _ } => *one,
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:590:17
+  --> $DIR/use_self.rs:589:17
    |
 LL |             let Foo(x) = self;
    |                 ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:595:17
+  --> $DIR/use_self.rs:594:17
    |
 LL |             let crate::issue8845::Foo(x) = self;
    |                 ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:602:17
+  --> $DIR/use_self.rs:601:17
    |
 LL |             let Bar { x, .. } = self;
    |                 ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:607:17
+  --> $DIR/use_self.rs:606:17
    |
 LL |             let crate::issue8845::Bar { x, .. } = self;
    |                 ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:648:17
+  --> $DIR/use_self.rs:645:17
    |
 LL |                 E::A => {},
    |                 ^ help: use the applicable keyword: `Self`
diff --git a/triagebot.toml b/triagebot.toml
index 80c30393832..acb476ee696 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -4,9 +4,26 @@ allow-unauthenticated = [
     "good-first-issue"
 ]
 
-[assign]
-
 # Allows shortcuts like `@rustbot ready`
 #
 # See https://github.com/rust-lang/triagebot/wiki/Shortcuts
 [shortcut]
+
+[autolabel."S-waiting-on-review"]
+new_pr = true
+
+[assign]
+contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md"
+
+[assign.owners]
+"/.github" = ["@flip1995"]
+"*" = [
+    "@flip1995",
+    "@Manishearth",
+    "@llogiq",
+    "@giraffate",
+    "@xFrednet",
+    "@Alexendoo",
+    "@dswij",
+    "@Jarcho",
+]